Skip to content

Commit 568065e

Browse files
committed
fix: bound validator cache lifecycle
1 parent 57b3c3e commit 568065e

16 files changed

Lines changed: 576 additions & 61 deletions

changelogs/unreleased.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Unreleased
22

3-
- No unreleased changes.
3+
## 2026-06-10
4+
5+
- **[Fixed]** Bound Validator/AJV compiled schema lifecycle: repeated DSL definitions and DslBuilder materialization now reuse stable schema cache keys, cache eviction and `clearCache()` release AJV internal schema refs, `_removeAdditional` uses a bounded internal compile cache, locale flatten cache is bounded and revision-aware, and official plugin uninstall paths clean custom formats, types, keywords, global plugin state, and Validator caches.

docs/en/api-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,7 @@ Unified type registry. It is the public entry for custom type extension. If you
950950
- `TypeRegistry.register(name, def)`
951951
- `TypeRegistry.registerDynamic(name, factory)`
952952
- `TypeRegistry.unregister(name)`
953+
- `DslBuilder.unregisterType(name)` - removes one custom type from both the Builder-side custom type table and `TypeRegistry`.
953954
- `TypeRegistry.has(typeName)`
954955
- `TypeRegistry.getInternalKeys()`
955956
- `TypeRegistry.toJsonSchema(schema)`

docs/en/plugin-type-registration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ DslBuilder.registerType('orderCode', {
2323
});
2424
```
2525

26+
Use `DslBuilder.unregisterType('orderCode')` when a plugin or test needs to remove one Builder-side custom type. Use `DslBuilder.clearCustomTypes()` only when you intentionally want to clear all custom types, such as after isolated tests.
27+
2628
Plug-in extensions can be used in conjunction with [plugin-system.md](./plugin-system.md).
2729

2830
---
2931

3032
## Corresponding sample file
3133

3234
**Example entry**: [plugin-type-registration.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/plugin-type-registration.ts)
33-
**Note**: Covers both entrances `TypeRegistry.register()` and `DslBuilder.registerType()`, as well as the real validation and cleanup process after registration.
35+
**Note**: Covers `TypeRegistry.register()`, `DslBuilder.registerType()`, and cleanup with `DslBuilder.unregisterType()` / `DslBuilder.clearCustomTypes()`.

docs/en/union-types.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ Check if the type is registered.
184184

185185
Get all registered custom types.
186186

187+
#### `DslBuilder.unregisterType(name)`
188+
189+
Remove one custom type registered through `DslBuilder.registerType()` / `TypeRegistry`.
190+
187191
#### `DslBuilder.clearCustomTypes()`
188192

189193
Clear all custom types (mainly for testing).

docs/zh/api-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,7 @@ const schema = new JSONSchemaCore()
892892
- `TypeRegistry.register(name, def)`
893893
- `TypeRegistry.registerDynamic(name, factory)`
894894
- `TypeRegistry.unregister(name)`
895+
- `DslBuilder.unregisterType(name)` — 同时从 Builder 侧自定义类型表和 `TypeRegistry` 移除一个自定义类型
895896
- `TypeRegistry.has(typeName)`
896897
- `TypeRegistry.getInternalKeys()`
897898
- `TypeRegistry.toJsonSchema(schema)`

docs/zh/plugin-type-registration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ DslBuilder.registerType('orderCode', {
2323
});
2424
```
2525

26+
当插件或测试只需要移除一个 Builder 侧自定义类型时,使用 `DslBuilder.unregisterType('orderCode')`;只有确实要清空全部自定义类型(例如隔离测试收尾)时,才使用 `DslBuilder.clearCustomTypes()`
27+
2628
插件化扩展可结合 [plugin-system.md](./plugin-system.md) 使用。
2729

2830
---
2931

3032
## 对应示例文件
3133

3234
**示例入口**: [plugin-type-registration.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/plugin-type-registration.ts)
33-
**说明**: 同时覆盖 `TypeRegistry.register()``DslBuilder.registerType()` 两条入口,以及注册后的真实验证与清理流程
35+
**说明**: 覆盖 `TypeRegistry.register()``DslBuilder.registerType()`,以及通过 `DslBuilder.unregisterType()` / `DslBuilder.clearCustomTypes()` 清理注册状态的路径
3436

docs/zh/union-types.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ install(schemaDsl, options, context) {
185185

186186
获取所有已注册的自定义类型。
187187

188+
#### `DslBuilder.unregisterType(name)`
189+
190+
移除一个通过 `DslBuilder.registerType()` / `TypeRegistry` 注册的自定义类型。
191+
188192
#### `DslBuilder.clearCustomTypes()`
189193

190194
清除所有自定义类型(主要用于测试)。

src/core/DslBuilder.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ export class DslBuilder implements IDslBuilder {
116116
}
117117
}
118118

119+
/** Unregister a custom type from both DslBuilder and TypeRegistry. */
120+
static unregisterType(name: string): void {
121+
if (!name || typeof name !== 'string') {
122+
throw new Error('[schema-dsl] Type name must be a non-empty string')
123+
}
124+
TypeRegistry.unregister(name)
125+
DslBuilder._customTypes.delete(name)
126+
}
127+
119128
/** Check whether a type is registered (built-in or custom). */
120129
static hasType(type: string): boolean {
121130
return TypeRegistry.has(type)

src/core/Locale.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export const DEFAULT_LOCALE = 'en-US'
1919
export class Locale {
2020
private static _currentLocale: string = DEFAULT_LOCALE
2121
private static _customMessages: Record<string, LocaleMessage> = {}
22+
private static _revision = 0
23+
24+
static get revision(): number {
25+
return this._revision
26+
}
2227

2328
/** v1 compat: expose custom messages */
2429
static get customMessages(): Record<string, LocaleMessage> {
@@ -59,6 +64,7 @@ export class Locale {
5964

6065
static setMessages(messages: Record<string, LocaleMessage>): void {
6166
this._customMessages = { ...this._customMessages, ...messages }
67+
this._revision++
6268
}
6369

6470
static addLocale(locale: string, messages: Record<string, LocaleMessage>): void {
@@ -67,6 +73,7 @@ export class Locale {
6773
for (const [k, v] of Object.entries(messages)) {
6874
this._customMessages[`${locale}:${k}`] = v
6975
}
76+
this._revision++
7077
}
7178

7279
static getAvailableLocales(): string[] {
@@ -142,6 +149,7 @@ export class Locale {
142149
static reset(): void {
143150
this._currentLocale = DEFAULT_LOCALE
144151
this._customMessages = {}
152+
this._revision++
145153
}
146154

147155
// ─── Private Helpers ──────────────────────────────────────────────────────

0 commit comments

Comments
 (0)