@clowder-ai/plugin-contract 0.1.0-beta.9 → 0.2.0
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/dist/codegen/generate-contract.d.ts +6 -0
- package/dist/codegen/generate-contract.d.ts.map +1 -1
- package/dist/codegen/generate-contract.js +343 -19
- package/dist/codegen/generate-contract.js.map +1 -1
- package/dist/conformance/fixtures.d.ts +3 -1
- package/dist/conformance/fixtures.d.ts.map +1 -1
- package/dist/conformance/fixtures.js +2 -1
- package/dist/conformance/fixtures.js.map +1 -1
- package/dist/conformance/index.d.ts +2 -1
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +2 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/conformance/messaging-behavior-fixture.d.ts +12 -0
- package/dist/conformance/messaging-behavior-fixture.d.ts.map +1 -0
- package/dist/conformance/messaging-behavior-fixture.js +30 -0
- package/dist/conformance/messaging-behavior-fixture.js.map +1 -0
- package/dist/conformance/runner.d.ts.map +1 -1
- package/dist/conformance/runner.js +3 -1
- package/dist/conformance/runner.js.map +1 -1
- package/dist/docx-materialization.d.ts +8 -0
- package/dist/docx-materialization.d.ts.map +1 -0
- package/dist/docx-materialization.js +19 -0
- package/dist/docx-materialization.js.map +1 -0
- package/dist/generated/contract.generated.d.ts +1647 -52
- package/dist/generated/contract.generated.d.ts.map +1 -1
- package/dist/generated/contract.generated.js +72 -3
- package/dist/generated/contract.generated.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/validation/catalog.d.ts +21 -0
- package/dist/validation/catalog.d.ts.map +1 -0
- package/dist/validation/catalog.js +140 -0
- package/dist/validation/catalog.js.map +1 -0
- package/dist/validation/cloud-conversation-host.d.ts +18 -0
- package/dist/validation/cloud-conversation-host.d.ts.map +1 -0
- package/dist/validation/cloud-conversation-host.js +83 -0
- package/dist/validation/cloud-conversation-host.js.map +1 -0
- package/dist/validation/companion-bridge.d.ts +6 -0
- package/dist/validation/companion-bridge.d.ts.map +1 -0
- package/dist/validation/companion-bridge.js +198 -0
- package/dist/validation/companion-bridge.js.map +1 -0
- package/dist/validation/docx-materialization.d.ts +6 -0
- package/dist/validation/docx-materialization.d.ts.map +1 -0
- package/dist/validation/docx-materialization.js +36 -0
- package/dist/validation/docx-materialization.js.map +1 -0
- package/dist/validation/index.d.ts +9 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +9 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/manifest.d.ts.map +1 -1
- package/dist/validation/manifest.js +303 -0
- package/dist/validation/manifest.js.map +1 -1
- package/dist/validation/members.d.ts +4 -0
- package/dist/validation/members.d.ts.map +1 -0
- package/dist/validation/members.js +11 -0
- package/dist/validation/members.js.map +1 -0
- package/dist/validation/messaging-semantic.d.ts.map +1 -1
- package/dist/validation/messaging-semantic.js +98 -0
- package/dist/validation/messaging-semantic.js.map +1 -1
- package/dist/validation/messaging-wire.d.ts +42 -0
- package/dist/validation/messaging-wire.d.ts.map +1 -0
- package/dist/validation/messaging-wire.js +184 -0
- package/dist/validation/messaging-wire.js.map +1 -0
- package/dist/validation/operation-rows.d.ts +25 -0
- package/dist/validation/operation-rows.d.ts.map +1 -0
- package/dist/validation/operation-rows.js +135 -0
- package/dist/validation/operation-rows.js.map +1 -0
- package/dist/validation/signals.d.ts +1 -10
- package/dist/validation/signals.d.ts.map +1 -1
- package/dist/validation/signals.js +0 -174
- package/dist/validation/signals.js.map +1 -1
- package/dist/variant.d.ts +114 -0
- package/dist/variant.d.ts.map +1 -0
- package/dist/variant.js +305 -0
- package/dist/variant.js.map +1 -0
- package/dist/wire/constants.d.ts +2 -1
- package/dist/wire/constants.d.ts.map +1 -1
- package/dist/wire/constants.js +2 -1
- package/dist/wire/constants.js.map +1 -1
- package/dist/wire/dispatch.d.ts +49 -0
- package/dist/wire/dispatch.d.ts.map +1 -0
- package/dist/wire/dispatch.js +880 -0
- package/dist/wire/dispatch.js.map +1 -0
- package/dist/wire/disposition-fixtures.d.ts +9 -1
- package/dist/wire/disposition-fixtures.d.ts.map +1 -1
- package/dist/wire/disposition-fixtures.js +622 -95
- package/dist/wire/disposition-fixtures.js.map +1 -1
- package/dist/wire/envelope.d.ts +31 -0
- package/dist/wire/envelope.d.ts.map +1 -1
- package/dist/wire/envelope.js +48 -1
- package/dist/wire/envelope.js.map +1 -1
- package/dist/wire/errors.d.ts +31 -1
- package/dist/wire/errors.d.ts.map +1 -1
- package/dist/wire/errors.js +36 -0
- package/dist/wire/errors.js.map +1 -1
- package/dist/wire/grants.d.ts +4 -4
- package/dist/wire/grants.js +3 -3
- package/dist/wire/handshake-byte-bounds.d.ts.map +1 -1
- package/dist/wire/handshake-byte-bounds.js +5 -2
- package/dist/wire/handshake-byte-bounds.js.map +1 -1
- package/dist/wire/handshake.d.ts +6 -0
- package/dist/wire/handshake.d.ts.map +1 -1
- package/dist/wire/handshake.js +6 -3
- package/dist/wire/handshake.js.map +1 -1
- package/dist/wire/index.d.ts +13 -9
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +29 -6
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/lifecycle-byte-bounds.d.ts +40 -0
- package/dist/wire/lifecycle-byte-bounds.d.ts.map +1 -0
- package/dist/wire/lifecycle-byte-bounds.js +149 -0
- package/dist/wire/lifecycle-byte-bounds.js.map +1 -0
- package/dist/wire/messaging-byte-bounds.d.ts +62 -0
- package/dist/wire/messaging-byte-bounds.d.ts.map +1 -0
- package/dist/wire/messaging-byte-bounds.js +581 -0
- package/dist/wire/messaging-byte-bounds.js.map +1 -0
- package/dist/wire/registry.d.ts +58 -86
- package/dist/wire/registry.d.ts.map +1 -1
- package/dist/wire/registry.js +45 -88
- package/dist/wire/registry.js.map +1 -1
- package/dist/wire/row-shapes.d.ts +86 -69
- package/dist/wire/row-shapes.d.ts.map +1 -1
- package/dist/wire/row-shapes.js +30 -3
- package/dist/wire/row-shapes.js.map +1 -1
- package/fixtures/behavior/messaging/adversarial-invariants.json +92 -2
- package/fixtures/manifest/invalid/duplicate-signal-type.json +4 -2
- package/fixtures/manifest/invalid/icon-package-escape.json +22 -0
- package/fixtures/manifest/valid/localized-product-metadata.json +27 -0
- package/fixtures/messaging/valid/subscription-delivery.json +1 -1
- package/package.json +16 -3
- package/src/schemas/behavior-fixture.schema.json +903 -116
- package/src/schemas/catalog.schema.json +91 -0
- package/src/schemas/companion-bridge.schema.json +2256 -0
- package/src/schemas/docx-materialization.schema.json +302 -0
- package/src/schemas/manifest.schema.json +2273 -88
- package/src/schemas/messaging.schema.json +692 -41
- package/src/schemas/plugin-metadata.schema.json +62 -0
- package/src/schemas/signal.schema.json +68 -78
- package/dist/wire/signal-byte-bounds.d.ts +0 -97
- package/dist/wire/signal-byte-bounds.d.ts.map +0 -1
- package/dist/wire/signal-byte-bounds.js +0 -229
- package/dist/wire/signal-byte-bounds.js.map +0 -1
- package/fixtures/signals/invalid/destination-injection.json +0 -15
- package/fixtures/signals/invalid/non-utc-occurred-at.json +0 -12
- package/fixtures/signals/valid/feishu-meeting-artifact.json +0 -18
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe configuration-variant resolution (configurationVariants,
|
|
3
|
+
* contractVersion 0.2.0).
|
|
4
|
+
*
|
|
5
|
+
* This module is intentionally free of any `node:` or ajv import so the same
|
|
6
|
+
* code runs in the browser (settings form) and on the server (save/readiness/
|
|
7
|
+
* runtime projection). Value matching is strict `===` against `const`/`enum`
|
|
8
|
+
* declared in each branch's constrained `when` fragment.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import type { ConfigurationField, PluginManifest } from './generated/contract.generated.js';
|
|
13
|
+
export type VariantResolution = {
|
|
14
|
+
readonly status: 'selected';
|
|
15
|
+
readonly branchId: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly status: 'unselected';
|
|
18
|
+
} | {
|
|
19
|
+
readonly status: 'invalid';
|
|
20
|
+
readonly value: unknown;
|
|
21
|
+
};
|
|
22
|
+
export interface EffectiveConfiguration {
|
|
23
|
+
readonly resolution: VariantResolution;
|
|
24
|
+
readonly effectiveValues: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
type StoredValues = Readonly<Record<string, unknown>>;
|
|
27
|
+
/**
|
|
28
|
+
* The single string→type coercion boundary (round-three review P1).
|
|
29
|
+
*
|
|
30
|
+
* Host storage and web drafts transport every scalar as a string, while the
|
|
31
|
+
* contract's requiredness (`===` against declared `const`/`enum`), branch
|
|
32
|
+
* matching, and validation schema (`type: 'boolean'` / `type: 'number'`)
|
|
33
|
+
* are strictly typed. Consumers feed raw stored/drafted strings through this
|
|
34
|
+
* function before calling any other entry point in this module, so both sides
|
|
35
|
+
* evaluate identical values without stringifying the contract's predicates.
|
|
36
|
+
*
|
|
37
|
+
* Rules, per declared non-operation field:
|
|
38
|
+
* - `boolean`: `'true'`/`'false'` become the real booleans; any other string
|
|
39
|
+
* stays a string, so downstream validation fails closed instead of
|
|
40
|
+
* silently coercing garbage.
|
|
41
|
+
* - `number`: a string that trims non-empty and parses to a finite number
|
|
42
|
+
* becomes that number; anything else stays a string (same fail-closed).
|
|
43
|
+
* - every other kind (and every non-string input, which passes through
|
|
44
|
+
* untouched) keeps its value as-is.
|
|
45
|
+
*
|
|
46
|
+
* Keys that no declared field owns are passed through unchanged.
|
|
47
|
+
*/
|
|
48
|
+
export declare function coerceStoredValues(manifest: PluginManifest, values: StoredValues): Record<string, unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* Three-state variant resolution for a stored (or drafted) configuration.
|
|
51
|
+
*
|
|
52
|
+
* - `unselected`: no discriminator value present — neither stored nor declared
|
|
53
|
+
* as the discriminator field's `default` (or the manifest declares no
|
|
54
|
+
* variants, preserving pre-0.2.0 behavior).
|
|
55
|
+
* - `invalid`: a discriminator value is present (stored, or a declared default)
|
|
56
|
+
* but no branch `when` matches.
|
|
57
|
+
* - `selected`: exactly one branch matches; the first match wins if a
|
|
58
|
+
* non-conformant manifest makes branches overlap.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveConfigurationVariant(manifest: PluginManifest, values: StoredValues): VariantResolution;
|
|
61
|
+
/**
|
|
62
|
+
* Computes the effective configuration: common fields (not claimed by any
|
|
63
|
+
* branch) plus, when a branch is selected, that branch's fields. Missing
|
|
64
|
+
* stored values fall back to the declared field default — consumers never
|
|
65
|
+
* depend on save-time materialization. On `unselected`/`invalid` only common
|
|
66
|
+
* fields are returned; an invalid discriminator value is kept as stored (no
|
|
67
|
+
* silent fallback to the declared default).
|
|
68
|
+
*/
|
|
69
|
+
export declare function computeEffectiveConfiguration(manifest: PluginManifest, storedValues: StoredValues): EffectiveConfiguration;
|
|
70
|
+
/**
|
|
71
|
+
* The single source of truth for whether a field is required, given an
|
|
72
|
+
* effective (or drafted) configuration object.
|
|
73
|
+
*
|
|
74
|
+
* When `requiredWhen` is present it fully replaces the `required` flag: the
|
|
75
|
+
* field is required iff the referenced field's current value strictly
|
|
76
|
+
* (`===`, no stringification) equals one of the declared conditions. When
|
|
77
|
+
* `requiredWhen` is absent, the plain `required` flag decides. Branch
|
|
78
|
+
* membership never influences requiredness — it only decides whether a field
|
|
79
|
+
* is active at all.
|
|
80
|
+
*
|
|
81
|
+
* `values` must already be typed: pass the product of
|
|
82
|
+
* {@link computeEffectiveConfiguration} or run raw string storage/drafts
|
|
83
|
+
* through {@link coerceStoredValues} first. This function does not coerce —
|
|
84
|
+
* a string `"true"` against a declared `true` condition is *not* a match.
|
|
85
|
+
*/
|
|
86
|
+
export declare function evaluateFieldRequiredness(field: ConfigurationField, values: StoredValues): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Compiles the manifest into a standard JSON Schema for validating the
|
|
89
|
+
* *effective* configuration object (see {@link computeEffectiveConfiguration}),
|
|
90
|
+
* not the raw stored object. Consumers must enable standard URI format validation
|
|
91
|
+
* (for Ajv, register ajv-formats before compiling).
|
|
92
|
+
* Operation controls have no scalar value and are omitted; their target-based
|
|
93
|
+
* configured state remains the responsibility of the operation consumer.
|
|
94
|
+
*
|
|
95
|
+
* - Common fields (not claimed by any branch) are constrained at the top
|
|
96
|
+
* level: unconditionally required fields land in `required` and get a kind
|
|
97
|
+
* constraint (e.g. `{type:'string', minLength:1}` for string-likes, so a
|
|
98
|
+
* present `null` or empty string does not count as configured);
|
|
99
|
+
* `requiredWhen` fields become top-level `if/then` clauses keyed on the
|
|
100
|
+
* referenced field's runtime value.
|
|
101
|
+
* - Each branch compiles to one `oneOf` member containing the cloned `when`
|
|
102
|
+
* fragment, the discriminator in `required`, and its active fields handled
|
|
103
|
+
* the same way (static `required` for plain `required: true`, `if/then` for
|
|
104
|
+
* `requiredWhen`). A field that is `requiredWhen`-gated against the
|
|
105
|
+
* discriminator therefore stays optional in a member whose discriminator
|
|
106
|
+
* values do not satisfy the condition — enum branches covering several
|
|
107
|
+
* options keep working without merging values.
|
|
108
|
+
*
|
|
109
|
+
* Requiredness semantics are exactly {@link evaluateFieldRequiredness}; this
|
|
110
|
+
* schema is the ajv-executable form of that function plus kind constraints.
|
|
111
|
+
*/
|
|
112
|
+
export declare function buildVariantValidationSchema(manifest: PluginManifest): Record<string, unknown>;
|
|
113
|
+
export {};
|
|
114
|
+
//# sourceMappingURL=variant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant.d.ts","sourceRoot":"","sources":["../src/variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAE5F,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACjC;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnD;AAID,KAAK,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AA8BtD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwBzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,YAAY,GACnB,iBAAiB,CAsBnB;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,YAAY,GACzB,sBAAsB,CAsCxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,YAAY,GACnB,OAAO,CAQT;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,cAAc,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4DzB"}
|
package/dist/variant.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe configuration-variant resolution (configurationVariants,
|
|
3
|
+
* contractVersion 0.2.0).
|
|
4
|
+
*
|
|
5
|
+
* This module is intentionally free of any `node:` or ajv import so the same
|
|
6
|
+
* code runs in the browser (settings form) and on the server (save/readiness/
|
|
7
|
+
* runtime projection). Value matching is strict `===` against `const`/`enum`
|
|
8
|
+
* declared in each branch's constrained `when` fragment.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
function matchesConstraint(constraint, value) {
|
|
13
|
+
if (typeof constraint !== 'object' || constraint === null)
|
|
14
|
+
return false;
|
|
15
|
+
const candidate = constraint;
|
|
16
|
+
if ('const' in candidate)
|
|
17
|
+
return value === candidate['const'];
|
|
18
|
+
if ('enum' in candidate && Array.isArray(candidate['enum'])) {
|
|
19
|
+
return candidate['enum'].some((option) => option === value);
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
function branchMatches(branch, discriminator, value) {
|
|
24
|
+
const properties = branch.when['properties'];
|
|
25
|
+
if (typeof properties !== 'object' || properties === null)
|
|
26
|
+
return false;
|
|
27
|
+
return matchesConstraint(properties[discriminator], value);
|
|
28
|
+
}
|
|
29
|
+
function fieldDefault(field) {
|
|
30
|
+
return 'default' in field ? field.default : undefined;
|
|
31
|
+
}
|
|
32
|
+
function effectiveFieldValue(field, storedValues) {
|
|
33
|
+
const stored = Object.hasOwn(storedValues, field.key) ? storedValues[field.key] : undefined;
|
|
34
|
+
return stored === undefined ? fieldDefault(field) : stored;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The single string→type coercion boundary (round-three review P1).
|
|
38
|
+
*
|
|
39
|
+
* Host storage and web drafts transport every scalar as a string, while the
|
|
40
|
+
* contract's requiredness (`===` against declared `const`/`enum`), branch
|
|
41
|
+
* matching, and validation schema (`type: 'boolean'` / `type: 'number'`)
|
|
42
|
+
* are strictly typed. Consumers feed raw stored/drafted strings through this
|
|
43
|
+
* function before calling any other entry point in this module, so both sides
|
|
44
|
+
* evaluate identical values without stringifying the contract's predicates.
|
|
45
|
+
*
|
|
46
|
+
* Rules, per declared non-operation field:
|
|
47
|
+
* - `boolean`: `'true'`/`'false'` become the real booleans; any other string
|
|
48
|
+
* stays a string, so downstream validation fails closed instead of
|
|
49
|
+
* silently coercing garbage.
|
|
50
|
+
* - `number`: a string that trims non-empty and parses to a finite number
|
|
51
|
+
* becomes that number; anything else stays a string (same fail-closed).
|
|
52
|
+
* - every other kind (and every non-string input, which passes through
|
|
53
|
+
* untouched) keeps its value as-is.
|
|
54
|
+
*
|
|
55
|
+
* Keys that no declared field owns are passed through unchanged.
|
|
56
|
+
*/
|
|
57
|
+
export function coerceStoredValues(manifest, values) {
|
|
58
|
+
const kinds = new Map();
|
|
59
|
+
for (const field of manifest.configuration ?? []) {
|
|
60
|
+
if (field.kind !== 'operation')
|
|
61
|
+
kinds.set(field.key, field.kind);
|
|
62
|
+
}
|
|
63
|
+
const coerced = {};
|
|
64
|
+
for (const [key, value] of Object.entries(values)) {
|
|
65
|
+
const kind = kinds.get(key);
|
|
66
|
+
if (typeof value !== 'string' || kind === undefined) {
|
|
67
|
+
coerced[key] = value;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (kind === 'boolean') {
|
|
71
|
+
coerced[key] = value === 'true' ? true : value === 'false' ? false : value;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (kind === 'number') {
|
|
75
|
+
const trimmed = value.trim();
|
|
76
|
+
coerced[key] = trimmed.length > 0 && Number.isFinite(Number(trimmed)) ? Number(trimmed) : value;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
coerced[key] = value;
|
|
80
|
+
}
|
|
81
|
+
return coerced;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Three-state variant resolution for a stored (or drafted) configuration.
|
|
85
|
+
*
|
|
86
|
+
* - `unselected`: no discriminator value present — neither stored nor declared
|
|
87
|
+
* as the discriminator field's `default` (or the manifest declares no
|
|
88
|
+
* variants, preserving pre-0.2.0 behavior).
|
|
89
|
+
* - `invalid`: a discriminator value is present (stored, or a declared default)
|
|
90
|
+
* but no branch `when` matches.
|
|
91
|
+
* - `selected`: exactly one branch matches; the first match wins if a
|
|
92
|
+
* non-conformant manifest makes branches overlap.
|
|
93
|
+
*/
|
|
94
|
+
export function resolveConfigurationVariant(manifest, values) {
|
|
95
|
+
const variants = manifest.configurationVariants;
|
|
96
|
+
if (variants === undefined)
|
|
97
|
+
return { status: 'unselected' };
|
|
98
|
+
const stored = coerceStoredValues(manifest, values);
|
|
99
|
+
let value = Object.hasOwn(stored, variants.discriminator)
|
|
100
|
+
? stored[variants.discriminator]
|
|
101
|
+
: undefined;
|
|
102
|
+
if (value === undefined) {
|
|
103
|
+
const discriminatorField = (manifest.configuration ?? []).find((field) => field.key === variants.discriminator);
|
|
104
|
+
value = discriminatorField === undefined ? undefined : fieldDefault(discriminatorField);
|
|
105
|
+
}
|
|
106
|
+
if (value === undefined)
|
|
107
|
+
return { status: 'unselected' };
|
|
108
|
+
for (const branch of variants.branches) {
|
|
109
|
+
if (branchMatches(branch, variants.discriminator, value)) {
|
|
110
|
+
return { status: 'selected', branchId: branch.id };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { status: 'invalid', value };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Computes the effective configuration: common fields (not claimed by any
|
|
117
|
+
* branch) plus, when a branch is selected, that branch's fields. Missing
|
|
118
|
+
* stored values fall back to the declared field default — consumers never
|
|
119
|
+
* depend on save-time materialization. On `unselected`/`invalid` only common
|
|
120
|
+
* fields are returned; an invalid discriminator value is kept as stored (no
|
|
121
|
+
* silent fallback to the declared default).
|
|
122
|
+
*/
|
|
123
|
+
export function computeEffectiveConfiguration(manifest, storedValues) {
|
|
124
|
+
const configuration = manifest.configuration ?? [];
|
|
125
|
+
const variants = manifest.configurationVariants;
|
|
126
|
+
const stored = coerceStoredValues(manifest, storedValues);
|
|
127
|
+
const resolution = resolveConfigurationVariant(manifest, stored);
|
|
128
|
+
const branchFieldKeys = new Set();
|
|
129
|
+
if (variants !== undefined) {
|
|
130
|
+
for (const branch of variants.branches) {
|
|
131
|
+
for (const key of branch.fields)
|
|
132
|
+
branchFieldKeys.add(key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const activeBranch = resolution.status === 'selected' && variants !== undefined
|
|
136
|
+
? variants.branches.find((branch) => branch.id === resolution.branchId)
|
|
137
|
+
: undefined;
|
|
138
|
+
const activeKeys = new Set(activeBranch?.fields ?? []);
|
|
139
|
+
const effectiveValues = {};
|
|
140
|
+
for (const field of configuration) {
|
|
141
|
+
if (field.kind === 'operation')
|
|
142
|
+
continue;
|
|
143
|
+
const isBranchField = branchFieldKeys.has(field.key);
|
|
144
|
+
const isActive = activeKeys.has(field.key);
|
|
145
|
+
if (isBranchField && !isActive)
|
|
146
|
+
continue;
|
|
147
|
+
if (resolution.status === 'invalid' &&
|
|
148
|
+
variants !== undefined &&
|
|
149
|
+
field.key === variants.discriminator) {
|
|
150
|
+
effectiveValues[field.key] = stored[field.key];
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
effectiveValues[field.key] = effectiveFieldValue(field, stored);
|
|
154
|
+
}
|
|
155
|
+
return { resolution, effectiveValues };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The single source of truth for whether a field is required, given an
|
|
159
|
+
* effective (or drafted) configuration object.
|
|
160
|
+
*
|
|
161
|
+
* When `requiredWhen` is present it fully replaces the `required` flag: the
|
|
162
|
+
* field is required iff the referenced field's current value strictly
|
|
163
|
+
* (`===`, no stringification) equals one of the declared conditions. When
|
|
164
|
+
* `requiredWhen` is absent, the plain `required` flag decides. Branch
|
|
165
|
+
* membership never influences requiredness — it only decides whether a field
|
|
166
|
+
* is active at all.
|
|
167
|
+
*
|
|
168
|
+
* `values` must already be typed: pass the product of
|
|
169
|
+
* {@link computeEffectiveConfiguration} or run raw string storage/drafts
|
|
170
|
+
* through {@link coerceStoredValues} first. This function does not coerce —
|
|
171
|
+
* a string `"true"` against a declared `true` condition is *not* a match.
|
|
172
|
+
*/
|
|
173
|
+
export function evaluateFieldRequiredness(field, values) {
|
|
174
|
+
const requiredWhen = field.requiredWhen;
|
|
175
|
+
if (requiredWhen === undefined)
|
|
176
|
+
return field.required === true;
|
|
177
|
+
const conditions = Array.isArray(requiredWhen.value)
|
|
178
|
+
? requiredWhen.value
|
|
179
|
+
: [requiredWhen.value];
|
|
180
|
+
const actual = Object.hasOwn(values, requiredWhen.key) ? values[requiredWhen.key] : undefined;
|
|
181
|
+
return conditions.some((condition) => condition === actual);
|
|
182
|
+
}
|
|
183
|
+
function fieldValueConstraint(field) {
|
|
184
|
+
switch (field.kind) {
|
|
185
|
+
case 'string':
|
|
186
|
+
case 'secret':
|
|
187
|
+
return { type: 'string', minLength: 1, pattern: '\\S' };
|
|
188
|
+
case 'url':
|
|
189
|
+
return { type: 'string', minLength: 1, format: 'uri' };
|
|
190
|
+
case 'select':
|
|
191
|
+
return { type: 'string', enum: (field.options ?? []).map((option) => option.value) };
|
|
192
|
+
case 'number':
|
|
193
|
+
return { type: 'number' };
|
|
194
|
+
case 'boolean':
|
|
195
|
+
return { type: 'boolean' };
|
|
196
|
+
default:
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function conditionalRequirement(field) {
|
|
201
|
+
const requiredWhen = field.requiredWhen;
|
|
202
|
+
if (requiredWhen === undefined)
|
|
203
|
+
return undefined;
|
|
204
|
+
const conditions = Array.isArray(requiredWhen.value)
|
|
205
|
+
? requiredWhen.value
|
|
206
|
+
: [requiredWhen.value];
|
|
207
|
+
const constraint = fieldValueConstraint(field);
|
|
208
|
+
return {
|
|
209
|
+
if: {
|
|
210
|
+
properties: { [requiredWhen.key]: { enum: [...new Set(conditions)] } },
|
|
211
|
+
required: [requiredWhen.key],
|
|
212
|
+
},
|
|
213
|
+
then: {
|
|
214
|
+
required: [field.key],
|
|
215
|
+
...(Object.keys(constraint).length > 0 ? { properties: { [field.key]: constraint } } : {}),
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Compiles the manifest into a standard JSON Schema for validating the
|
|
221
|
+
* *effective* configuration object (see {@link computeEffectiveConfiguration}),
|
|
222
|
+
* not the raw stored object. Consumers must enable standard URI format validation
|
|
223
|
+
* (for Ajv, register ajv-formats before compiling).
|
|
224
|
+
* Operation controls have no scalar value and are omitted; their target-based
|
|
225
|
+
* configured state remains the responsibility of the operation consumer.
|
|
226
|
+
*
|
|
227
|
+
* - Common fields (not claimed by any branch) are constrained at the top
|
|
228
|
+
* level: unconditionally required fields land in `required` and get a kind
|
|
229
|
+
* constraint (e.g. `{type:'string', minLength:1}` for string-likes, so a
|
|
230
|
+
* present `null` or empty string does not count as configured);
|
|
231
|
+
* `requiredWhen` fields become top-level `if/then` clauses keyed on the
|
|
232
|
+
* referenced field's runtime value.
|
|
233
|
+
* - Each branch compiles to one `oneOf` member containing the cloned `when`
|
|
234
|
+
* fragment, the discriminator in `required`, and its active fields handled
|
|
235
|
+
* the same way (static `required` for plain `required: true`, `if/then` for
|
|
236
|
+
* `requiredWhen`). A field that is `requiredWhen`-gated against the
|
|
237
|
+
* discriminator therefore stays optional in a member whose discriminator
|
|
238
|
+
* values do not satisfy the condition — enum branches covering several
|
|
239
|
+
* options keep working without merging values.
|
|
240
|
+
*
|
|
241
|
+
* Requiredness semantics are exactly {@link evaluateFieldRequiredness}; this
|
|
242
|
+
* schema is the ajv-executable form of that function plus kind constraints.
|
|
243
|
+
*/
|
|
244
|
+
export function buildVariantValidationSchema(manifest) {
|
|
245
|
+
const variants = manifest.configurationVariants;
|
|
246
|
+
const configuration = manifest.configuration ?? [];
|
|
247
|
+
const configByKey = new Map();
|
|
248
|
+
for (const field of configuration)
|
|
249
|
+
configByKey.set(field.key, field);
|
|
250
|
+
const branchFieldKeys = new Set();
|
|
251
|
+
for (const branch of variants?.branches ?? []) {
|
|
252
|
+
for (const key of branch.fields)
|
|
253
|
+
branchFieldKeys.add(key);
|
|
254
|
+
}
|
|
255
|
+
const properties = {};
|
|
256
|
+
const required = [];
|
|
257
|
+
const commonConditionals = [];
|
|
258
|
+
for (const field of configuration) {
|
|
259
|
+
if (field.kind === 'operation' || branchFieldKeys.has(field.key))
|
|
260
|
+
continue;
|
|
261
|
+
const constraint = fieldValueConstraint(field);
|
|
262
|
+
if (Object.keys(constraint).length > 0)
|
|
263
|
+
properties[field.key] = constraint;
|
|
264
|
+
const conditional = conditionalRequirement(field);
|
|
265
|
+
if (conditional !== undefined) {
|
|
266
|
+
commonConditionals.push(conditional);
|
|
267
|
+
}
|
|
268
|
+
else if (field.required === true) {
|
|
269
|
+
required.push(field.key);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const oneOf = (variants?.branches ?? []).map((branch) => {
|
|
273
|
+
const memberProperties = structuredClone((branch.when['properties'] ?? {}));
|
|
274
|
+
const memberRequired = [variants.discriminator];
|
|
275
|
+
const memberConditionals = [];
|
|
276
|
+
for (const key of branch.fields) {
|
|
277
|
+
const field = configByKey.get(key);
|
|
278
|
+
if (field === undefined || field.kind === 'operation')
|
|
279
|
+
continue;
|
|
280
|
+
const constraint = fieldValueConstraint(field);
|
|
281
|
+
if (Object.keys(constraint).length > 0)
|
|
282
|
+
memberProperties[key] = constraint;
|
|
283
|
+
const conditional = conditionalRequirement(field);
|
|
284
|
+
if (conditional !== undefined) {
|
|
285
|
+
memberConditionals.push(conditional);
|
|
286
|
+
}
|
|
287
|
+
else if (field.required === true) {
|
|
288
|
+
memberRequired.push(key);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
properties: memberProperties,
|
|
293
|
+
required: memberRequired,
|
|
294
|
+
...(memberConditionals.length > 0 ? { allOf: memberConditionals } : {}),
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
type: 'object',
|
|
299
|
+
properties,
|
|
300
|
+
...(required.length > 0 ? { required } : {}),
|
|
301
|
+
...(commonConditionals.length > 0 ? { allOf: commonConditionals } : {}),
|
|
302
|
+
...(oneOf.length > 0 ? { oneOf } : {}),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=variant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant.js","sourceRoot":"","sources":["../src/variant.ts"],"names":[],"mappings":"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"}
|
package/dist/wire/constants.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ export declare const JSONRPC_VERSION: "2.0";
|
|
|
14
14
|
/**
|
|
15
15
|
* Maximum payload bytes per element payload (frozen x-clowder-bounds).
|
|
16
16
|
* Open-membered payloads (MediaRefElementPayload, RichBlockElementPayload)
|
|
17
|
-
*
|
|
17
|
+
* use their contract schemas for structural narrowing. Unknown extension payloads remain
|
|
18
|
+
* open and are bounded by this ceiling.
|
|
18
19
|
*/
|
|
19
20
|
export declare const MAX_ELEMENT_PAYLOAD_BYTES: 65536;
|
|
20
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/wire/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sEAAsE;AACtE,eAAO,MAAM,eAAe,EAAG,OAAkB,CAAC;AAElD,oCAAoC;AACpC,eAAO,MAAM,YAAY,EAAG,OAAgB,CAAC;AAE7C,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,EAAG,OAAgB,CAAC;AAEjD,gDAAgD;AAChD,eAAO,MAAM,eAAe,EAAG,KAAc,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/wire/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sEAAsE;AACtE,eAAO,MAAM,eAAe,EAAG,OAAkB,CAAC;AAElD,oCAAoC;AACpC,eAAO,MAAM,YAAY,EAAG,OAAgB,CAAC;AAE7C,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,EAAG,OAAgB,CAAC;AAEjD,gDAAgD;AAChD,eAAO,MAAM,eAAe,EAAG,KAAc,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAG,KAAe,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAG,MAAgB,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAG,EAAW,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAG,GAAY,CAAC;AAErD;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAG,EAAW,CAAC"}
|
package/dist/wire/constants.js
CHANGED
|
@@ -14,7 +14,8 @@ export const JSONRPC_VERSION = '2.0';
|
|
|
14
14
|
/**
|
|
15
15
|
* Maximum payload bytes per element payload (frozen x-clowder-bounds).
|
|
16
16
|
* Open-membered payloads (MediaRefElementPayload, RichBlockElementPayload)
|
|
17
|
-
*
|
|
17
|
+
* use their contract schemas for structural narrowing. Unknown extension payloads remain
|
|
18
|
+
* open and are bounded by this ceiling.
|
|
18
19
|
*/
|
|
19
20
|
export const MAX_ELEMENT_PAYLOAD_BYTES = 65_536;
|
|
20
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/wire/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,SAAkB,CAAC;AAElD,oCAAoC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,OAAgB,CAAC;AAE7C,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAgB,CAAC;AAEjD,gDAAgD;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,KAAc,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/wire/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,SAAkB,CAAC;AAElD,oCAAoC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,OAAgB,CAAC;AAE7C,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAgB,CAAC;AAEjD,gDAAgD;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,KAAc,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAe,CAAC;AAEzD;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAgB,CAAC;AAExD;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAW,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAY,CAAC;AAErD;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAW,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire dispatch classifier — pre-dispatch frame classification per the
|
|
3
|
+
* disposition table (T-A through T-M, §3.8-1 plus beta.8 closure).
|
|
4
|
+
*
|
|
5
|
+
* This module classifies decoded NDJSON frames into one of the 13
|
|
6
|
+
* disposition classes. T-A (transport failure) and T-B (JSON parse error)
|
|
7
|
+
* are handled by the NDJSON frame decoder layer — this classifier covers
|
|
8
|
+
* T-C through T-M.
|
|
9
|
+
*
|
|
10
|
+
* A frame that passes all rejection checks returns disposition=null with
|
|
11
|
+
* outcome='accept', indicating a valid request that should be dispatched
|
|
12
|
+
* to a method handler.
|
|
13
|
+
*
|
|
14
|
+
* This module lives in @clowder-ai/plugin-contract (F202 C1: the wire
|
|
15
|
+
* frame classifier was moved here from @clowder-ai/plugin-sdk so the
|
|
16
|
+
* dependency arrow is 插件 → SDK → contract ← Host). It defines nothing
|
|
17
|
+
* new — every classification rule traces to the frozen disposition table
|
|
18
|
+
* in this package, and every key set it uses is co-located with or
|
|
19
|
+
* generated from the contract types it mirrors.
|
|
20
|
+
*/
|
|
21
|
+
import type { DecodedNdjsonFrame, JsonObject } from '../conformance/stdio-harness/ndjson-frame.js';
|
|
22
|
+
import type { DispositionClass } from './disposition.js';
|
|
23
|
+
import type { RequestSnapshot } from './disposition-fixtures.js';
|
|
24
|
+
import { type WireMethodName } from './registry.js';
|
|
25
|
+
export type { RequestSnapshot } from './disposition-fixtures.js';
|
|
26
|
+
export interface InFlightEntry {
|
|
27
|
+
readonly method: WireMethodName;
|
|
28
|
+
readonly requestSnapshot?: RequestSnapshot;
|
|
29
|
+
}
|
|
30
|
+
export interface DispatchResult {
|
|
31
|
+
/** T-class from the disposition table, or null for valid requests. */
|
|
32
|
+
readonly disposition: DispositionClass | null;
|
|
33
|
+
/** Required transport action: close, respond (with error), or accept. */
|
|
34
|
+
readonly outcome: 'close' | 'respond' | 'accept';
|
|
35
|
+
/** For 'respond': the error envelope to write back as NDJSON. */
|
|
36
|
+
readonly response?: JsonObject;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Classify a decoded NDJSON frame into a disposition class.
|
|
40
|
+
*
|
|
41
|
+
* Covers T-C through T-M of the disposition table. T-A and T-B
|
|
42
|
+
* are handled by the NDJSON frame decoder layer.
|
|
43
|
+
*
|
|
44
|
+
* @param frame Decoded frame with raw bytes and parsed value.
|
|
45
|
+
* @param inFlight Map of in-flight request IDs to their entry metadata.
|
|
46
|
+
* @returns The disposition result with class, outcome, and optional error response.
|
|
47
|
+
*/
|
|
48
|
+
export declare function classifyFrame(frame: DecodedNdjsonFrame, inFlight: ReadonlyMap<string, InFlightEntry>): DispatchResult;
|
|
49
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/wire/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAEnG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAwDjE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAiBvB,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC9C,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;CAChC;AAwzBD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,cAAc,CAuEhB"}
|