@ankhorage/runtime 5.1.2 → 5.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.
@@ -1,11 +1,17 @@
1
1
  import { isRecord } from '@ankhorage/utility/object';
2
- /*** Routes API and state capabilities before delegating application-owned capabilities. */
2
+ /*** Keeps local state mutations while preferring the configured host executor for API capabilities. */
3
3
  export function composeRuntimeCapabilityExecutor(composition) {
4
4
  return async (args) => args.capability.startsWith('api.')
5
- ? composition.apiCapabilityExecutor(args)
5
+ ? executeApiCapabilityAsync(args, composition)
6
6
  : args.capability === 'state.set'
7
7
  ? executeStateSetCapabilityAsync(args, composition.stateAdapter)
8
- : executeApplicationCapabilityAsync(args, composition.capabilityExecutor);
8
+ : args.capability === 'state.update'
9
+ ? executeStateUpdateCapabilityAsync(args, composition.stateAdapter)
10
+ : executeApplicationCapabilityAsync(args, composition.capabilityExecutor);
11
+ }
12
+ /*** Uses the host API executor when supplied, falling back to the manifest executor. */
13
+ function executeApiCapabilityAsync(args, composition) {
14
+ return composition.capabilityExecutor?.(args) ?? composition.apiCapabilityExecutor(args);
9
15
  }
10
16
  /*** Applies one serializable state value through the configured application state adapter. */
11
17
  function executeStateSetCapabilityAsync(args, stateAdapter) {
@@ -33,6 +39,28 @@ function executeStateSetCapabilityAsync(args, stateAdapter) {
33
39
  return Promise.resolve(stateAdapterSetErrorResult(error));
34
40
  }
35
41
  }
