@openapi-flow/core 0.3.8 → 0.3.10

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Zod `^4.6.5` is a required peer, shared with the host rather than installed as a separately pinned runtime copy. Development tests use 4.6.5; the strict installed-consumer check also uses the host's 4.6.7. This preserves the public schema type boundary without casts or dependency overrides.
4
4
 
5
- Version **0.3.8**. Canonical draft, clarification and runtime-input APIs described below are included in this release source. Original-choice follow-ups derive their display and typed reply in Core. This patch aligns the three libraries for the new n8n response-object capability without adding a Core-specific composition path. Ajv 8.20.0 does not correctly validate a declared `properties.__proto__`; generated validation may reject valid data or omit that property's constraints. This previously accepted limitation remains unchanged, without renaming data or skipping its failing regression. See the [release record](https://github.com/carefreelife98/openapi-flow/blob/main/docs/releases/0.3.8.md) for publication and host status.
5
+ Version **0.3.9**. Canonical draft, clarification and runtime-input APIs described below are included in this release source. Requirement-edit schema descriptions distinguish explicit record editing from workflow behavior changes; runtime validation rules and typed human answers are unchanged. Original-choice follow-ups still derive their display and typed reply in Core. Ajv 8.20.0 does not correctly validate a declared `properties.__proto__`; generated validation may reject valid data or omit that property's constraints. This previously accepted limitation remains unchanged, without renaming data or skipping its failing regression. See the [release record](https://github.com/carefreelife98/openapi-flow/blob/main/docs/releases/0.3.9.md) for publication and host status.
6
6
 
7
7
  Native and fixed-material connection planning accept the optional, existing `WorkflowEntryPointContract` as `entrypoint`. Core validates and snapshots this owner-provided start alongside the other inputs; it does not infer one when absent, add a selectable trigger or let model output modify it.
8
8
 
@@ -0,0 +1,3 @@
1
+ import type { OasValueValidationCache, OasValueValidationCacheOptions } from '../../types/oas-value-validation.js';
2
+ /** Cache compilation only; every supplied value still runs the original validator. */
3
+ export declare function createOasValueValidationCache(options: OasValueValidationCacheOptions): OasValueValidationCache;
@@ -0,0 +1,46 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { isDeepStrictEqual } from 'node:util';
3
+ /** Cache compilation only; every supplied value still runs the original validator. */
4
+ export function createOasValueValidationCache(options) {
5
+ for (const name of ['maximumEntries', 'maximumRetainedSchemaBytes'])
6
+ if (!Number.isSafeInteger(options[name]) || options[name] <= 0)
7
+ throw new Error(`OAS validator cache.${name} must be a positive safe integer`);
8
+ const { maximumEntries, maximumRetainedSchemaBytes } = options;
9
+ const entries = new Map();
10
+ let retainedSchemaBytes = 0;
11
+ return {
12
+ getOrCreate: (schema, dialect, compile) => {
13
+ const key = JSON.stringify([dialect, schema]);
14
+ const existing = entries.get(key);
15
+ // Serialization alone is not identity: undefined and non-JSON values can collide.
16
+ if (existing !== undefined &&
17
+ isDeepStrictEqual(existing.schema, schema)) {
18
+ entries.delete(key);
19
+ entries.set(key, existing);
20
+ return existing.validate;
21
+ }
22
+ // Neither returned schemas nor caller-owned objects can mutate the compiled resource.
23
+ const snapshot = structuredClone(schema);
24
+ const validate = compile(snapshot);
25
+ const bytes = Buffer.byteLength(key);
26
+ // Large schemas remain supported; only their optional retention is bounded.
27
+ if (bytes > maximumRetainedSchemaBytes)
28
+ return validate;
29
+ if (existing !== undefined) {
30
+ entries.delete(key);
31
+ retainedSchemaBytes -= existing.bytes;
32
+ }
33
+ while (entries.size >= maximumEntries ||
34
+ retainedSchemaBytes + bytes > maximumRetainedSchemaBytes) {
35
+ const oldest = entries.entries().next();
36
+ if (oldest.done)
37
+ throw new Error('OAS validator cache retained-size invariant is invalid');
38
+ entries.delete(oldest.value[0]);
39
+ retainedSchemaBytes -= oldest.value[1].bytes;
40
+ }
41
+ entries.set(key, { schema: snapshot, validate, bytes });
42
+ retainedSchemaBytes += bytes;
43
+ return validate;
44
+ },
45
+ };
46
+ }
@@ -2,6 +2,12 @@ import { createHash } from 'node:crypto';
2
2
  import { createValidator } from '@scalar/json-schema-validator';
