@zhin.js/gateway 0.0.0-stage → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -2
- package/lib/config/ai-config-loader.d.ts +22 -0
- package/lib/config/ai-config-loader.js +96 -0
- package/lib/config/configuration-chat-policy.d.ts +57 -0
- package/lib/config/configuration-chat-policy.js +423 -0
- package/lib/config/configuration-patch.d.ts +16 -0
- package/lib/config/configuration-patch.js +105 -0
- package/lib/config/configuration-path.d.ts +6 -0
- package/lib/config/configuration-path.js +40 -0
- package/lib/config/configuration-projection.d.ts +6 -0
- package/lib/config/configuration-projection.js +28 -0
- package/lib/config/configuration-redaction.d.ts +24 -0
- package/lib/config/configuration-redaction.js +128 -0
- package/lib/config/configuration.d.ts +81 -0
- package/lib/config/configuration.js +686 -0
- package/lib/config/console-schema.d.ts +11 -0
- package/lib/config/console-schema.js +222 -0
- package/lib/config/environment-files.d.ts +2 -0
- package/lib/config/environment-files.js +21 -0
- package/lib/config/plugin-package-map.d.ts +2 -0
- package/lib/config/plugin-package-map.js +14 -0
- package/lib/config/plugin-schema-catalog.d.ts +22 -0
- package/lib/config/plugin-schema-catalog.js +262 -0
- package/lib/config.d.ts +12 -0
- package/lib/config.js +12 -0
- package/lib/database/config.d.ts +22 -0
- package/lib/database/config.js +159 -0
- package/lib/database/index.d.ts +2 -0
- package/lib/database/index.js +2 -0
- package/lib/database/project-database.d.ts +31 -0
- package/lib/database/project-database.js +130 -0
- package/lib/database.d.ts +2 -0
- package/lib/database.js +2 -0
- package/lib/http/console-rpc.d.ts +19 -0
- package/lib/http/console-rpc.js +51 -0
- package/lib/http/gateway-console.d.ts +63 -0
- package/lib/http/gateway-console.js +309 -0
- package/lib/http/gateway-events.d.ts +35 -0
- package/lib/http/gateway-events.js +172 -0
- package/lib/http/gateway-http-server.d.ts +95 -0
- package/lib/http/gateway-http-server.js +518 -0
- package/lib/http/local-principal.d.ts +2 -0
- package/lib/http/local-principal.js +16 -0
- package/lib/http/run-gateway.d.ts +43 -0
- package/lib/http/run-gateway.js +268 -0
- package/lib/http/start-gateway.d.ts +114 -0
- package/lib/http/start-gateway.js +220 -0
- package/lib/http-response.d.ts +6 -0
- package/lib/http-response.js +25 -0
- package/lib/http.d.ts +6 -0
- package/lib/http.js +6 -0
- package/lib/iam/crypto.d.ts +12 -0
- package/lib/iam/crypto.js +58 -0
- package/lib/iam/index.d.ts +8 -0
- package/lib/iam/index.js +8 -0
- package/lib/iam/install.d.ts +36 -0
- package/lib/iam/install.js +74 -0
- package/lib/iam/local-admin.d.ts +27 -0
- package/lib/iam/local-admin.js +102 -0
- package/lib/iam/rate-limit.d.ts +15 -0
- package/lib/iam/rate-limit.js +52 -0
- package/lib/iam/routes.d.ts +23 -0
- package/lib/iam/routes.js +157 -0
- package/lib/iam/rpc.d.ts +14 -0
- package/lib/iam/rpc.js +100 -0
- package/lib/iam/service.d.ts +203 -0
- package/lib/iam/service.js +784 -0
- package/lib/iam/setup-service.d.ts +83 -0
- package/lib/iam/setup-service.js +206 -0
- package/lib/iam/tables.d.ts +20 -0
- package/lib/iam/tables.js +82 -0
- package/lib/iam.d.ts +2 -0
- package/lib/iam.js +2 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +22 -0
- package/lib/local/client.d.ts +71 -0
- package/lib/local/client.js +296 -0
- package/lib/local/handler.d.ts +30 -0
- package/lib/local/handler.js +86 -0
- package/lib/local/local-socket.d.ts +55 -0
- package/lib/local/local-socket.js +232 -0
- package/lib/local/paths.d.ts +34 -0
- package/lib/local/paths.js +58 -0
- package/lib/local/protocol.d.ts +140 -0
- package/lib/local/protocol.js +90 -0
- package/lib/local/state-file.d.ts +8 -0
- package/lib/local/state-file.js +45 -0
- package/lib/local.d.ts +10 -0
- package/lib/local.js +10 -0
- package/lib/onboarding/allowlist.d.ts +54 -0
- package/lib/onboarding/allowlist.js +106 -0
- package/lib/onboarding/errors.d.ts +7 -0
- package/lib/onboarding/errors.js +17 -0
- package/lib/onboarding/index.d.ts +6 -0
- package/lib/onboarding/index.js +6 -0
- package/lib/onboarding/install.d.ts +99 -0
- package/lib/onboarding/install.js +132 -0
- package/lib/onboarding/package-manager.d.ts +47 -0
- package/lib/onboarding/package-manager.js +179 -0
- package/lib/onboarding/service.d.ts +53 -0
- package/lib/onboarding/service.js +144 -0
- package/lib/onboarding/state-store.d.ts +26 -0
- package/lib/onboarding/state-store.js +97 -0
- package/lib/onboarding.d.ts +2 -0
- package/lib/onboarding.js +2 -0
- package/lib/packages/authorization.d.ts +23 -0
- package/lib/packages/authorization.js +109 -0
- package/lib/packages/errors.d.ts +11 -0
- package/lib/packages/errors.js +26 -0
- package/lib/packages/index.d.ts +5 -0
- package/lib/packages/index.js +5 -0
- package/lib/packages/rpc.d.ts +30 -0
- package/lib/packages/rpc.js +155 -0
- package/lib/packages/service.d.ts +121 -0
- package/lib/packages/service.js +441 -0
- package/lib/packages/snapshot.d.ts +14 -0
- package/lib/packages/snapshot.js +73 -0
- package/lib/packages.d.ts +2 -0
- package/lib/packages.js +2 -0
- package/lib/plugin-management/activity.d.ts +6 -0
- package/lib/plugin-management/activity.js +14 -0
- package/lib/plugin-management/config-explanation.d.ts +6 -0
- package/lib/plugin-management/config-explanation.js +113 -0
- package/lib/plugin-management/use-cases.d.ts +15 -0
- package/lib/plugin-management/use-cases.js +120 -0
- package/lib/plugin-management/workflow.d.ts +92 -0
- package/lib/plugin-management/workflow.js +772 -0
- package/lib/plugin-management.d.ts +5 -0
- package/lib/plugin-management.js +5 -0
- package/lib/project/config-file.d.ts +6 -0
- package/lib/project/config-file.js +58 -0
- package/lib/project/environment.d.ts +13 -0
- package/lib/project/environment.js +52 -0
- package/lib/project/instance-declarations.d.ts +55 -0
- package/lib/project/instance-declarations.js +542 -0
- package/lib/project/legacy-config.d.ts +25 -0
- package/lib/project/legacy-config.js +70 -0
- package/lib/project/node-requirements.d.ts +7 -0
- package/lib/project/node-requirements.js +20 -0
- package/lib/project/tsx-module-loader.d.ts +7 -0
- package/lib/project/tsx-module-loader.js +29 -0
- package/lib/project.d.ts +7 -0
- package/lib/project.js +7 -0
- package/lib/server.d.ts +11 -0
- package/lib/server.js +11 -0
- package/lib/supervisor/instance-logs.d.ts +39 -0
- package/lib/supervisor/instance-logs.js +152 -0
- package/lib/supervisor/instance-supervisor.d.ts +140 -0
- package/lib/supervisor/instance-supervisor.js +408 -0
- package/lib/supervisor/run-instance.d.ts +35 -0
- package/lib/supervisor/run-instance.js +161 -0
- package/lib/supervisor/terminal-state.d.ts +30 -0
- package/lib/supervisor/terminal-state.js +57 -0
- package/lib/supervisor.d.ts +4 -0
- package/lib/supervisor.js +4 -0
- package/lib/terminal/backends.d.ts +38 -0
- package/lib/terminal/backends.js +193 -0
- package/lib/terminal/config.d.ts +63 -0
- package/lib/terminal/config.js +237 -0
- package/lib/terminal/host.d.ts +26 -0
- package/lib/terminal/host.js +76 -0
- package/lib/terminal/index.d.ts +60 -0
- package/lib/terminal/index.js +135 -0
- package/lib/terminal/modules.d.ts +87 -0
- package/lib/terminal/modules.js +44 -0
- package/lib/terminal/service.d.ts +101 -0
- package/lib/terminal/service.js +455 -0
- package/lib/terminal/tickets.d.ts +46 -0
- package/lib/terminal/tickets.js +76 -0
- package/lib/terminal.d.ts +3 -0
- package/lib/terminal.js +3 -0
- package/lib/usage/aggregate.d.ts +87 -0
- package/lib/usage/aggregate.js +212 -0
- package/lib/usage.d.ts +2 -0
- package/lib/usage.js +2 -0
- package/package.json +116 -3
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type Schema = Record<string, unknown>;
|
|
2
|
+
/** The one JSON Schema union that existing string Select fields can represent exactly. */
|
|
3
|
+
export declare function readStringLiteralOptions(input: Record<string, unknown>): readonly {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string;
|
|
6
|
+
'x-i18n'?: Record<string, unknown>;
|
|
7
|
+
}[] | null;
|
|
8
|
+
/** Display projection only. Runtime retains the original JSON Schema validator. */
|
|
9
|
+
export declare function jsonSchemaToConsoleSchema(input: unknown, key?: string): Schema | null;
|
|
10
|
+
export declare function isConsoleSchemaJson(schema: Schema): boolean;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
const record = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
2
|
+
const own = (value, key) => Object.hasOwn(value, key);
|
|
3
|
+
const jsonTypes = new Set(['object', 'array', 'string', 'number', 'integer', 'boolean', 'null']);
|
|
4
|
+
const unsupportedKeywords = ['not', 'if', 'then', 'else', 'dependentSchemas', '$dynamicRef', 'patternProperties', 'contains'];
|
|
5
|
+
/** The one JSON Schema union that existing string Select fields can represent exactly. */
|
|
6
|
+
export function readStringLiteralOptions(input) {
|
|
7
|
+
if (!Array.isArray(input.anyOf) || input.anyOf.length === 0 || input.anyOf.length > 64)
|
|
8
|
+
return null;
|
|
9
|
+
const annotations = ['title', 'description', 'x-descriptionZh', 'x-i18n', '$comment'];
|
|
10
|
+
const parentKeys = ['anyOf', 'type', 'default', 'writeOnly', 'readOnly', 'deprecated', 'x-zhin-config', ...annotations];
|
|
11
|
+
if (Object.keys(input).some(key => !parentKeys.includes(key))
|
|
12
|
+
|| (input.type !== undefined && input.type !== 'string'))
|
|
13
|
+
return null;
|
|
14
|
+
const values = new Set();
|
|
15
|
+
const options = [];
|
|
16
|
+
for (const branch of input.anyOf) {
|
|
17
|
+
if (!branch || typeof branch !== 'object' || Array.isArray(branch))
|
|
18
|
+
return null;
|
|
19
|
+
const item = branch;
|
|
20
|
+
if (Object.keys(item).some(key => !['const', 'type', 'default', ...annotations].includes(key))
|
|
21
|
+
|| !Object.prototype.hasOwnProperty.call(item, 'const')
|
|
22
|
+
|| typeof item.const !== 'string' || item.const.length === 0 || item.const.length > 256
|
|
23
|
+
|| values.has(item.const) || (item.type !== undefined && item.type !== 'string')
|
|
24
|
+
|| (Object.prototype.hasOwnProperty.call(item, 'default') && item.default !== item.const))
|
|
25
|
+
return null;
|
|
26
|
+
values.add(item.const);
|
|
27
|
+
const label = [item['x-descriptionZh'], item.title, item.description].find(value => typeof value === 'string');
|
|
28
|
+
const translations = item['x-i18n'];
|
|
29
|
+
options.push({ label: typeof label === 'string' ? label : item.const, value: item.const,
|
|
30
|
+
...(translations && typeof translations === 'object' && !Array.isArray(translations)
|
|
31
|
+
? { 'x-i18n': structuredClone(translations) } : {}) });
|
|
32
|
+
}
|
|
33
|
+
if (Object.prototype.hasOwnProperty.call(input, 'default')
|
|
34
|
+
&& (typeof input.default !== 'string' || !values.has(input.default)))
|
|
35
|
+
return null;
|
|
36
|
+
const metadata = input['x-zhin-config'];
|
|
37
|
+
if (metadata && typeof metadata === 'object' && 'options' in metadata) {
|
|
38
|
+
const custom = metadata.options;
|
|
39
|
+
if (!Array.isArray(custom) || custom.length !== options.length
|
|
40
|
+
|| new Set(custom.map(item => item?.value)).size !== options.length
|
|
41
|
+
|| custom.some(item => !item || typeof item !== 'object'
|
|
42
|
+
|| typeof item.label !== 'string' || !values.has(item.value)))
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return options;
|
|
46
|
+
}
|
|
47
|
+
/** Display projection only. Runtime retains the original JSON Schema validator. */
|
|
48
|
+
export function jsonSchemaToConsoleSchema(input, key) {
|
|
49
|
+
if (input === undefined || input === null)
|
|
50
|
+
return null;
|
|
51
|
+
return convert(input, key, { root: input, references: new Set(), depth: 0, budget: { remaining: 8192 } });
|
|
52
|
+
}
|
|
53
|
+
function convert(input, key, context) {
|
|
54
|
+
const fallback = (reason) => ({ type: 'any', ...(key === undefined ? {} : { key }), ...(reason ? { reason } : {}) });
|
|
55
|
+
if (--context.budget.remaining < 0 || context.depth > 64)
|
|
56
|
+
return fallback('projection-limit');
|
|
57
|
+
if (typeof input === 'boolean')
|
|
58
|
+
return input ? fallback() : fallback('never-schema');
|
|
59
|
+
if (!record(input))
|
|
60
|
+
return fallback('malformed-schema');
|
|
61
|
+
const schema = input;
|
|
62
|
+
const properties = new Set([...context.properties ?? [], ...Object.keys(record(schema.properties) ? schema.properties : {})]);
|
|
63
|
+
const child = (value, name, references = context.references) => convert(value, name, { ...context, depth: context.depth + 1, references, properties: undefined });
|
|
64
|
+
const sameValue = (value, references = context.references) => convert(value, undefined, { ...context, depth: context.depth + 1, references, properties });
|
|
65
|
+
const finish = (value) => annotate(value, schema, key);
|
|
66
|
+
if (typeof schema.$ref === 'string') {
|
|
67
|
+
const reference = schema.$ref;
|
|
68
|
+
if (reference !== '#' && !reference.startsWith('#/'))
|
|
69
|
+
return finish(fallback('external-reference'));
|
|
70
|
+
if (context.references.has(reference))
|
|
71
|
+
return finish(fallback('cyclic-reference'));
|
|
72
|
+
let target = context.root;
|
|
73
|
+
try {
|
|
74
|
+
for (const raw of reference === '#' ? [] : reference.slice(2).split('/')) {
|
|
75
|
+
const part = decodeURIComponent(raw).replaceAll('~1', '/').replaceAll('~0', '~');
|
|
76
|
+
if (!record(target) || !own(target, part))
|
|
77
|
+
return finish(fallback('unresolved-reference'));
|
|
78
|
+
target = target[part];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return finish(fallback('unresolved-reference'));
|
|
83
|
+
}
|
|
84
|
+
const references = new Set(context.references);
|
|
85
|
+
references.add(reference);
|
|
86
|
+
const resolved = sameValue(target, references);
|
|
87
|
+
const { $ref: _reference, ...siblings } = schema;
|
|
88
|
+
const validations = Object.keys(siblings).filter(name => !annotationKeys.has(name) && !['$defs', 'definitions', '$id', '$schema', '$comment'].includes(name));
|
|
89
|
+
return finish(validations.length ? { type: 'intersect', list: [resolved, sameValue(siblings)] } : resolved);
|
|
90
|
+
}
|
|
91
|
+
// Unsupported semantics stay editable as JSON at this field; siblings never disappear.
|
|
92
|
+
if (unsupportedKeywords.some(name => own(schema, name))) {
|
|
93
|
+
if (properties.size && validationOnly(schema, properties)) {
|
|
94
|
+
const constraint = { type: 'constraint', reason: 'host-validation' };
|
|
95
|
+
const { if: _if, then: _then, else: _else, not: _not, ...base } = schema;
|
|
96
|
+
return finish(schema.properties ? { type: 'intersect', list: [constraint, sameValue(base)] } : constraint);
|
|
97
|
+
}
|
|
98
|
+
return finish(fallback('unsupported-construct'));
|
|
99
|
+
}
|
|
100
|
+
const literalOptions = readStringLiteralOptions(schema);
|
|
101
|
+
if (literalOptions)
|
|
102
|
+
return finish({ type: 'string', options: literalOptions });
|
|
103
|
+
if (isConsoleSchemaJson(schema)) {
|
|
104
|
+
const result = { ...schema };
|
|
105
|
+
if (record(schema.object))
|
|
106
|
+
result.object = Object.fromEntries(Object.entries(schema.object).map(([name, value]) => [name, child(value)]));
|
|
107
|
+
if (Array.isArray(schema.list))
|
|
108
|
+
result.list = schema.list.map(value => child(value));
|
|
109
|
+
if (schema.inner !== undefined)
|
|
110
|
+
result.inner = schema.inner === false ? false : child(schema.inner);
|
|
111
|
+
if (schema.additional !== undefined)
|
|
112
|
+
result.additional = schema.additional === false ? false : child(schema.additional);
|
|
113
|
+
if (schema.type === 'const' && !own(schema, 'value') && own(schema, 'default'))
|
|
114
|
+
result.value = schema.default;
|
|
115
|
+
return finish(result);
|
|
116
|
+
}
|
|
117
|
+
for (const [keyword, type] of [['allOf', 'intersect'], ['anyOf', 'union'], ['oneOf', 'union']]) {
|
|
118
|
+
if (!Array.isArray(schema[keyword]))
|
|
119
|
+
continue;
|
|
120
|
+
if (schema[keyword].length === 0)
|
|
121
|
+
return finish(fallback('malformed-schema'));
|
|
122
|
+
const { [keyword]: _branches, ...siblings } = schema;
|
|
123
|
+
const list = schema[keyword].map(value => sameValue(value));
|
|
124
|
+
const structuralSibling = ['properties', 'items', 'prefixItems', 'additionalProperties'].some(name => own(siblings, name));
|
|
125
|
+
const composed = { type, list, ...(type === 'union' ? { mode: keyword } : {}) };
|
|
126
|
+
return finish(structuralSibling ? { type: 'intersect', list: [composed, sameValue(siblings)] } : composed);
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(schema.type)) {
|
|
129
|
+
const { type: _types, ...constraints } = schema;
|
|
130
|
+
return finish({ type: 'union', list: schema.type.map(type => child({ ...constraints, type })) });
|
|
131
|
+
}
|
|
132
|
+
if (own(schema, 'const'))
|
|
133
|
+
return finish({ type: 'const', value: schema.const });
|
|
134
|
+
const type = typeof schema.type === 'string' ? schema.type : schema.properties ? 'object' : schema.items || schema.prefixItems ? 'array' : undefined;
|
|
135
|
+
if (type === 'object') {
|
|
136
|
+
const properties = record(schema.properties) ? schema.properties : {};
|
|
137
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
138
|
+
const object = Object.fromEntries(Object.entries(properties).map(([name, value]) => [name, { ...child(value, name), ...(required.has(name) ? { required: true } : {}) }]));
|
|
139
|
+
const additional = schema.additionalProperties === false ? false : child(schema.additionalProperties ?? true);
|
|
140
|
+
return finish(Object.keys(object).length === 0 && record(schema.additionalProperties)
|
|
141
|
+
? { type: 'dict', inner: additional } : { type: 'object', object, additional });
|
|
142
|
+
}
|
|
143
|
+
if (type === 'array') {
|
|
144
|
+
const prefix = Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.items) ? schema.items : undefined;
|
|
145
|
+
return finish(prefix ? { type: 'tuple', list: prefix.map(value => child(value)), inner: schema.items === false || schema.additionalItems === false ? false : child(Array.isArray(schema.items) ? schema.additionalItems ?? true : schema.items ?? true) }
|
|
146
|
+
: { type: 'list', inner: child(schema.items ?? true) });
|
|
147
|
+
}
|
|
148
|
+
const inferred = type ?? (Array.isArray(schema.enum) && schema.enum.length ? (schema.enum[0] === null ? 'null' : typeof schema.enum[0]) : 'any');
|
|
149
|
+
return finish({ type: inferred === 'integer' ? 'number' : jsonTypes.has(inferred) ? inferred : 'any', ...(inferred === 'integer' ? { integer: true } : {}) });
|
|
150
|
+
}
|
|
151
|
+
const annotationKeys = new Set(['title', 'description', 'x-descriptionZh', 'x-i18n', 'default', 'readOnly', 'writeOnly', 'examples', 'deprecated', 'x-zhin-config', 'key', 'hidden', 'component', 'options', 'effect', 'secret']);
|
|
152
|
+
function annotate(result, schema, key) {
|
|
153
|
+
const output = { ...result };
|
|
154
|
+
if (record(schema['x-i18n']))
|
|
155
|
+
output['x-i18n'] = schema['x-i18n'];
|
|
156
|
+
for (const name of ['key', 'title', 'default', 'min', 'max', 'step', 'integer', 'pattern', 'minLength', 'maxLength', 'minItems', 'maxItems', 'minimum', 'maximum', 'multipleOf', 'uniqueItems', 'minProperties', 'maxProperties', 'exclusiveMinimum', 'exclusiveMaximum', 'readOnly', 'deprecated', 'format', 'unevaluatedProperties'])
|
|
157
|
+
if (own(schema, name))
|
|
158
|
+
output[name] = schema[name];
|
|
159
|
+
if (key !== undefined && output.key === undefined)
|
|
160
|
+
output.key = key;
|
|
161
|
+
const description = typeof schema['x-descriptionZh'] === 'string' ? schema['x-descriptionZh'] : schema.description;
|
|
162
|
+
if (typeof description === 'string')
|
|
163
|
+
output.description = description;
|
|
164
|
+
// Existing bilingual Host schemas already keep the English original beside x-descriptionZh.
|
|
165
|
+
if (typeof schema['x-descriptionZh'] === 'string' && typeof schema.description === 'string') {
|
|
166
|
+
const catalog = record(output['x-i18n']) ? output['x-i18n'] : {};
|
|
167
|
+
const english = record(catalog.en) ? catalog.en : {};
|
|
168
|
+
output['x-i18n'] = { ...catalog, en: { description: schema.description, ...english } };
|
|
169
|
+
}
|
|
170
|
+
if (typeof schema.required === 'boolean')
|
|
171
|
+
output.required = schema.required;
|
|
172
|
+
if (Array.isArray(schema.enum))
|
|
173
|
+
output.options = schema.enum.map(value => ({ label: String(value), value }));
|
|
174
|
+
for (const name of ['hidden', 'component', 'options', 'effect'])
|
|
175
|
+
if (schema[name] !== undefined)
|
|
176
|
+
output[name] = schema[name];
|
|
177
|
+
const metadata = record(schema['x-zhin-config']) ? schema['x-zhin-config'] : {};
|
|
178
|
+
for (const name of ['hidden', 'component', 'options', 'effect'])
|
|
179
|
+
if (metadata[name] !== undefined)
|
|
180
|
+
output[name] = metadata[name];
|
|
181
|
+
if (output.secret !== true && typeof schema.secret === 'boolean')
|
|
182
|
+
output.secret = schema.secret;
|
|
183
|
+
if (output.secret === true || schema.writeOnly === true || schema.secret === true || metadata.secret === true) {
|
|
184
|
+
output.secret = true;
|
|
185
|
+
if (output.component !== 'textarea')
|
|
186
|
+
output.component = 'password';
|
|
187
|
+
}
|
|
188
|
+
const bounds = schema.type === 'string' ? ['minLength', 'maxLength'] : schema.type === 'array' ? ['minItems', 'maxItems'] : ['minimum', 'maximum'];
|
|
189
|
+
if (typeof schema[bounds[0]] === 'number')
|
|
190
|
+
output.min = schema[bounds[0]];
|
|
191
|
+
if (typeof schema[bounds[1]] === 'number')
|
|
192
|
+
output.max = schema[bounds[1]];
|
|
193
|
+
if (typeof schema.multipleOf === 'number')
|
|
194
|
+
output.step = schema.multipleOf;
|
|
195
|
+
return output;
|
|
196
|
+
}
|
|
197
|
+
export function isConsoleSchemaJson(schema) {
|
|
198
|
+
if (schema.object !== undefined || schema.list !== undefined || schema.inner !== undefined || schema.additional !== undefined)
|
|
199
|
+
return true;
|
|
200
|
+
return typeof schema.type === 'string' && (['dict', 'union', 'tuple', 'intersect', 'const', 'any', 'date', 'regexp', 'list'].includes(schema.type)
|
|
201
|
+
|| ['string', 'number', 'boolean', 'null'].includes(schema.type) && (schema.key !== undefined || typeof schema.required === 'boolean' || ['hidden', 'component', 'options', 'effect', 'secret'].some(name => own(schema, name))) && !['const', 'anyOf', 'oneOf', 'allOf', '$ref'].some(name => own(schema, name)));
|
|
202
|
+
}
|
|
203
|
+
/** Validation-only branches do not own a second editable copy of the same value. */
|
|
204
|
+
function validationOnly(schema, properties) {
|
|
205
|
+
if (['$ref', '$dynamicRef', 'default', 'items', 'prefixItems', 'additionalProperties', 'patternProperties', 'contains', 'dependentSchemas'].some(name => own(schema, name)))
|
|
206
|
+
return false;
|
|
207
|
+
if (Array.isArray(schema.required) && schema.required.some(name => !properties.has(String(name))))
|
|
208
|
+
return false;
|
|
209
|
+
if (record(schema.properties)) {
|
|
210
|
+
for (const [name, definition] of Object.entries(schema.properties)) {
|
|
211
|
+
if (!properties.has(name) || !record(definition) || !validationOnly(definition, new Set()))
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
for (const name of ['if', 'then', 'else', 'not'])
|
|
216
|
+
if (own(schema, name) && (!record(schema[name]) || !validationOnly(schema[name], properties)))
|
|
217
|
+
return false;
|
|
218
|
+
for (const name of ['allOf', 'anyOf', 'oneOf'])
|
|
219
|
+
if (Array.isArray(schema[name]) && !schema[name].every(value => record(value) && validationOnly(value, properties)))
|
|
220
|
+
return false;
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const ENV_FILES = new Set(['.env', '.env.development', '.env.production']);
|
|
4
|
+
export async function readEnvFile(projectRoot, filename) {
|
|
5
|
+
if (!ENV_FILES.has(filename))
|
|
6
|
+
throw new Error(`Invalid env file: ${filename}`);
|
|
7
|
+
const file = join(projectRoot, filename);
|
|
8
|
+
try {
|
|
9
|
+
return await readFile(file, 'utf8');
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code === 'ENOENT')
|
|
13
|
+
return '';
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function writeEnvFile(projectRoot, filename, content) {
|
|
18
|
+
if (!ENV_FILES.has(filename))
|
|
19
|
+
throw new Error(`Invalid env file: ${filename}`);
|
|
20
|
+
await writeFile(join(projectRoot, filename), content, 'utf8');
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { createConfigDocument, readApplicationPluginDeclaration } from '@zhin.js/runtime';
|
|
3
|
+
import { findConfigFile } from '../project/config-file.js';
|
|
4
|
+
/** Reads only the application's instance declaration authority. */
|
|
5
|
+
export async function readPluginPackageMap(projectRoot, currentDocument) {
|
|
6
|
+
let document = currentDocument;
|
|
7
|
+
if (!document) {
|
|
8
|
+
const file = findConfigFile(projectRoot);
|
|
9
|
+
if (!file)
|
|
10
|
+
throw new Error('Missing configVersion: 2 configuration; run zhin setup');
|
|
11
|
+
document = (await createConfigDocument(join(projectRoot, file)).read()).document;
|
|
12
|
+
}
|
|
13
|
+
return new Map(readApplicationPluginDeclaration(document).instances.map(item => [item.instanceKey, item.package]));
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RuntimeConfigDocument } from '@zhin.js/plugin-runtime';
|
|
2
|
+
/** Resolves schemas using the same package roots and declarations as Runtime. */
|
|
3
|
+
export declare class PluginSchemaCatalog {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(projectRoot: string, declarations?: () => ReadonlyMap<string, Readonly<Record<string, unknown>>> | Promise<ReadonlyMap<string, Readonly<Record<string, unknown>>>>);
|
|
6
|
+
read(pluginName?: string, document?: RuntimeConfigDocument): Promise<unknown>;
|
|
7
|
+
readJson(pluginName: string, document?: RuntimeConfigDocument): Promise<Readonly<Record<string, unknown>> | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Raw JSON schemas for several keys sharing one resolver and one declaration read.
|
|
10
|
+
* A key whose schema cannot be read maps to its `Error`, so callers can fail closed per key.
|
|
11
|
+
*/
|
|
12
|
+
readJsonMany(keys: Iterable<string>, document?: RuntimeConfigDocument): Promise<ReadonlyMap<string, Readonly<Record<string, unknown>> | null | Error>>;
|
|
13
|
+
/**
|
|
14
|
+
* Form schema of an installed plugin package that no instance declares yet (ADR 0053 §2 step 4):
|
|
15
|
+
* the package must be a production dependency (`dependencies` / `optionalDependencies`) of the
|
|
16
|
+
* project and a Plugin package. Returns the declared-instance form shape plus `declared: false`,
|
|
17
|
+
* `null` when such a package ships no `schema.json`, and `undefined` when `packageName` is not
|
|
18
|
+
* such a package (declared, not a dependency, not installed, not a Plugin, or not a package name).
|
|
19
|
+
*/
|
|
20
|
+
readUndeclared(packageName: string, currentDocument?: RuntimeConfigDocument): Promise<Readonly<Record<string, unknown>> | null | undefined>;
|
|
21
|
+
readAll(keys: Iterable<string>, document?: RuntimeConfigDocument): Promise<Record<string, unknown>>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { createConfigDocument, HOST_CONFIG_KEYS, readApplicationPluginDeclaration, HOST_CONFIG_SCHEMA, ManifestValidationError, NodePackageResolver, PackageResolutionError, } from '@zhin.js/runtime';
|
|
4
|
+
import { findConfigFile } from '../project/config-file.js';
|
|
5
|
+
import { isConsoleSchemaJson, jsonSchemaToConsoleSchema } from './console-schema.js';
|
|
6
|
+
const HOST_CONFIG_KEY_SET = new Set(HOST_CONFIG_KEYS);
|
|
7
|
+
/** Resolves schemas using the same package roots and declarations as Runtime. */
|
|
8
|
+
export class PluginSchemaCatalog {
|
|
9
|
+
#projectRoot;
|
|
10
|
+
#declarations;
|
|
11
|
+
constructor(projectRoot, declarations) {
|
|
12
|
+
this.#projectRoot = projectRoot;
|
|
13
|
+
this.#declarations = declarations;
|
|
14
|
+
}
|
|
15
|
+
async read(pluginName, document) {
|
|
16
|
+
if (!pluginName)
|
|
17
|
+
return null;
|
|
18
|
+
return this.#read(pluginName, () => NodePackageResolver.create(this.#projectRoot), await this.#declarations?.(), false, document);
|
|
19
|
+
}
|
|
20
|
+
async readJson(pluginName, document) {
|
|
21
|
+
return (await this.#read(pluginName, () => NodePackageResolver.create(this.#projectRoot), await this.#declarations?.(), true, document));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Raw JSON schemas for several keys sharing one resolver and one declaration read.
|
|
25
|
+
* A key whose schema cannot be read maps to its `Error`, so callers can fail closed per key.
|
|
26
|
+
*/
|
|
27
|
+
async readJsonMany(keys, document) {
|
|
28
|
+
const list = [...keys];
|
|
29
|
+
let declarations;
|
|
30
|
+
try {
|
|
31
|
+
declarations = await this.#declarations?.();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
35
|
+
return new Map(list.map(key => [key, failure]));
|
|
36
|
+
}
|
|
37
|
+
let resolver;
|
|
38
|
+
return new Map(await Promise.all(list.map(async (key) => {
|
|
39
|
+
try {
|
|
40
|
+
const schema = await this.#read(key, () => resolver ??= NodePackageResolver.create(this.#projectRoot), declarations, true, document);
|
|
41
|
+
return [key, schema];
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return [key, error instanceof Error ? error : new Error(String(error))];
|
|
45
|
+
}
|
|
46
|
+
})));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Form schema of an installed plugin package that no instance declares yet (ADR 0053 §2 step 4):
|
|
50
|
+
* the package must be a production dependency (`dependencies` / `optionalDependencies`) of the
|
|
51
|
+
* project and a Plugin package. Returns the declared-instance form shape plus `declared: false`,
|
|
52
|
+
* `null` when such a package ships no `schema.json`, and `undefined` when `packageName` is not
|
|
53
|
+
* such a package (declared, not a dependency, not installed, not a Plugin, or not a package name).
|
|
54
|
+
*/
|
|
55
|
+
async readUndeclared(packageName, currentDocument) {
|
|
56
|
+
if (HOST_CONFIG_KEY_SET.has(packageName) || packageName === 'root'
|
|
57
|
+
|| !/^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/u.test(packageName))
|
|
58
|
+
return undefined;
|
|
59
|
+
let target;
|
|
60
|
+
try {
|
|
61
|
+
const packages = await NodePackageResolver.create(this.#projectRoot);
|
|
62
|
+
const root = await packages.root(this.#projectRoot);
|
|
63
|
+
const file = findConfigFile(this.#projectRoot);
|
|
64
|
+
const document = currentDocument ?? (file ? (await createConfigDocument(join(this.#projectRoot, file)).read()).document : {});
|
|
65
|
+
const declaration = readApplicationPluginDeclaration(document);
|
|
66
|
+
if (declaration.version !== 2)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (declaration.instances.some(item => item.instanceKey === packageName || item.package === packageName))
|
|
69
|
+
return undefined;
|
|
70
|
+
const manifest = root.packageJson;
|
|
71
|
+
if (!Object.hasOwn(manifest.dependencies ?? {}, packageName)
|
|
72
|
+
&& !Object.hasOwn(manifest.optionalDependencies ?? {}, packageName))
|
|
73
|
+
return undefined;
|
|
74
|
+
try {
|
|
75
|
+
target = await resolveReference(packages, root, { package: packageName, instanceKey: packageName });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// A dependency without a Zhin manifest is a plain library, not an undeclared plugin.
|
|
79
|
+
if (error instanceof ManifestValidationError)
|
|
80
|
+
return undefined;
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
// A declaration through another reference (local path, alias) that resolves to the same
|
|
84
|
+
// package root already declares this package.
|
|
85
|
+
for (const item of declaration.instances) {
|
|
86
|
+
const declared = await resolveReference(packages, root, item).catch(() => undefined);
|
|
87
|
+
if (declared && target && declared.root === target.root)
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new Error(`Cannot resolve schema for ${packageName}: invalid or unavailable Plugin manifest`, { cause: error });
|
|
93
|
+
}
|
|
94
|
+
if (!target)
|
|
95
|
+
return undefined;
|
|
96
|
+
const schema = (await this.#declarations?.())?.get(target.root) ?? await readOwnSchema(target.root, packageName, true);
|
|
97
|
+
if (!schema)
|
|
98
|
+
return null;
|
|
99
|
+
assertSchemaStructure(schema, `config declaration for ${packageName}`);
|
|
100
|
+
const form = jsonSchemaToConsoleSchema(schema);
|
|
101
|
+
return form ? Object.freeze({ ...form, declared: false }) : null;
|
|
102
|
+
}
|
|
103
|
+
async readAll(keys, document) {
|
|
104
|
+
let resolver;
|
|
105
|
+
const declarations = await this.#declarations?.();
|
|
106
|
+
if (!document) {
|
|
107
|
+
const file = findConfigFile(this.#projectRoot);
|
|
108
|
+
if (file)
|
|
109
|
+
document = (await createConfigDocument(join(this.#projectRoot, file)).read()).document;
|
|
110
|
+
}
|
|
111
|
+
const entries = await Promise.all([...keys].map(async (key) => [
|
|
112
|
+
key,
|
|
113
|
+
await this.#read(key, () => resolver ??= NodePackageResolver.create(this.#projectRoot), declarations, false, document),
|
|
114
|
+
]));
|
|
115
|
+
return Object.fromEntries(entries.filter((entry) => entry[1] != null));
|
|
116
|
+
}
|
|
117
|
+
async #read(pluginName, resolver, declarations, rawSchema = false, currentDocument) {
|
|
118
|
+
if (HOST_CONFIG_KEY_SET.has(pluginName)) {
|
|
119
|
+
// Host sections share the Runtime schema and its local definition scope.
|
|
120
|
+
// This is only a display projection; Runtime remains the config validator.
|
|
121
|
+
const raw = pluginName === 'plugin'
|
|
122
|
+
? declarations?.get(await realpath(this.#projectRoot)) ?? await readOwnSchema(this.#projectRoot, 'plugin', !rawSchema)
|
|
123
|
+
: HOST_CONFIG_SCHEMA.properties[pluginName];
|
|
124
|
+
if (rawSchema)
|
|
125
|
+
return raw && pluginName !== 'plugin'
|
|
126
|
+
? { ...raw, $defs: HOST_CONFIG_SCHEMA.$defs } : raw ?? null;
|
|
127
|
+
if (raw && pluginName === 'plugin')
|
|
128
|
+
assertSchemaStructure(raw, 'config declaration for plugin');
|
|
129
|
+
return jsonSchemaToConsoleSchema(raw && pluginName !== 'plugin' ? { ...raw, $defs: HOST_CONFIG_SCHEMA.$defs } : raw);
|
|
130
|
+
}
|
|
131
|
+
if (!/^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/u.test(pluginName)
|
|
132
|
+
&& !pluginName.split('/').every((key) => /^[a-z0-9][a-z0-9-]*$/u.test(key))) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
let target;
|
|
136
|
+
try {
|
|
137
|
+
const packages = await resolver();
|
|
138
|
+
const root = await packages.root(this.#projectRoot);
|
|
139
|
+
const file = findConfigFile(this.#projectRoot);
|
|
140
|
+
const document = currentDocument ?? (file ? (await createConfigDocument(join(this.#projectRoot, file)).read()).document : {});
|
|
141
|
+
const declaration = readApplicationPluginDeclaration(document);
|
|
142
|
+
if (declaration.version === 2) {
|
|
143
|
+
const name = pluginName.startsWith('root/') ? pluginName.slice(5) : pluginName;
|
|
144
|
+
const exact = declaration.instances.find(item => item.instanceKey === name);
|
|
145
|
+
const matches = exact ? [exact] : declaration.instances.filter(item => item.package === pluginName);
|
|
146
|
+
if (matches.length > 1)
|
|
147
|
+
throw new Error('此包存在多个插件实例,请使用精确 instanceKey');
|
|
148
|
+
if (matches[0])
|
|
149
|
+
target = await resolveReference(packages, root, matches[0]);
|
|
150
|
+
else if (pluginName === 'root')
|
|
151
|
+
target = root;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (error instanceof Error && error.message.includes('多个插件实例'))
|
|
156
|
+
throw error;
|
|
157
|
+
throw new Error(`Cannot resolve schema for ${pluginName}: invalid or unavailable Plugin manifest`, { cause: error });
|
|
158
|
+
}
|
|
159
|
+
if (!target)
|
|
160
|
+
return null;
|
|
161
|
+
const schema = declarations?.get(target.root) ?? await readOwnSchema(target.root, pluginName, !rawSchema);
|
|
162
|
+
if (rawSchema)
|
|
163
|
+
return schema;
|
|
164
|
+
if (schema)
|
|
165
|
+
assertSchemaStructure(schema, `config declaration for ${pluginName}`);
|
|
166
|
+
return jsonSchemaToConsoleSchema(schema);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function resolveReference(resolver, owner, reference) {
|
|
170
|
+
try {
|
|
171
|
+
const pkg = await resolver.resolve(reference.package, owner);
|
|
172
|
+
return pkg.packageJson.zhin.type === 'plugin' ? pkg : undefined;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (error instanceof PackageResolutionError)
|
|
176
|
+
return undefined;
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Missing schemas have no form; malformed schemas must not masquerade as missing. */
|
|
181
|
+
async function readOwnSchema(root, name, forForm = true) {
|
|
182
|
+
let text;
|
|
183
|
+
try {
|
|
184
|
+
text = await readFile(join(root, 'schema.json'), 'utf8');
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
|
|
188
|
+
return null;
|
|
189
|
+
throw new Error(`Cannot read schema.json for ${name}`, { cause: error });
|
|
190
|
+
}
|
|
191
|
+
let value;
|
|
192
|
+
try {
|
|
193
|
+
value = JSON.parse(text);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
throw new Error(`Invalid JSON in schema.json for ${name}`);
|
|
197
|
+
}
|
|
198
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
199
|
+
throw new Error(`schema.json for ${name} must contain a JSON Schema object`);
|
|
200
|
+
}
|
|
201
|
+
const schema = value;
|
|
202
|
+
if (schema.type !== undefined && schema.type !== 'object') {
|
|
203
|
+
throw new Error(`schema.json for ${name} root schema type must be object`);
|
|
204
|
+
}
|
|
205
|
+
if (schema.properties !== undefined
|
|
206
|
+
&& (!schema.properties || typeof schema.properties !== 'object' || Array.isArray(schema.properties))) {
|
|
207
|
+
throw new Error(`schema.json for ${name} properties must be an object`);
|
|
208
|
+
}
|
|
209
|
+
if (forForm)
|
|
210
|
+
assertSchemaStructure(schema, `schema.json for ${name}`);
|
|
211
|
+
return schema;
|
|
212
|
+
}
|
|
213
|
+
/** Reject malformed child definitions before the form converter can silently omit them. */
|
|
214
|
+
function assertSchemaStructure(value, location) {
|
|
215
|
+
if (typeof value === 'boolean')
|
|
216
|
+
return;
|
|
217
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
218
|
+
throw new Error(`${location} must contain a supported schema object`);
|
|
219
|
+
}
|
|
220
|
+
const schema = value;
|
|
221
|
+
if (schema.$ref !== undefined && typeof schema.$ref !== 'string')
|
|
222
|
+
throw new Error(`${location}.$ref must be a string`);
|
|
223
|
+
if (schema.type !== undefined && !(typeof schema.type === 'string' || Array.isArray(schema.type) && schema.type.every(type => typeof type === 'string')))
|
|
224
|
+
throw new Error(`${location}.type must be a string or string array`);
|
|
225
|
+
if (schema.required !== undefined) {
|
|
226
|
+
const nativeFlag = typeof schema.required === 'boolean' && isConsoleSchemaJson(schema);
|
|
227
|
+
if (!nativeFlag && (!Array.isArray(schema.required)
|
|
228
|
+
|| !schema.required.every((key) => typeof key === 'string')
|
|
229
|
+
|| new Set(schema.required).size !== schema.required.length)) {
|
|
230
|
+
throw new Error(`${location}.required must be a unique string array or a native Schema boolean`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const key of ['properties', '$defs', 'definitions', 'object', 'patternProperties', 'dependentSchemas']) {
|
|
234
|
+
const children = schema[key];
|
|
235
|
+
if (children === undefined)
|
|
236
|
+
continue;
|
|
237
|
+
if (!children || typeof children !== 'object' || Array.isArray(children)) {
|
|
238
|
+
throw new Error(`${location}.${key} must be an object`);
|
|
239
|
+
}
|
|
240
|
+
for (const [name, child] of Object.entries(children)) {
|
|
241
|
+
assertSchemaStructure(child, `${location}.${key}.${name}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (const key of ['items', 'inner', 'additionalProperties', 'additional', 'additionalItems', 'contains', 'not', 'if', 'then', 'else']) {
|
|
245
|
+
const child = schema[key];
|
|
246
|
+
if (child === undefined || (key === 'additionalProperties' && typeof child === 'boolean'))
|
|
247
|
+
continue;
|
|
248
|
+
if (key === 'items' && Array.isArray(child)) {
|
|
249
|
+
child.forEach((item, index) => assertSchemaStructure(item, `${location}.items[${index}]`));
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
assertSchemaStructure(child, `${location}.${key}`);
|
|
253
|
+
}
|
|
254
|
+
for (const key of ['list', 'anyOf', 'oneOf', 'allOf', 'prefixItems']) {
|
|
255
|
+
const children = schema[key];
|
|
256
|
+
if (children === undefined)
|
|
257
|
+
continue;
|
|
258
|
+
if (!Array.isArray(children) || ['anyOf', 'oneOf', 'allOf'].includes(key) && children.length === 0)
|
|
259
|
+
throw new Error(`${location}.${key} must be a non-empty schema array`);
|
|
260
|
+
children.forEach((child, index) => assertSchemaStructure(child, `${location}.${key}[${index}]`));
|
|
261
|
+
}
|
|
262
|
+
}
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** `@zhin.js/gateway/config` — raw project configuration and `.env` access. */
|
|
2
|
+
export * from './config/ai-config-loader.js';
|
|
3
|
+
export * from './config/configuration.js';
|
|
4
|
+
export * from './config/configuration-chat-policy.js';
|
|
5
|
+
export * from './config/configuration-patch.js';
|
|
6
|
+
export * from './config/configuration-path.js';
|
|
7
|
+
export * from './config/configuration-projection.js';
|
|
8
|
+
export * from './config/configuration-redaction.js';
|
|
9
|
+
export * from './config/console-schema.js';
|
|
10
|
+
export * from './config/environment-files.js';
|
|
11
|
+
export * from './config/plugin-package-map.js';
|
|
12
|
+
export * from './config/plugin-schema-catalog.js';
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** `@zhin.js/gateway/config` — raw project configuration and `.env` access. */
|
|
2
|
+
export * from './config/ai-config-loader.js';
|
|
3
|
+
export * from './config/configuration.js';
|
|
4
|
+
export * from './config/configuration-chat-policy.js';
|
|
5
|
+
export * from './config/configuration-patch.js';
|
|
6
|
+
export * from './config/configuration-path.js';
|
|
7
|
+
export * from './config/configuration-projection.js';
|
|
8
|
+
export * from './config/configuration-redaction.js';
|
|
9
|
+
export * from './config/console-schema.js';
|
|
10
|
+
export * from './config/environment-files.js';
|
|
11
|
+
export * from './config/plugin-package-map.js';
|
|
12
|
+
export * from './config/plugin-schema-catalog.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ProjectDatabaseConfig = {
|
|
2
|
+
readonly dialect: string;
|
|
3
|
+
} & Readonly<Record<string, unknown>>;
|
|
4
|
+
/** Project-relative default when no `database` block is configured. */
|
|
5
|
+
export declare const DEFAULT_PROJECT_DATABASE_FILE: string;
|
|
6
|
+
/** Validates an expanded `database` block (port of `normalizeDatabaseHostConfig`). */
|
|
7
|
+
export declare function normalizeProjectDatabaseConfig(input: ProjectDatabaseConfig): ProjectDatabaseConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the expanded root config document's `database` block. A relative SQLite file is
|
|
10
|
+
* anchored at the project root (the instance runs with the project root as cwd, so both
|
|
11
|
+
* processes open the same file).
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveProjectDatabaseConfig(projectRoot: string, document: unknown): Promise<ProjectDatabaseConfig>;
|
|
14
|
+
/**
|
|
15
|
+
* The root config document with `${…}` expanded against process env and the project `.env`
|
|
16
|
+
* layers of `environment` (missing variables become empty, as in the instance). Only the gateway's
|
|
17
|
+
* own blocks are read from it (`database`, `console.terminal`): plugin declarations are not
|
|
18
|
+
* validated here, so a config the instance rejects still leaves the Console reachable.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadExpandedProjectConfig(projectRoot: string, environment?: string): Promise<Readonly<Record<string, unknown>>>;
|
|
21
|
+
/** {@link resolveProjectDatabaseConfig} over {@link loadExpandedProjectConfig}. */
|
|
22
|
+
export declare function loadProjectDatabaseConfig(projectRoot: string, environment?: string): Promise<ProjectDatabaseConfig>;
|