instant-ctrl-flow-logic 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/loader.ts +65 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instant-ctrl-flow-logic",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "description": "InstantCtrlFlow 引擎:xnl bundle 加载 → 行为树编译 → 无状态单次执行",
6
6
  "type": "module",
@@ -11,7 +11,7 @@
11
11
  "./filesystem": "./src/filesystem.ts"
12
12
  },
13
13
  "dependencies": {
14
- "depa-behavior-tree": "0.1.0",
14
+ "depa-behavior-tree": "0.1.1",
15
15
  "flow-step-space-contract": "0.1.1",
16
16
  "flow-step-space-logic": "0.1.1",
17
17
  "instant-ctrl-flow-contract": "0.1.2",
package/src/loader.ts CHANGED
@@ -64,6 +64,9 @@ const PROFILE_STATEMENTS: Record<FlowForm, ReadonlySet<string>> = {
64
64
  BPCtrlFlow: new Set([...COMMON_STATEMENTS, 'ExternalJob', 'Timer', 'TaskStep']),
65
65
  };
66
66
  const CODE_REF = /^vfs:\/\/[^#\s]+#[A-Za-z_$][A-Za-z0-9_$]*$/;
67
+ const HALF_CODE_RESOURCE_ENVELOPE_VERSION = 'halfcode.resource-envelope/v1';
68
+ const DEPA_FLOW_SEMANTIC_API_VERSION = 'depa.flows/v1';
69
+ const DEPA_FLOW_RESOURCE_SPEC_VERSION = 1;
67
70
 
68
71
  interface XnlEl {
69
72
  kind: string;
@@ -75,6 +78,64 @@ interface XnlEl {
75
78
  extend?: { order?: string[]; children: Record<string, XnlEl> };
76
79
  }
77
80
 
81
+ interface FlowRootIdentityProjection {
82
+ apiVersion: string;
83
+ version: string;
84
+ diagnostic?: { code: string; message: string };
85
+ }
86
+
87
+ function projectFlowRootIdentity(metadata: Readonly<Record<string, unknown>>): FlowRootIdentityProjection {
88
+ const legacyApiVersion = typeof metadata.apiVersion === 'string' ? metadata.apiVersion : undefined;
89
+ const legacyVersion = typeof metadata.version === 'string' ? metadata.version : undefined;
90
+ const hasLegacyMetadata = metadata.apiVersion !== undefined || metadata.version !== undefined;
91
+ const hasResourceMetadata = metadata.envelopeVersion !== undefined || metadata.specVersion !== undefined;
92
+ if (hasLegacyMetadata && hasResourceMetadata) {
93
+ return {
94
+ apiVersion: '',
95
+ version: '',
96
+ diagnostic: {
97
+ code: 'mixed-flow-root-metadata',
98
+ message: 'flow root must use either depa apiVersion/version or Halfcode envelopeVersion/specVersion metadata',
99
+ },
100
+ };
101
+ }
102
+ if (hasResourceMetadata) {
103
+ if (metadata.envelopeVersion !== HALF_CODE_RESOURCE_ENVELOPE_VERSION) {
104
+ return {
105
+ apiVersion: '',
106
+ version: '',
107
+ diagnostic: {
108
+ code: 'unsupported-flow-resource-envelope',
109
+ message: `flow resource root requires envelopeVersion "${HALF_CODE_RESOURCE_ENVELOPE_VERSION}"`,
110
+ },
111
+ };
112
+ }
113
+ if (metadata.specVersion !== DEPA_FLOW_RESOURCE_SPEC_VERSION) {
114
+ return {
115
+ apiVersion: '',
116
+ version: '',
117
+ diagnostic: {
118
+ code: 'unsupported-flow-resource-spec-version',
119
+ message: `flow resource root requires numeric specVersion ${DEPA_FLOW_RESOURCE_SPEC_VERSION}`,
120
+ },
121
+ };
122
+ }
123
+ return {
124
+ apiVersion: DEPA_FLOW_SEMANTIC_API_VERSION,
125
+ version: String(DEPA_FLOW_RESOURCE_SPEC_VERSION),
126
+ };
127
+ }
128
+ if (legacyApiVersion && legacyVersion) return { apiVersion: legacyApiVersion, version: legacyVersion };
129
+ return {
130
+ apiVersion: legacyApiVersion ?? '',
131
+ version: legacyVersion ?? '',
132
+ diagnostic: {
133
+ code: 'container-missing-meta',
134
+ message: 'container missing metadata "apiVersion" or "version"',
135
+ },
136
+ };
137
+ }
138
+
78
139
  const isEl = (n: unknown): n is XnlEl =>
79
140
  !!n && typeof n === 'object' && ((n as XnlEl).kind === 'DataElement' || (n as XnlEl).kind === 'TextElement');
80
141
 
@@ -428,11 +489,13 @@ function loadCtrlFlowSources(
428
489
  : undefined;
429
490
  const stateSeed = facets['state.seed'] ? { ...(facets['state.seed'].attributes ?? {}) } : undefined;
430
491
  const taskSpace = facets['task.space'] ? toNodeSpec(facets['task.space']) : undefined;
492
+ const rootIdentity = projectFlowRootIdentity(container.metadata ?? {});
493
+ if (rootIdentity.diagnostic) note(rootIdentity.diagnostic.code, rootIdentity.diagnostic.message);
431
494
  const spec: FlowBundleSpec = {
432
495
  form,
433
496
  fqn,
434
- apiVersion: String(container.metadata?.apiVersion ?? ''),
435
- version: String(container.metadata?.version ?? ''),
497
+ apiVersion: rootIdentity.apiVersion,
498
+ version: rootIdentity.version,
436
499
  title: container.attributes?.title as string | undefined,
437
500
  sourceNames,
438
501
  baseUri: options.baseUri,
@@ -447,7 +510,6 @@ function loadCtrlFlowSources(
447
510
  taskSpace,
448
511
  diagnostics,
449
512
  };
450
- if (!spec.apiVersion) note('container-missing-meta', 'container missing metadata "apiVersion"');
451
513
  if (!fqn.includes('.')) note('container-id-not-fqn', `container #id must be a dotted FQN, got "${fqn}"`);
452
514
  return { spec, diagnostics };
453
515
  }