3
3
  import { normalizeOasValueSchema } from './normalize-oas-value-schema.js';
4
4
  import { isObject } from '../../utils/is-object.js';
5
+ import { createOasValueValidationCache } from './create-oas-value-validation-cache.js';
6
+ // Internal retention budgets, not limits on accepted OAS contracts or request values.
7
+ const compiledValidators = createOasValueValidationCache({
8
+ maximumEntries: 64,
9
+ maximumRetainedSchemaBytes: 1024 * 1024,
10
+ });
5
11
  export function createOasValueValidator(input, dialect, source) {
6
12
  if (typeof input !== 'boolean' && !isObject(input))
7
13
  throw new Error(`${source} must be an OAS schema object or boolean`);
@@ -24,7 +30,11 @@ export function createOasValueValidator(input, dialect, source) {
24
30
  };
25
31
  let validate;
26
32
  try {
27
- validate = createValidator(schema);
33
+ validate = compiledValidators.getOrCreate(schema, dialect, (snapshot) => {
34
+ const compiled = createValidator(snapshot);
35
+ // Scalar parses string documents as YAML; encode actual string values first.
36
+ return (value) => compiled(typeof value === 'string' ? JSON.stringify(value) : value);
37
+ });
28
38
  }
29
39
  catch (error) {
30
40
  throw new Error(`${source} cannot compile its OAS value schema`, {
@@ -33,7 +43,6 @@ export function createOasValueValidator(input, dialect, source) {
33
43
  }
34
44
  return {
35
45
  schema,
36
- // Scalar parses string documents as YAML; encode actual string values first.
37
- validate: (value) => validate(typeof value === 'string' ? JSON.stringify(value) : value),
46
+ validate,
38
47
  };
39
48
  }
@@ -118,11 +118,15 @@ export function createWorkflowDraftEditProposalSchema(options, sources) {
118
118
  }));
119
119
  if (sources.requirementIds.length)
120
120
  for (const kind of ['revise-requirement', 'remove-requirement'])
121
- decisions.push(z.strictObject({
121
+ decisions.push(z
122
+ .strictObject({
122
123
  kind: z.literal(kind),
123
124
  requirementId: z.enum(sources.requirementIds),
124
125
  ...fields,
125
- }));
126
+ })
127
+ .describe(kind === 'revise-requirement'
128
+ ? 'An explicit request to edit the existing requirement record itself: its stored statement, state or scope; not a request to change workflow behavior. API/native node, input, source and connection changes use their own decisions and do not require this bookkeeping edit. A later typed human answer supplies replacement fields.'
129
+ : 'An explicit request to withdraw this existing requirement record itself, not its API/native nodes or connections. Do not infer record withdrawal from workflow behavior changes.'));
126
130
  if (sources.removedRequirementIds.length)
127
131
  decisions.push(z.strictObject({
128
132
  kind: z.literal('restore-requirement'),
@@ -1,8 +1,21 @@
1
1
  import type { createValidator } from '@scalar/json-schema-validator';
2
2
  import type { JsonObject } from './openapi.js';
3
+ import type { SchemaDialect } from './schema-dialect.js';
3
4
  export type OasValueSchema = JsonObject | boolean;
4
5
  export type OasValueValidation = ReturnType<typeof createValidator>;
5
6
  export interface OasValueValidator {
6
7
  schema: JsonObject;
7
8
  validate: OasValueValidation;
8
9
  }
10
+ export interface OasValueValidationCacheOptions {
11
+ maximumEntries: number;
12
+ maximumRetainedSchemaBytes: number;
13
+ }
14
+ export interface OasValueValidationCacheEntry {
15
+ schema: JsonObject;
16
+ validate: OasValueValidation;
17
+ bytes: number;
18
+ }
19
+ export interface OasValueValidationCache {
20
+ getOrCreate(schema: JsonObject, dialect: SchemaDialect, compile: (schema: JsonObject) => OasValueValidation): OasValueValidation;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openapi-flow/core",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "license": "MIT",
5
5
  "description": "Engine-independent OpenAPI catalogs, operation contracts and typed request schemas",
6
6
  "keywords": [