42
+ /*** Applies a serializable atomic state update through the configured state adapter. */
43
+ function executeStateUpdateCapabilityAsync(args, stateAdapter) {
44
+ if (stateAdapter?.update === undefined)
45
+ return Promise.resolve(missingStateUpdateAdapterResult());
46
+ const input = resolveStateUpdateInput(args.input);
47
+ if (input === undefined)
48
+ return Promise.resolve(invalidStateUpdateInputResult());
49
+ try {
50
+ const result = stateAdapter.update(input.path, input.update);
51
+ return Promise.resolve(result.ok
52
+ ? { data: { path: input.path, value: result.data }, ok: true }
53
+ : {
54
+ diagnostics: [
55
+ { code: result.error.code, message: result.error.message, severity: 'error' },
56
+ ],
57
+ ok: false,
58
+ });
59
+ }
60
+ catch (error) {
61
+ return Promise.resolve(stateAdapterUpdateErrorResult(error));
62
+ }
63
+ }
36
64
  async function executeApplicationCapabilityAsync(args, capabilityExecutor) {
37
65
  if (capabilityExecutor !== undefined)
38
66
  return capabilityExecutor(args);
@@ -55,6 +83,19 @@ function resolveStateSetInput(input) {
55
83
  return undefined;
56
84
  return { path, value };
57
85
  }
86
+ /*** Validates a portable append update before it reaches the StateAdapter boundary. */
87
+ function resolveStateUpdateInput(input) {
88
+ if (!isRecord(input))
89
+ return undefined;
90
+ const { path, update } = input;
91
+ if (!isStatePath(path) || !isStateUpdate(update))
92
+ return undefined;
93
+ return { path, update };
94
+ }
95
+ /*** Narrows unknown manifest input to the released serializable append descriptor. */
96
+ function isStateUpdate(value) {
97
+ return isRecord(value) && value.kind === 'append' && isStateValue(value.value);
98
+ }
58
99
  function isStatePath(value) {
59
100
  return ((typeof value === 'string' && value.trim().length > 0) ||
60
101
  (Array.isArray(value) &&
@@ -83,6 +124,19 @@ function stateAdapterSetErrorResult(error) {
83
124
  ok: false,
84
125
  };
85
126
  }
127
+ /*** Converts thrown state-update adapter failures into public Runtime diagnostics. */
128
+ function stateAdapterUpdateErrorResult(error) {
129
+ return {
130
+ diagnostics: [
131
+ {
132
+ code: 'state-adapter-error',
133
+ message: error instanceof Error ? error.message : 'StateAdapter.update failed.',
134
+ severity: 'error',
135
+ },
136
+ ],
137
+ ok: false,
138
+ };
139
+ }
86
140
  function missingStateAdapterResult() {
87
141
  return {
88
142
  diagnostics: [
@@ -107,4 +161,30 @@ function invalidStateSetInputResult() {
107
161
  ok: false,
108
162
  };
109
163
  }
164
+ /*** Reports an unavailable atomic-update adapter without delegating application execution. */
165
+ function missingStateUpdateAdapterResult() {
166
+ return {
167
+ diagnostics: [
168
+ {
169
+ code: 'missing-adapter',
170
+ message: "Capability 'state.update' requires a configured StateAdapter with update support.",
171
+ severity: 'error',
172
+ },
173
+ ],
174
+ ok: false,
175
+ };
176
+ }
177
+ /*** Reports malformed state-update descriptors at the Runtime capability boundary. */
178
+ function invalidStateUpdateInputResult() {
179
+ return {
180
+ diagnostics: [
181
+ {
182
+ code: 'invalid-input',
183
+ message: "Capability 'state.update' requires a non-empty path and serializable append update.",
184
+ severity: 'error',
185
+ },
186
+ ],
187
+ ok: false,
188
+ };
189
+ }
110
190
  //# sourceMappingURL=runtimeCapabilityExecutor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeCapabilityExecutor.js","sourceRoot":"","sources":["../src/runtimeCapabilityExecutor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StateAdapter, StatePath, StateValue } from '@ankhorage/contracts';\nimport { isRecord } from '@ankhorage/utility/object';\n\nimport type {\n RuntimeCapabilityExecutionArgs,\n RuntimeCapabilityExecutionResult,\n RuntimeCapabilityExecutor,\n} from './runtimeBindings';\n\nexport interface RuntimeCapabilityExecutorComposition {\n readonly apiCapabilityExecutor: RuntimeCapabilityExecutor;\n readonly capabilityExecutor?: RuntimeCapabilityExecutor;\n readonly stateAdapter?: StateAdapter;\n}\n\n/*** Routes API and state capabilities before delegating application-owned capabilities. */\nexport function composeRuntimeCapabilityExecutor(\n composition: RuntimeCapabilityExecutorComposition,\n): RuntimeCapabilityExecutor {\n return async (args) =>\n args.capability.startsWith('api.')\n ? composition.apiCapabilityExecutor(args)\n : args.capability === 'state.set'\n ? executeStateSetCapabilityAsync(args, composition.stateAdapter)\n : executeApplicationCapabilityAsync(args, composition.capabilityExecutor);\n}\n\n/*** Applies one serializable state value through the configured application state adapter. */\nfunction executeStateSetCapabilityAsync(\n args: RuntimeCapabilityExecutionArgs,\n stateAdapter: StateAdapter | undefined,\n): Promise<RuntimeCapabilityExecutionResult> {\n if (stateAdapter === undefined) return Promise.resolve(missingStateAdapterResult());\n\n const input = resolveStateSetInput(args.input);\n if (input === undefined) return Promise.resolve(invalidStateSetInputResult());\n\n try {\n const result = stateAdapter.set(input.path, input.value);\n return Promise.resolve(\n result.ok\n ? { data: { path: input.path, value: input.value }, ok: true }\n : {\n diagnostics: [\n {\n code: result.error.code,\n message: result.error.message,\n severity: 'error',\n },\n ],\n ok: false,\n },\n );\n } catch (error) {\n return Promise.resolve(stateAdapterSetErrorResult(error));\n }\n}\n\nasync function executeApplicationCapabilityAsync(\n args: RuntimeCapabilityExecutionArgs,\n capabilityExecutor: RuntimeCapabilityExecutor | undefined,\n): Promise<RuntimeCapabilityExecutionResult> {\n if (capabilityExecutor !== undefined) return capabilityExecutor(args);\n return {\n diagnostics: [\n {\n code: 'missing-operation',\n message: `Capability '${args.capability}' has no configured executor.`,\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\nfunction resolveStateSetInput(\n input: RuntimeCapabilityExecutionArgs['input'],\n): { readonly path: StatePath; readonly value: StateValue } | undefined {\n if (!isRecord(input) || !('value' in input)) return undefined;\n const { path, value } = input;\n if (!isStatePath(path) || !isStateValue(value)) return undefined;\n return { path, value };\n}\n\nfunction isStatePath(value: unknown): value is StatePath {\n return (\n (typeof value === 'string' && value.trim().length > 0) ||\n (Array.isArray(value) &&\n value.length > 0 &&\n value.every((part) => typeof part === 'string' && part.trim().length > 0))\n );\n}\n\nfunction isStateValue(value: unknown): value is StateValue {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (Array.isArray(value)) return value.every(isStateValue);\n return isRecord(value) && Object.values(value).every(isStateValue);\n}\n\n/*** Converts thrown adapter failures into capability diagnostics at the public Runtime boundary. */\nfunction stateAdapterSetErrorResult(error: unknown): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'state-adapter-error',\n message: error instanceof Error ? error.message : 'StateAdapter.set failed.',\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\nfunction missingStateAdapterResult(): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'missing-adapter',\n message: \"Capability 'state.set' requires a configured StateAdapter.\",\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\nfunction invalidStateSetInputResult(): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'invalid-input',\n message: \"Capability 'state.set' requires a non-empty path and serializable value.\",\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n"]}
1
+ {"version":3,"file":"runtimeCapabilityExecutor.js","sourceRoot":"","sources":["../src/runtimeCapabilityExecutor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StateAdapter, StatePath, StateUpdate, StateValue } from '@ankhorage/contracts';\nimport { isRecord } from '@ankhorage/utility/object';\n\nimport type {\n RuntimeCapabilityExecutionArgs,\n RuntimeCapabilityExecutionResult,\n RuntimeCapabilityExecutor,\n} from './runtimeBindings';\n\nexport interface RuntimeCapabilityExecutorComposition {\n readonly apiCapabilityExecutor: RuntimeCapabilityExecutor;\n readonly capabilityExecutor?: RuntimeCapabilityExecutor;\n readonly stateAdapter?: StateAdapter;\n}\n\n/*** Keeps local state mutations while preferring the configured host executor for API capabilities. */\nexport function composeRuntimeCapabilityExecutor(\n composition: RuntimeCapabilityExecutorComposition,\n): RuntimeCapabilityExecutor {\n return async (args) =>\n args.capability.startsWith('api.')\n ? executeApiCapabilityAsync(args, composition)\n : args.capability === 'state.set'\n ? executeStateSetCapabilityAsync(args, composition.stateAdapter)\n : args.capability === 'state.update'\n ? executeStateUpdateCapabilityAsync(args, composition.stateAdapter)\n : executeApplicationCapabilityAsync(args, composition.capabilityExecutor);\n}\n\n/*** Uses the host API executor when supplied, falling back to the manifest executor. */\nfunction executeApiCapabilityAsync(\n args: RuntimeCapabilityExecutionArgs,\n composition: RuntimeCapabilityExecutorComposition,\n): Promise<RuntimeCapabilityExecutionResult> {\n return composition.capabilityExecutor?.(args) ?? composition.apiCapabilityExecutor(args);\n}\n\n/*** Applies one serializable state value through the configured application state adapter. */\nfunction executeStateSetCapabilityAsync(\n args: RuntimeCapabilityExecutionArgs,\n stateAdapter: StateAdapter | undefined,\n): Promise<RuntimeCapabilityExecutionResult> {\n if (stateAdapter === undefined) return Promise.resolve(missingStateAdapterResult());\n\n const input = resolveStateSetInput(args.input);\n if (input === undefined) return Promise.resolve(invalidStateSetInputResult());\n\n try {\n const result = stateAdapter.set(input.path, input.value);\n return Promise.resolve(\n result.ok\n ? { data: { path: input.path, value: input.value }, ok: true }\n : {\n diagnostics: [\n {\n code: result.error.code,\n message: result.error.message,\n severity: 'error',\n },\n ],\n ok: false,\n },\n );\n } catch (error) {\n return Promise.resolve(stateAdapterSetErrorResult(error));\n }\n}\n\n/*** Applies a serializable atomic state update through the configured state adapter. */\nfunction executeStateUpdateCapabilityAsync(\n args: RuntimeCapabilityExecutionArgs,\n stateAdapter: StateAdapter | undefined,\n): Promise<RuntimeCapabilityExecutionResult> {\n if (stateAdapter?.update === undefined) return Promise.resolve(missingStateUpdateAdapterResult());\n\n const input = resolveStateUpdateInput(args.input);\n if (input === undefined) return Promise.resolve(invalidStateUpdateInputResult());\n\n try {\n const result = stateAdapter.update(input.path, input.update);\n return Promise.resolve(\n result.ok\n ? { data: { path: input.path, value: result.data }, ok: true }\n : {\n diagnostics: [\n { code: result.error.code, message: result.error.message, severity: 'error' },\n ],\n ok: false,\n },\n );\n } catch (error) {\n return Promise.resolve(stateAdapterUpdateErrorResult(error));\n }\n}\n\nasync function executeApplicationCapabilityAsync(\n args: RuntimeCapabilityExecutionArgs,\n capabilityExecutor: RuntimeCapabilityExecutor | undefined,\n): Promise<RuntimeCapabilityExecutionResult> {\n if (capabilityExecutor !== undefined) return capabilityExecutor(args);\n return {\n diagnostics: [\n {\n code: 'missing-operation',\n message: `Capability '${args.capability}' has no configured executor.`,\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\nfunction resolveStateSetInput(\n input: RuntimeCapabilityExecutionArgs['input'],\n): { readonly path: StatePath; readonly value: StateValue } | undefined {\n if (!isRecord(input) || !('value' in input)) return undefined;\n const { path, value } = input;\n if (!isStatePath(path) || !isStateValue(value)) return undefined;\n return { path, value };\n}\n\n/*** Validates a portable append update before it reaches the StateAdapter boundary. */\nfunction resolveStateUpdateInput(\n input: RuntimeCapabilityExecutionArgs['input'],\n): { readonly path: StatePath; readonly update: StateUpdate } | undefined {\n if (!isRecord(input)) return undefined;\n const { path, update } = input;\n if (!isStatePath(path) || !isStateUpdate(update)) return undefined;\n return { path, update };\n}\n\n/*** Narrows unknown manifest input to the released serializable append descriptor. */\nfunction isStateUpdate(value: unknown): value is StateUpdate {\n return isRecord(value) && value.kind === 'append' && isStateValue(value.value);\n}\n\nfunction isStatePath(value: unknown): value is StatePath {\n return (\n (typeof value === 'string' && value.trim().length > 0) ||\n (Array.isArray(value) &&\n value.length > 0 &&\n value.every((part) => typeof part === 'string' && part.trim().length > 0))\n );\n}\n\nfunction isStateValue(value: unknown): value is StateValue {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (Array.isArray(value)) return value.every(isStateValue);\n return isRecord(value) && Object.values(value).every(isStateValue);\n}\n\n/*** Converts thrown adapter failures into capability diagnostics at the public Runtime boundary. */\nfunction stateAdapterSetErrorResult(error: unknown): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'state-adapter-error',\n message: error instanceof Error ? error.message : 'StateAdapter.set failed.',\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\n/*** Converts thrown state-update adapter failures into public Runtime diagnostics. */\nfunction stateAdapterUpdateErrorResult(error: unknown): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'state-adapter-error',\n message: error instanceof Error ? error.message : 'StateAdapter.update failed.',\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\nfunction missingStateAdapterResult(): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'missing-adapter',\n message: \"Capability 'state.set' requires a configured StateAdapter.\",\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\nfunction invalidStateSetInputResult(): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'invalid-input',\n message: \"Capability 'state.set' requires a non-empty path and serializable value.\",\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\n/*** Reports an unavailable atomic-update adapter without delegating application execution. */\nfunction missingStateUpdateAdapterResult(): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'missing-adapter',\n message:\n \"Capability 'state.update' requires a configured StateAdapter with update support.\",\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n\n/*** Reports malformed state-update descriptors at the Runtime capability boundary. */\nfunction invalidStateUpdateInputResult(): RuntimeCapabilityExecutionResult {\n return {\n diagnostics: [\n {\n code: 'invalid-input',\n message:\n \"Capability 'state.update' requires a non-empty path and serializable append update.\",\n severity: 'error',\n },\n ],\n ok: false,\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { StateAdapter, StateValue } from '@ankhorage/contracts';
1
+ import { type StateAdapter, type StateValue } from '@ankhorage/contracts';
2
2
  export interface RuntimeMemoryStateAdapterOptions {
3
3
  readonly initialState?: Readonly<Record<string, StateValue>>;
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeStateAdapter.d.ts","sourceRoot":"","sources":["../src/runtimeStateAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAMZ,UAAU,EACX,MAAM,sBAAsB,CAAC;AAS9B,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;CAC9D;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,gCAAqC,GAC7C,YAAY,CAmFd"}
1
+ {"version":3,"file":"runtimeStateAdapter.d.ts","sourceRoot":"","sources":["../src/runtimeStateAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EAOjB,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAc9B,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;CAC9D;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,gCAAqC,GAC7C,YAAY,CAmHd"}
@@ -1,18 +1,35 @@
1
+ import { isSerializableValue, } from '@ankhorage/contracts';
1
2
  export function createRuntimeMemoryStateAdapter(options = {}) {
2
3
  let rootState = { ...(options.initialState ?? {}) };
3
4
  const listeners = new Map();
5
+ const notifications = [];
6
+ let isNotifying = false;
4
7
  const emit = (path) => {
5
8
  const pathResult = normalizeStatePath(path);
6
9
  if (!pathResult.ok)
7
10
  return;
8
- for (const entry of listeners.values()) {
9
- if (!areStatePathsRelated(pathResult.parts, entry.parts))
10
- continue;
11
- const value = readStatePath(rootState, entry.parts);
12
- for (const listener of entry.listeners) {
13
- listener(value);
11
+ notifications.push({ changedParts: pathResult.parts, state: rootState });
12
+ if (isNotifying)
13
+ return;
14
+ isNotifying = true;
15
+ try {
16
+ while (notifications.length > 0) {
17
+ const notification = notifications.shift();
18
+ if (notification === undefined)
19
+ continue;
20
+ for (const entry of [...listeners.values()]) {
21
+ if (!areStatePathsRelated(notification.changedParts, entry.parts))
22
+ continue;
23
+ const value = readStatePath(notification.state, entry.parts);
24
+ for (const listener of [...entry.listeners]) {
25
+ listener(value);
26
+ }
27
+ }
14
28
  }
15
29
  }
30
+ finally {
31
+ isNotifying = false;
32
+ }
16
33
  };
17
34
  return {
18
35
  capabilities: {
@@ -37,6 +54,20 @@ export function createRuntimeMemoryStateAdapter(options = {}) {
37
54
  emit(path);
38
55
  return { ok: true };
39
56
  },
57
+ update(path, update) {
58
+ const pathResult = normalizeStatePath(path);
59
+ if (!pathResult.ok)
60
+ return pathResult;
61
+ const updateResult = applyStateUpdate(readStatePath(rootState, pathResult.parts), update);
62
+ if (!updateResult.ok)
63
+ return updateResult;
64
+ const nextStateResult = setStatePath(rootState, pathResult.parts, updateResult.data);
65
+ if (!nextStateResult.ok)
66
+ return nextStateResult;
67
+ rootState = nextStateResult.data;
68
+ emit(path);
69
+ return createStateDataResult(updateResult.data);
70
+ },
40
71
  subscribe(path, listener) {
41
72
  const pathResult = normalizeStatePath(path);
42
73
  if (!pathResult.ok)
@@ -128,6 +159,17 @@ function setStatePath(source, parts, value) {
128
159
  [head]: childResult.data,
129
160
  });
130
161
  }
162
+ /*** Applies one supported atomic update without observing an intermediate state. */
163
+ function applyStateUpdate(value, update) {
164
+ if (value !== undefined && !isStateValueArray(value)) {
165
+ return createStateError('incompatible_value', 'Append updates require an existing array state value.');
166
+ }
167
+ return createStateDataResult([...(value ?? []), update.value]);
168
+ }
169
+ /*** Narrows a serializable value to an element-preserving state array. */
170
+ function isStateValueArray(value) {
171
+ return Array.isArray(value) && value.every(isSerializableValue);
172
+ }
131
173
  function deleteStatePath(source, parts) {
132
174
  const [head, ...tail] = parts;
133
175
  if (head === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeStateAdapter.js","sourceRoot":"","sources":["../src/runtimeStateAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n StateAdapter,\n StateAdapterError,\n StateListener,\n StatePath,\n StateResult,\n StateSubscription,\n StateValue,\n} from '@ankhorage/contracts';\n\ntype MutableStateRecord = Record<string, StateValue>;\ntype StateListenerSet = Set<(value: StateValue | undefined) => void>;\ninterface StateListenerEntry {\n readonly listeners: StateListenerSet;\n readonly parts: readonly string[];\n}\n\nexport interface RuntimeMemoryStateAdapterOptions {\n readonly initialState?: Readonly<Record<string, StateValue>>;\n}\n\nexport function createRuntimeMemoryStateAdapter(\n options: RuntimeMemoryStateAdapterOptions = {},\n): StateAdapter {\n let rootState: MutableStateRecord = { ...(options.initialState ?? {}) };\n const listeners = new Map<string, StateListenerEntry>();\n\n const emit = (path: StatePath) => {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return;\n\n for (const entry of listeners.values()) {\n if (!areStatePathsRelated(pathResult.parts, entry.parts)) continue;\n\n const value = readStatePath(rootState, entry.parts);\n for (const listener of entry.listeners) {\n listener(value);\n }\n }\n };\n\n return {\n capabilities: {\n subscriptions: true,\n computed: false,\n persistence: false,\n },\n get<TValue extends StateValue = StateValue>(path: StatePath): StateResult<TValue | undefined> {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n return createStateDataResult(\n readStatePath(rootState, pathResult.parts) as TValue | undefined,\n );\n },\n set<TValue extends StateValue = StateValue>(path: StatePath, value: TValue): StateResult {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const nextStateResult = setStatePath(rootState, pathResult.parts, value);\n if (!nextStateResult.ok) return nextStateResult;\n\n rootState = nextStateResult.data;\n emit(path);\n return { ok: true };\n },\n subscribe<TValue extends StateValue = StateValue>(\n path: StatePath,\n listener: StateListener<TValue>,\n ): StateResult<StateSubscription> {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const key = pathPartsToKey(pathResult.parts);\n const entry = listeners.get(key) ?? {\n listeners: new Set<(value: StateValue | undefined) => void>(),\n parts: pathResult.parts,\n };\n const wrappedListener = (value: StateValue | undefined) => {\n listener({ path, value: value as TValue | undefined });\n };\n\n entry.listeners.add(wrappedListener);\n listeners.set(key, entry);\n\n return createStateDataResult({\n unsubscribe() {\n entry.listeners.delete(wrappedListener);\n if (entry.listeners.size === 0) {\n listeners.delete(key);\n }\n },\n });\n },\n delete(path: StatePath): StateResult {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const nextStateResult = deleteStatePath(rootState, pathResult.parts);\n if (!nextStateResult.ok) return nextStateResult;\n\n rootState = nextStateResult.data;\n emit(path);\n return { ok: true };\n },\n };\n}\n\ninterface ResolvedStatePath {\n readonly ok: true;\n readonly parts: readonly string[];\n}\n\ninterface StatePathError {\n readonly ok: false;\n readonly error: StateAdapterError;\n}\n\ntype StatePathResult = ResolvedStatePath | StatePathError;\n\nfunction normalizeStatePath(path: StatePath): StatePathResult {\n const parts = typeof path === 'string' ? path.split('.') : [...path];\n const normalized = parts.map((part) => part.trim()).filter(Boolean);\n\n if (normalized.length === 0) {\n return createStateError('invalid_path', 'State path must contain at least one segment.');\n }\n\n return {\n ok: true,\n parts: normalized,\n };\n}\n\nfunction pathPartsToKey(parts: readonly string[]): string {\n return parts.join('.');\n}\n\n/*** Determines whether a state mutation changes an ancestor, descendant, or exact subscription. */\nfunction areStatePathsRelated(\n changedParts: readonly string[],\n subscribedParts: readonly string[],\n): boolean {\n const sharedLength = Math.min(changedParts.length, subscribedParts.length);\n return changedParts\n .slice(0, sharedLength)\n .every((part, index) => part === subscribedParts[index]);\n}\n\nfunction readStatePath(\n source: MutableStateRecord,\n parts: readonly string[],\n): StateValue | undefined {\n let currentValue: StateValue | undefined = source;\n\n for (const part of parts) {\n if (!isStateRecord(currentValue)) {\n return undefined;\n }\n\n currentValue = currentValue[part];\n }\n\n return currentValue;\n}\n\nfunction setStatePath(\n source: MutableStateRecord,\n parts: readonly string[],\n value: StateValue,\n): StateResult<MutableStateRecord> {\n const [head, ...tail] = parts;\n if (head === undefined) {\n return createStateError('invalid_path', 'State path must contain at least one segment.');\n }\n\n if (tail.length === 0) {\n return createStateDataResult({\n ...source,\n [head]: value,\n });\n }\n\n const existing = source[head];\n const child = existing === undefined ? {} : existing;\n if (!isStateRecord(child)) {\n return createStateError(\n 'path_conflict',\n `Cannot set nested state below non-object path segment \"${head}\".`,\n );\n }\n\n const childResult = setStatePath(child, tail, value);\n if (!childResult.ok) return childResult;\n\n return createStateDataResult({\n ...source,\n [head]: childResult.data,\n });\n}\n\nfunction deleteStatePath(\n source: MutableStateRecord,\n parts: readonly string[],\n): StateResult<MutableStateRecord> {\n const [head, ...tail] = parts;\n if (head === undefined) {\n return createStateError('invalid_path', 'State path must contain at least one segment.');\n }\n\n if (tail.length === 0) {\n const nextState: MutableStateRecord = {};\n for (const [key, value] of Object.entries(source)) {\n if (key !== head) {\n nextState[key] = value;\n }\n }\n\n return createStateDataResult(nextState);\n }\n\n const existing = source[head];\n if (existing === undefined) {\n return createStateDataResult(source);\n }\n\n if (!isStateRecord(existing)) {\n return createStateError(\n 'path_conflict',\n `Cannot delete nested state below non-object path segment \"${head}\".`,\n );\n }\n\n const childResult = deleteStatePath(existing, tail);\n if (!childResult.ok) return childResult;\n\n return createStateDataResult({\n ...source,\n [head]: childResult.data,\n });\n}\n\nfunction isStateRecord(value: StateValue | undefined): value is MutableStateRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction createStateError(code: string, message: string): StatePathError {\n const error: StateAdapterError = { code, message };\n return { ok: false, error };\n}\n\nfunction createStateDataResult<TValue>(data: TValue): StateResult<TValue> {\n const result = {\n ok: true,\n data,\n };\n\n return result as StateResult<TValue>;\n}\n"]}
1
+ {"version":3,"file":"runtimeStateAdapter.js","sourceRoot":"","sources":["../src/runtimeStateAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n isSerializableValue,\n type StateAdapter,\n type StateAdapterError,\n type StateListener,\n type StatePath,\n type StateResult,\n type StateSubscription,\n type StateUpdate,\n type StateValue,\n} from '@ankhorage/contracts';\n\ntype MutableStateRecord = Record<string, StateValue>;\ntype StateListenerSet = Set<(value: StateValue | undefined) => void>;\ninterface StateListenerEntry {\n readonly listeners: StateListenerSet;\n readonly parts: readonly string[];\n}\n\ninterface StateNotification {\n readonly changedParts: readonly string[];\n readonly state: MutableStateRecord;\n}\n\nexport interface RuntimeMemoryStateAdapterOptions {\n readonly initialState?: Readonly<Record<string, StateValue>>;\n}\n\nexport function createRuntimeMemoryStateAdapter(\n options: RuntimeMemoryStateAdapterOptions = {},\n): StateAdapter {\n let rootState: MutableStateRecord = { ...(options.initialState ?? {}) };\n const listeners = new Map<string, StateListenerEntry>();\n const notifications: StateNotification[] = [];\n let isNotifying = false;\n\n const emit = (path: StatePath) => {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return;\n\n notifications.push({ changedParts: pathResult.parts, state: rootState });\n if (isNotifying) return;\n\n isNotifying = true;\n try {\n while (notifications.length > 0) {\n const notification = notifications.shift();\n if (notification === undefined) continue;\n\n for (const entry of [...listeners.values()]) {\n if (!areStatePathsRelated(notification.changedParts, entry.parts)) continue;\n\n const value = readStatePath(notification.state, entry.parts);\n for (const listener of [...entry.listeners]) {\n listener(value);\n }\n }\n }\n } finally {\n isNotifying = false;\n }\n };\n\n return {\n capabilities: {\n subscriptions: true,\n computed: false,\n persistence: false,\n },\n get<TValue extends StateValue = StateValue>(path: StatePath): StateResult<TValue | undefined> {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n return createStateDataResult(\n readStatePath(rootState, pathResult.parts) as TValue | undefined,\n );\n },\n set<TValue extends StateValue = StateValue>(path: StatePath, value: TValue): StateResult {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const nextStateResult = setStatePath(rootState, pathResult.parts, value);\n if (!nextStateResult.ok) return nextStateResult;\n\n rootState = nextStateResult.data;\n emit(path);\n return { ok: true };\n },\n update<TValue extends StateValue = StateValue>(\n path: StatePath,\n update: StateUpdate,\n ): StateResult<TValue> {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const updateResult = applyStateUpdate(readStatePath(rootState, pathResult.parts), update);\n if (!updateResult.ok) return updateResult;\n\n const nextStateResult = setStatePath(rootState, pathResult.parts, updateResult.data);\n if (!nextStateResult.ok) return nextStateResult;\n\n rootState = nextStateResult.data;\n emit(path);\n return createStateDataResult(updateResult.data as TValue);\n },\n subscribe<TValue extends StateValue = StateValue>(\n path: StatePath,\n listener: StateListener<TValue>,\n ): StateResult<StateSubscription> {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const key = pathPartsToKey(pathResult.parts);\n const entry = listeners.get(key) ?? {\n listeners: new Set<(value: StateValue | undefined) => void>(),\n parts: pathResult.parts,\n };\n const wrappedListener = (value: StateValue | undefined) => {\n listener({ path, value: value as TValue | undefined });\n };\n\n entry.listeners.add(wrappedListener);\n listeners.set(key, entry);\n\n return createStateDataResult({\n unsubscribe() {\n entry.listeners.delete(wrappedListener);\n if (entry.listeners.size === 0) {\n listeners.delete(key);\n }\n },\n });\n },\n delete(path: StatePath): StateResult {\n const pathResult = normalizeStatePath(path);\n if (!pathResult.ok) return pathResult;\n\n const nextStateResult = deleteStatePath(rootState, pathResult.parts);\n if (!nextStateResult.ok) return nextStateResult;\n\n rootState = nextStateResult.data;\n emit(path);\n return { ok: true };\n },\n };\n}\n\ninterface ResolvedStatePath {\n readonly ok: true;\n readonly parts: readonly string[];\n}\n\ninterface StatePathError {\n readonly ok: false;\n readonly error: StateAdapterError;\n}\n\ntype StatePathResult = ResolvedStatePath | StatePathError;\n\nfunction normalizeStatePath(path: StatePath): StatePathResult {\n const parts = typeof path === 'string' ? path.split('.') : [...path];\n const normalized = parts.map((part) => part.trim()).filter(Boolean);\n\n if (normalized.length === 0) {\n return createStateError('invalid_path', 'State path must contain at least one segment.');\n }\n\n return {\n ok: true,\n parts: normalized,\n };\n}\n\nfunction pathPartsToKey(parts: readonly string[]): string {\n return parts.join('.');\n}\n\n/*** Determines whether a state mutation changes an ancestor, descendant, or exact subscription. */\nfunction areStatePathsRelated(\n changedParts: readonly string[],\n subscribedParts: readonly string[],\n): boolean {\n const sharedLength = Math.min(changedParts.length, subscribedParts.length);\n return changedParts\n .slice(0, sharedLength)\n .every((part, index) => part === subscribedParts[index]);\n}\n\nfunction readStatePath(\n source: MutableStateRecord,\n parts: readonly string[],\n): StateValue | undefined {\n let currentValue: StateValue | undefined = source;\n\n for (const part of parts) {\n if (!isStateRecord(currentValue)) {\n return undefined;\n }\n\n currentValue = currentValue[part];\n }\n\n return currentValue;\n}\n\nfunction setStatePath(\n source: MutableStateRecord,\n parts: readonly string[],\n value: StateValue,\n): StateResult<MutableStateRecord> {\n const [head, ...tail] = parts;\n if (head === undefined) {\n return createStateError('invalid_path', 'State path must contain at least one segment.');\n }\n\n if (tail.length === 0) {\n return createStateDataResult({\n ...source,\n [head]: value,\n });\n }\n\n const existing = source[head];\n const child = existing === undefined ? {} : existing;\n if (!isStateRecord(child)) {\n return createStateError(\n 'path_conflict',\n `Cannot set nested state below non-object path segment \"${head}\".`,\n );\n }\n\n const childResult = setStatePath(child, tail, value);\n if (!childResult.ok) return childResult;\n\n return createStateDataResult({\n ...source,\n [head]: childResult.data,\n });\n}\n\n/*** Applies one supported atomic update without observing an intermediate state. */\nfunction applyStateUpdate(\n value: StateValue | undefined,\n update: StateUpdate,\n): StateResult<StateValue> {\n if (value !== undefined && !isStateValueArray(value)) {\n return createStateError(\n 'incompatible_value',\n 'Append updates require an existing array state value.',\n );\n }\n\n return createStateDataResult([...(value ?? []), update.value]);\n}\n\n/*** Narrows a serializable value to an element-preserving state array. */\nfunction isStateValueArray(value: StateValue): value is readonly StateValue[] {\n return Array.isArray(value) && value.every(isSerializableValue);\n}\n\nfunction deleteStatePath(\n source: MutableStateRecord,\n parts: readonly string[],\n): StateResult<MutableStateRecord> {\n const [head, ...tail] = parts;\n if (head === undefined) {\n return createStateError('invalid_path', 'State path must contain at least one segment.');\n }\n\n if (tail.length === 0) {\n const nextState: MutableStateRecord = {};\n for (const [key, value] of Object.entries(source)) {\n if (key !== head) {\n nextState[key] = value;\n }\n }\n\n return createStateDataResult(nextState);\n }\n\n const existing = source[head];\n if (existing === undefined) {\n return createStateDataResult(source);\n }\n\n if (!isStateRecord(existing)) {\n return createStateError(\n 'path_conflict',\n `Cannot delete nested state below non-object path segment \"${head}\".`,\n );\n }\n\n const childResult = deleteStatePath(existing, tail);\n if (!childResult.ok) return childResult;\n\n return createStateDataResult({\n ...source,\n [head]: childResult.data,\n });\n}\n\nfunction isStateRecord(value: StateValue | undefined): value is MutableStateRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction createStateError(code: string, message: string): StatePathError {\n const error: StateAdapterError = { code, message };\n return { ok: false, error };\n}\n\nfunction createStateDataResult<TValue>(data: TValue): StateResult<TValue> {\n const result = {\n ok: true,\n data,\n };\n\n return result as StateResult<TValue>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/runtime",
3
- "version": "5.1.2",
3
+ "version": "5.2.0",
4
4
  "description": "Platform-neutral runtime renderer, contracts, and helpers for Ankhorage generated apps.",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -166,6 +166,88 @@
166
166
  }
167
167
  }
168
168
  },
169
+ {
170
+ "id": "state.update",
171
+ "owner": "@ankhorage/runtime",
172
+ "access": [
173
+ "invoke",
174
+ "write"
175
+ ],
176
+ "binding": {
177
+ "kind": "state",
178
+ "bindableAs": [
179
+ "target"
180
+ ]
181
+ },
182
+ "label": "Update runtime state",
183
+ "input": {
184
+ "schema": {
185
+ "type": "object",
186
+ "required": [
187
+ "path",
188
+ "update"
189
+ ],
190
+ "properties": {
191
+ "path": {
192
+ "anyOf": [
193
+ {
194
+ "type": "string"
195
+ },
196
+ {
197
+ "type": "array",
198
+ "items": {
199
+ "type": "string"
200
+ }
201
+ }
202
+ ]
203
+ },
204
+ "update": {
205
+ "type": "object",
206
+ "required": [
207
+ "kind",
208
+ "value"
209
+ ],
210
+ "properties": {
211
+ "kind": {
212
+ "type": "string",
213
+ "enum": [
214
+ "append"
215
+ ]
216
+ },
217
+ "value": {}
218
+ }
219
+ }
220
+ }
221
+ }
222
+ },
223
+ "output": {
224
+ "schema": {
225
+ "type": "object",
226
+ "required": [
227
+ "path",
228
+ "value"
229
+ ],
230
+ "properties": {
231
+ "path": {
232
+ "anyOf": [
233
+ {
234
+ "type": "string"
235
+ },
236
+ {
237
+ "type": "array",
238
+ "items": {
239
+ "type": "string"
240
+ }
241
+ }
242
+ ]
243
+ },
244
+ "value": {
245
+ "type": "array"
246
+ }
247
+ }
248
+ }
249
+ }
250
+ },
169
251
  {
170
252
  "id": "runtime.eventOperation",
171
253
  "owner": "@ankhorage/runtime",
@@ -261,7 +343,7 @@
261
343
  },
262
344
  "dependencies": {
263
345
  "@ankhorage/capability": "^0.2.2",
264
- "@ankhorage/contracts": "^26.0.2",
346
+ "@ankhorage/contracts": "^26.2.0",
265
347
  "@ankhorage/data-sources": "^2.1.3",
266
348
  "@ankhorage/utility": "^1.10.0"
267
349
  },
@@ -276,7 +358,7 @@
276
358
  }
277
359
  },
278
360
  "devDependencies": {
279
- "@ankhorage/devtools": "^2.3.23",
361
+ "@ankhorage/devtools": "^2.3.30",
280
362
  "@ankhorage/paradox": "^0.3.5",
281
363
  "@types/bun": "^1.4.2",
282
364
  "@types/node": "^26.6.4",
@@ -1,7 +1,7 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="90" height="20" role="img" aria-label="npm: v5.1.2">
2
- <title>npm: v5.1.2</title>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="90" height="20" role="img" aria-label="npm: v5.2.0">
2
+ <title>npm: v5.2.0</title>
3
3
  <rect width="38" height="20" fill="#374151"/>
4
4
  <rect x="38" width="52" height="20" fill="#cb3837"/>
5
5
  <text x="19" y="14" fill="#ffffff" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11" text-anchor="middle">npm</text>
6
- <text x="64" y="14" fill="#ffffff" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11" text-anchor="middle">v5.1.2</text>
6
+ <text x="64" y="14" fill="#ffffff" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11" text-anchor="middle">v5.2.0</text>
7
7
  </svg>
@@ -126,6 +126,12 @@ graph TD
126
126
  package__ankhorage_runtime -.-> module_src_runtimeRepeatDiagnostics_ts
127
127
  module_src_runtimeRepeatEmptyState_ts["src/runtimeRepeatEmptyState.ts"]
128
128
  package__ankhorage_runtime -.-> module_src_runtimeRepeatEmptyState_ts
129
+ module_src_RuntimeScreen_test_tsx["src/RuntimeScreen.test.tsx"]
130
+ package__ankhorage_runtime -.-> module_src_RuntimeScreen_test_tsx
131
+ module_src_RuntimeScreen_test_tsx --> module_src_registry_tsx
132
+ module_src_RuntimeScreen_test_tsx --> module_src_runtimeApiCapabilities_ts
133
+ module_src_RuntimeScreen_test_tsx --> module_src_runtimeBindings_ts
134
+ module_src_RuntimeScreen_test_tsx --> module_src_RuntimeRendererConfig_tsx
129
135
  module_src_RuntimeScreen_tsx["src/RuntimeScreen.tsx"]
130
136
  package__ankhorage_runtime -.-> module_src_RuntimeScreen_tsx
131
137
  module_src_RuntimeScreen_tsx --> module_src_ManifestContext_tsx
@@ -142,6 +148,9 @@ graph TD
142
148
  module_src_runtimeScreenLoaders_ts["src/runtimeScreenLoaders.ts"]
143
149
  package__ankhorage_runtime -.-> module_src_runtimeScreenLoaders_ts
144
150
  module_src_runtimeScreenLoaders_ts --> module_src_runtimeBindings_ts
151
+ module_src_runtimeStateAdapter_test_ts["src/runtimeStateAdapter.test.ts"]
152
+ package__ankhorage_runtime -.-> module_src_runtimeStateAdapter_test_ts
153
+ module_src_runtimeStateAdapter_test_ts --> module_src_runtimeStateAdapter_ts
145
154
  module_src_runtimeStateAdapter_ts["src/runtimeStateAdapter.ts"]
146
155
  package__ankhorage_runtime -.-> module_src_runtimeStateAdapter_ts
147
156
  module_src_runtimeStateReactivity_test_tsx["src/runtimeStateReactivity.test.tsx"]
@@ -35,9 +35,11 @@ graph LR
35
35
  module_src_runtimeRepeat_ts["src/runtimeRepeat.ts"]
36
36
  module_src_runtimeRepeatDiagnostics_ts["src/runtimeRepeatDiagnostics.ts"]
37
37
  module_src_runtimeRepeatEmptyState_ts["src/runtimeRepeatEmptyState.ts"]
38
+ module_src_RuntimeScreen_test_tsx["src/RuntimeScreen.test.tsx"]
38
39
  module_src_RuntimeScreen_tsx["src/RuntimeScreen.tsx"]
39
40
  module_src_runtimeScreenLoaders_test_ts["src/runtimeScreenLoaders.test.ts"]
40
41
  module_src_runtimeScreenLoaders_ts["src/runtimeScreenLoaders.ts"]
42
+ module_src_runtimeStateAdapter_test_ts["src/runtimeStateAdapter.test.ts"]
41
43
  module_src_runtimeStateAdapter_ts["src/runtimeStateAdapter.ts"]
42
44
  module_src_runtimeStateReactivity_test_tsx["src/runtimeStateReactivity.test.tsx"]
43
45
  module_src_useRuntimeMediaProps_ts["src/useRuntimeMediaProps.ts"]
@@ -35,9 +35,11 @@ graph LR
35
35
  module_src_runtimeRepeat_ts["src/runtimeRepeat.ts"]
36
36
  module_src_runtimeRepeatDiagnostics_ts["src/runtimeRepeatDiagnostics.ts"]
37
37
  module_src_runtimeRepeatEmptyState_ts["src/runtimeRepeatEmptyState.ts"]
38
+ module_src_RuntimeScreen_test_tsx["src/RuntimeScreen.test.tsx"]
38
39
  module_src_RuntimeScreen_tsx["src/RuntimeScreen.tsx"]
39
40
  module_src_runtimeScreenLoaders_test_ts["src/runtimeScreenLoaders.test.ts"]
40
41
  module_src_runtimeScreenLoaders_ts["src/runtimeScreenLoaders.ts"]
42
+ module_src_runtimeStateAdapter_test_ts["src/runtimeStateAdapter.test.ts"]
41
43
  module_src_runtimeStateAdapter_ts["src/runtimeStateAdapter.ts"]
42
44
  module_src_runtimeStateReactivity_test_tsx["src/runtimeStateReactivity.test.tsx"]
43
45
  module_src_useRuntimeMediaProps_ts["src/useRuntimeMediaProps.ts"]
@@ -98,6 +100,10 @@ graph LR
98
100
  module_src_RuntimeRendererConfig_tsx --> module_src_runtimeEventOperationLifecycle_ts
99
101
  module_src_RuntimeRendererConfig_tsx --> module_src_runtimeMedia_ts
100
102
  module_src_runtimeRepeat_ts --> module_src_runtimeBindings_ts
103
+ module_src_RuntimeScreen_test_tsx --> module_src_registry_tsx
104
+ module_src_RuntimeScreen_test_tsx --> module_src_runtimeApiCapabilities_ts
105
+ module_src_RuntimeScreen_test_tsx --> module_src_runtimeBindings_ts
106
+ module_src_RuntimeScreen_test_tsx --> module_src_RuntimeRendererConfig_tsx
101
107
  module_src_RuntimeScreen_tsx --> module_src_ManifestContext_tsx
102
108
  module_src_RuntimeScreen_tsx --> module_src_registry_tsx
103
109
  module_src_RuntimeScreen_tsx --> module_src_runtimeApiCapabilities_ts
@@ -108,6 +114,7 @@ graph LR
108
114
  module_src_RuntimeScreen_tsx --> module_src_runtimeStateAdapter_ts
109
115
  module_src_runtimeScreenLoaders_test_ts --> module_src_runtimeScreenLoaders_ts
110
116
  module_src_runtimeScreenLoaders_ts --> module_src_runtimeBindings_ts
117
+ module_src_runtimeStateAdapter_test_ts --> module_src_runtimeStateAdapter_ts
111
118
  module_src_runtimeStateReactivity_test_tsx --> module_src_registry_tsx
112
119
  module_src_runtimeStateReactivity_test_tsx --> module_src_runtimeBindings_ts
113
120
  module_src_runtimeStateReactivity_test_tsx --> module_src_runtimeStateAdapter_ts
@@ -1,7 +1,9 @@
1
1
  sequenceDiagram
2
2
  participant participant_createMissingCapabilityExecutorDiagnostic as createMissingCapabilityExecutorDiagnostic
3
+ participant participant_executeApi as executeApi
3
4
  participant participant_executeCapability as executeCapability
4
5
  participant participant_executeRuntimeBindingInvocation as executeRuntimeBindingInvocation
6
+ participant participant_navigate as navigate
5
7
  participant participant_resolveBindingInputMap as resolveBindingInputMap
6
8
  participant participant_resolveObjectAsync as resolveObjectAsync
7
9
  participant participant_writeResultSlot as writeResultSlot
@@ -12,6 +14,10 @@ sequenceDiagram
12
14
  participant_resolveObjectAsync-->>participant_resolveBindingInputMap: return
13
15
  participant_resolveBindingInputMap-->>participant_executeRuntimeBindingInvocation: return
14
16
  participant_executeRuntimeBindingInvocation->>participant_executeCapability: context.executeCapability()
17
+ participant_executeCapability->>participant_executeApi: executeApi()
18
+ participant_executeApi-->>participant_executeCapability: return
19
+ participant_executeCapability->>participant_navigate: navigate()
20
+ participant_navigate-->>participant_executeCapability: return
15
21
  participant_executeCapability-->>participant_executeRuntimeBindingInvocation: return
16
22
  participant_executeRuntimeBindingInvocation->>participant_writeResultSlot: context.writeResultSlot()
17
23
  participant_writeResultSlot->>participant_writeResultSlot: args.writeResultSlot()
@@ -66,7 +66,7 @@
66
66
  {
67
67
  "name": "composeRuntimeCapabilityExecutor",
68
68
  "title": null,
69
- "description": "Routes API and state capabilities before delegating application-owned capabilities.",
69
+ "description": "Keeps local state mutations while preferring the configured host executor for API capabilities.",
70
70
  "isReadme": false,
71
71
  "see": [],
72
72
  "security": [],
@@ -201,7 +201,7 @@
201
201
  "modulePath": "src/runtimeActionRegistry.ts",
202
202
  "sourceLocation": {
203
203
  "filePath": "src/runtimeActionRegistry.ts",
204
- "line": 179,
204
+ "line": 183,
205
205
  "column": 1
206
206
  },
207
207
  "exportPaths": [
@@ -472,7 +472,7 @@
472
472
  "modulePath": "src/runtimeStateAdapter.ts",
473
473
  "sourceLocation": {
474
474
  "filePath": "src/runtimeStateAdapter.ts",
475
- "line": 22,
475
+ "line": 29,
476
476
  "column": 1
477
477
  },
478
478
  "exportPaths": [
@@ -1012,7 +1012,7 @@
1012
1012
  "modulePath": "src/runtimeActionRegistry.ts",
1013
1013
  "sourceLocation": {
1014
1014
  "filePath": "src/runtimeActionRegistry.ts",
1015
- "line": 193,
1015
+ "line": 197,
1016
1016
  "column": 1
1017
1017
  },
1018
1018
  "exportPaths": [
@@ -2897,7 +2897,7 @@
2897
2897
  "modulePath": "src/runtimeStateAdapter.ts",
2898
2898
  "sourceLocation": {
2899
2899
  "filePath": "src/runtimeStateAdapter.ts",
2900
- "line": 18,
2900
+ "line": 25,
2901
2901
  "column": 1
2902
2902
  },
2903
2903
  "exportPaths": [
@@ -3804,7 +3804,7 @@
3804
3804
  "modulePath": "src/runtimeActionRegistry.ts",
3805
3805
  "sourceLocation": {
3806
3806
  "filePath": "src/runtimeActionRegistry.ts",
3807
- "line": 149,
3807
+ "line": 153,
3808
3808
  "column": 1
3809
3809
  },
3810
3810
  "exportPaths": [