@ankhorage/runtime 0.2.4 → 1.0.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/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/RuntimeScreen.d.ts.map +1 -1
- package/dist/RuntimeScreen.js +5 -43
- package/dist/RuntimeScreen.js.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +0 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +0 -1
- package/dist/registry.js.map +1 -1
- package/dist/runtimeDataSourceOperations.d.ts +3 -1
- package/dist/runtimeDataSourceOperations.d.ts.map +1 -1
- package/dist/runtimeDataSourceOperations.js +31 -26
- package/dist/runtimeDataSourceOperations.js.map +1 -1
- package/dist/runtimeDatabaseOperationExecutor.d.ts +9 -0
- package/dist/runtimeDatabaseOperationExecutor.d.ts.map +1 -0
- package/dist/runtimeDatabaseOperationExecutor.js +228 -0
- package/dist/runtimeDatabaseOperationExecutor.js.map +1 -0
- package/dist/runtimeScreenLoaders.d.ts +1 -2
- package/dist/runtimeScreenLoaders.d.ts.map +1 -1
- package/dist/runtimeScreenLoaders.js +1 -4
- package/dist/runtimeScreenLoaders.js.map +1 -1
- package/package.json +3 -3
- package/paradox/badges/npm.svg +3 -3
- package/paradox/components.md +1 -1
- package/paradox/diagrams/architecture-overview.mmd +5 -12
- package/paradox/diagrams/export-graph.mmd +1 -31
- package/paradox/diagrams/module-relationships.mmd +4 -9
- package/paradox/diagrams/sequences/runtime-screen.mmd +0 -3
- package/paradox/exports.json +30 -311
- package/paradox/exports.md +94 -199
- package/paradox/index.html +382 -744
- package/paradox/paradox.json +685 -928
- package/dist/appExtensionRegistry.d.ts +0 -3
- package/dist/appExtensionRegistry.d.ts.map +0 -1
- package/dist/appExtensionRegistry.js +0 -2
- package/dist/appExtensionRegistry.js.map +0 -1
- package/dist/runtimeApiLoaders.d.ts +0 -35
- package/dist/runtimeApiLoaders.d.ts.map +0 -1
- package/dist/runtimeApiLoaders.js +0 -218
- package/dist/runtimeApiLoaders.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"appExtensionRegistry.d.ts","sourceRoot":"","sources":["../src/appExtensionRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,eAAO,MAAM,gCAAgC,EAAE,iBAAsB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"appExtensionRegistry.js","sourceRoot":"","sources":["../src/appExtensionRegistry.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gCAAgC,GAAsB,EAAE,CAAC","sourcesContent":["import type { ComponentRegistry } from './componentRegistry';\n\nexport const APP_EXTENSION_COMPONENT_REGISTRY: ComponentRegistry = {};\n"]}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ApiScreenDataLoaderDefinition, AppDataManifest, StateAdapter, StateValue } from '@ankhorage/contracts';
|
|
2
|
-
export type RuntimeApiLoaderMode = 'byId' | 'list' | 'one' | 'random';
|
|
3
|
-
export type RuntimeApiLoaderDefinition = ApiScreenDataLoaderDefinition;
|
|
4
|
-
export interface RuntimeApiLoaderDiagnostic {
|
|
5
|
-
readonly code: 'invalid-record' | 'missing-api' | 'missing-target-path' | 'state-set-failed' | 'unsupported-loader';
|
|
6
|
-
readonly message: string;
|
|
7
|
-
readonly apiId?: string;
|
|
8
|
-
readonly targetPath?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface RuntimeApiLoaderExecutionResult {
|
|
11
|
-
readonly diagnostics: readonly RuntimeApiLoaderDiagnostic[];
|
|
12
|
-
}
|
|
13
|
-
export interface RuntimeApiLoaderMaterializationResult {
|
|
14
|
-
readonly state: Readonly<Record<string, StateValue>>;
|
|
15
|
-
readonly diagnostics: readonly RuntimeApiLoaderDiagnostic[];
|
|
16
|
-
}
|
|
17
|
-
export declare function materializeRuntimeApiLoaderState(args: {
|
|
18
|
-
readonly data?: AppDataManifest;
|
|
19
|
-
readonly loaders: readonly RuntimeApiLoaderDefinition[];
|
|
20
|
-
}): RuntimeApiLoaderMaterializationResult;
|
|
21
|
-
export declare function executeRuntimeApiLoaders(args: {
|
|
22
|
-
readonly data?: AppDataManifest;
|
|
23
|
-
readonly loaders: readonly RuntimeApiLoaderDefinition[];
|
|
24
|
-
readonly stateAdapter: StateAdapter;
|
|
25
|
-
}): RuntimeApiLoaderExecutionResult;
|
|
26
|
-
export declare function useRuntimeApiStateLoaders(args: {
|
|
27
|
-
readonly data?: AppDataManifest;
|
|
28
|
-
readonly loaders?: readonly RuntimeApiLoaderDefinition[];
|
|
29
|
-
readonly stateAdapter?: StateAdapter;
|
|
30
|
-
}): {
|
|
31
|
-
readonly diagnostics: readonly RuntimeApiLoaderDiagnostic[];
|
|
32
|
-
readonly stateAdapter: StateAdapter;
|
|
33
|
-
readonly stateVersion: number;
|
|
34
|
-
};
|
|
35
|
-
//# sourceMappingURL=runtimeApiLoaders.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeApiLoaders.d.ts","sourceRoot":"","sources":["../src/runtimeApiLoaders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAG7B,eAAe,EAEf,YAAY,EAEZ,UAAU,EACX,MAAM,sBAAsB,CAAC;AAK9B,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AAEvE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EACT,gBAAgB,GAChB,aAAa,GACb,qBAAqB,GACrB,kBAAkB,GAClB,oBAAoB,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,WAAW,EAAE,SAAS,0BAA0B,EAAE,CAAC;CAC7D;AAED,MAAM,WAAW,qCAAqC;IACpD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,SAAS,0BAA0B,EAAE,CAAC;CAC7D;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACrD,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,0BAA0B,EAAE,CAAC;CACzD,GAAG,qCAAqC,CAuDxC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACxD,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;CACrC,GAAG,+BAA+B,CAmBlC;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACzD,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;CACtC,GAAG;IACF,QAAQ,CAAC,WAAW,EAAE,SAAS,0BAA0B,EAAE,CAAC;IAC5D,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAsBA"}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { createRuntimeMemoryStateAdapter } from './runtimeStateAdapter';
|
|
3
|
-
export function materializeRuntimeApiLoaderState(args) {
|
|
4
|
-
const state = {};
|
|
5
|
-
const diagnostics = [];
|
|
6
|
-
for (const loader of args.loaders) {
|
|
7
|
-
const targetPath = loader.targetPath.trim();
|
|
8
|
-
if (targetPath.length === 0) {
|
|
9
|
-
diagnostics.push({
|
|
10
|
-
code: 'missing-target-path',
|
|
11
|
-
apiId: loader.apiId,
|
|
12
|
-
message: `API loader for ${loader.apiId} must define a target path.`,
|
|
13
|
-
});
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
const api = args.data?.apis?.[loader.apiId];
|
|
17
|
-
const resource = getGeneratedApiCollectionResource(api);
|
|
18
|
-
if (api?.kind !== 'generated' || resource === null) {
|
|
19
|
-
diagnostics.push({
|
|
20
|
-
code: 'missing-api',
|
|
21
|
-
apiId: loader.apiId,
|
|
22
|
-
targetPath,
|
|
23
|
-
message: `Generated API ${loader.apiId} could not be found.`,
|
|
24
|
-
});
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const selectedValue = selectApiLoaderValue({ api, resource, loader, diagnostics, targetPath });
|
|
28
|
-
if (selectedValue === undefined) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const stateValue = toStateValue(selectedValue);
|
|
32
|
-
if (stateValue === undefined) {
|
|
33
|
-
diagnostics.push({
|
|
34
|
-
code: 'invalid-record',
|
|
35
|
-
apiId: loader.apiId,
|
|
36
|
-
targetPath,
|
|
37
|
-
message: `Generated API ${loader.apiId} produced a value that cannot be represented as runtime state.`,
|
|
38
|
-
});
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const setResult = writeObjectPath(state, targetPath, stateValue);
|
|
42
|
-
if (!setResult.ok) {
|
|
43
|
-
diagnostics.push({
|
|
44
|
-
code: 'state-set-failed',
|
|
45
|
-
apiId: loader.apiId,
|
|
46
|
-
targetPath,
|
|
47
|
-
message: setResult.error.message,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return { state, diagnostics };
|
|
52
|
-
}
|
|
53
|
-
export function executeRuntimeApiLoaders(args) {
|
|
54
|
-
const materialized = materializeRuntimeApiLoaderState({
|
|
55
|
-
data: args.data,
|
|
56
|
-
loaders: args.loaders,
|
|
57
|
-
});
|
|
58
|
-
const diagnostics = [...materialized.diagnostics];
|
|
59
|
-
for (const [targetPath, value] of flattenStateEntries(materialized.state)) {
|
|
60
|
-
const result = args.stateAdapter.set(targetPath, value);
|
|
61
|
-
if (!result.ok) {
|
|
62
|
-
diagnostics.push({
|
|
63
|
-
code: 'state-set-failed',
|
|
64
|
-
targetPath,
|
|
65
|
-
message: result.error.message,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return { diagnostics };
|
|
70
|
-
}
|
|
71
|
-
export function useRuntimeApiStateLoaders(args) {
|
|
72
|
-
const fallbackStateAdapter = React.useMemo(() => createRuntimeMemoryStateAdapter(), []);
|
|
73
|
-
const stateAdapter = args.stateAdapter ?? fallbackStateAdapter;
|
|
74
|
-
const loaders = args.loaders ?? [];
|
|
75
|
-
const [stateVersion, setStateVersion] = React.useState(0);
|
|
76
|
-
const [diagnostics, setDiagnostics] = React.useState([]);
|
|
77
|
-
React.useEffect(() => {
|
|
78
|
-
const result = executeRuntimeApiLoaders({
|
|
79
|
-
data: args.data,
|
|
80
|
-
loaders,
|
|
81
|
-
stateAdapter,
|
|
82
|
-
});
|
|
83
|
-
setDiagnostics(result.diagnostics);
|
|
84
|
-
setStateVersion((current) => current + 1);
|
|
85
|
-
}, [args.data, loaders, stateAdapter]);
|
|
86
|
-
return {
|
|
87
|
-
diagnostics,
|
|
88
|
-
stateAdapter,
|
|
89
|
-
stateVersion,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function getGeneratedApiCollectionResource(api) {
|
|
93
|
-
if (api?.kind !== 'generated' || api.resource?.kind !== 'collection') {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
return api.resource;
|
|
97
|
-
}
|
|
98
|
-
function selectApiLoaderValue(args) {
|
|
99
|
-
const seed = args.resource.seed ?? [];
|
|
100
|
-
const primaryKey = args.resource.collection.primaryKey ?? 'id';
|
|
101
|
-
switch (args.loader.mode) {
|
|
102
|
-
case 'list':
|
|
103
|
-
return seed;
|
|
104
|
-
case 'one':
|
|
105
|
-
return seed[0];
|
|
106
|
-
case 'random': {
|
|
107
|
-
const index = deterministicIndex(args.api.id, seed.length);
|
|
108
|
-
return index === undefined ? undefined : seed[index];
|
|
109
|
-
}
|
|
110
|
-
case 'byId': {
|
|
111
|
-
const loaderId = normalizePrimitiveId(args.loader.id);
|
|
112
|
-
if (loaderId === undefined)
|
|
113
|
-
return undefined;
|
|
114
|
-
return seed.find((record) => {
|
|
115
|
-
return normalizePrimitiveId(record[primaryKey]) === loaderId;
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
default:
|
|
119
|
-
args.diagnostics.push({
|
|
120
|
-
code: 'unsupported-loader',
|
|
121
|
-
apiId: args.loader.apiId,
|
|
122
|
-
targetPath: args.targetPath,
|
|
123
|
-
message: `API loader mode ${String(args.loader.mode)} is not supported.`,
|
|
124
|
-
});
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function normalizePrimitiveId(value) {
|
|
129
|
-
if (typeof value === 'string')
|
|
130
|
-
return value;
|
|
131
|
-
if (typeof value === 'number')
|
|
132
|
-
return Number.isFinite(value) ? value.toString() : undefined;
|
|
133
|
-
if (typeof value === 'boolean')
|
|
134
|
-
return value ? 'true' : 'false';
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
function deterministicIndex(apiId, length) {
|
|
138
|
-
if (length === 0)
|
|
139
|
-
return undefined;
|
|
140
|
-
let hash = 0;
|
|
141
|
-
for (const char of apiId) {
|
|
142
|
-
hash = (hash * 31 + char.charCodeAt(0)) % length;
|
|
143
|
-
}
|
|
144
|
-
return hash;
|
|
145
|
-
}
|
|
146
|
-
function writeObjectPath(target, path, value) {
|
|
147
|
-
const parts = path
|
|
148
|
-
.split('.')
|
|
149
|
-
.map((part) => part.trim())
|
|
150
|
-
.filter(Boolean);
|
|
151
|
-
const [head, ...tail] = parts;
|
|
152
|
-
if (head === undefined) {
|
|
153
|
-
return {
|
|
154
|
-
ok: false,
|
|
155
|
-
error: {
|
|
156
|
-
code: 'invalid_path',
|
|
157
|
-
message: 'State target path must contain at least one segment.',
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
if (tail.length === 0) {
|
|
162
|
-
target[head] = value;
|
|
163
|
-
return { ok: true };
|
|
164
|
-
}
|
|
165
|
-
const existing = target[head];
|
|
166
|
-
const child = existing === undefined ? {} : existing;
|
|
167
|
-
if (!isMutableStateRecord(child)) {
|
|
168
|
-
return {
|
|
169
|
-
ok: false,
|
|
170
|
-
error: {
|
|
171
|
-
code: 'path_conflict',
|
|
172
|
-
message: `Cannot set nested state below non-object path segment "${head}".`,
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
target[head] = child;
|
|
177
|
-
return writeObjectPath(child, tail.join('.'), value);
|
|
178
|
-
}
|
|
179
|
-
function flattenStateEntries(source, prefix = '') {
|
|
180
|
-
return Object.entries(source).flatMap(([key, value]) => {
|
|
181
|
-
const path = prefix.length > 0 ? `${prefix}.${key}` : key;
|
|
182
|
-
return [[path, value]];
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
function toStateValue(value) {
|
|
186
|
-
if (value === null)
|
|
187
|
-
return null;
|
|
188
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
189
|
-
return value;
|
|
190
|
-
}
|
|
191
|
-
if (Array.isArray(value)) {
|
|
192
|
-
const items = [];
|
|
193
|
-
for (const item of value) {
|
|
194
|
-
const stateItem = toStateValue(item);
|
|
195
|
-
if (stateItem === undefined)
|
|
196
|
-
return undefined;
|
|
197
|
-
items.push(stateItem);
|
|
198
|
-
}
|
|
199
|
-
return items;
|
|
200
|
-
}
|
|
201
|
-
if (!isUnknownRecord(value))
|
|
202
|
-
return undefined;
|
|
203
|
-
const record = {};
|
|
204
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
205
|
-
const stateEntry = toStateValue(entry);
|
|
206
|
-
if (stateEntry === undefined)
|
|
207
|
-
return undefined;
|
|
208
|
-
record[key] = stateEntry;
|
|
209
|
-
}
|
|
210
|
-
return record;
|
|
211
|
-
}
|
|
212
|
-
function isUnknownRecord(value) {
|
|
213
|
-
return typeof value === 'object' && value != null && !Array.isArray(value);
|
|
214
|
-
}
|
|
215
|
-
function isMutableStateRecord(value) {
|
|
216
|
-
return typeof value === 'object' && value != null && !Array.isArray(value);
|
|
217
|
-
}
|
|
218
|
-
//# sourceMappingURL=runtimeApiLoaders.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeApiLoaders.js","sourceRoot":"","sources":["../src/runtimeApiLoaders.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ApiScreenDataLoaderDefinition,\n AppApiCollectionResourceDefinition,\n AppApiDefinition,\n AppDataManifest,\n AppGeneratedApiDefinition,\n StateAdapter,\n StateResult,\n StateValue,\n} from '@ankhorage/contracts';\nimport React from 'react';\n\nimport { createRuntimeMemoryStateAdapter } from './runtimeStateAdapter';\n\nexport type RuntimeApiLoaderMode = 'byId' | 'list' | 'one' | 'random';\n\nexport type RuntimeApiLoaderDefinition = ApiScreenDataLoaderDefinition;\n\nexport interface RuntimeApiLoaderDiagnostic {\n readonly code:\n | 'invalid-record'\n | 'missing-api'\n | 'missing-target-path'\n | 'state-set-failed'\n | 'unsupported-loader';\n readonly message: string;\n readonly apiId?: string;\n readonly targetPath?: string;\n}\n\nexport interface RuntimeApiLoaderExecutionResult {\n readonly diagnostics: readonly RuntimeApiLoaderDiagnostic[];\n}\n\nexport interface RuntimeApiLoaderMaterializationResult {\n readonly state: Readonly<Record<string, StateValue>>;\n readonly diagnostics: readonly RuntimeApiLoaderDiagnostic[];\n}\n\nexport function materializeRuntimeApiLoaderState(args: {\n readonly data?: AppDataManifest;\n readonly loaders: readonly RuntimeApiLoaderDefinition[];\n}): RuntimeApiLoaderMaterializationResult {\n const state: Record<string, StateValue> = {};\n const diagnostics: RuntimeApiLoaderDiagnostic[] = [];\n\n for (const loader of args.loaders) {\n const targetPath = loader.targetPath.trim();\n if (targetPath.length === 0) {\n diagnostics.push({\n code: 'missing-target-path',\n apiId: loader.apiId,\n message: `API loader for ${loader.apiId} must define a target path.`,\n });\n continue;\n }\n\n const api = args.data?.apis?.[loader.apiId];\n const resource = getGeneratedApiCollectionResource(api);\n if (api?.kind !== 'generated' || resource === null) {\n diagnostics.push({\n code: 'missing-api',\n apiId: loader.apiId,\n targetPath,\n message: `Generated API ${loader.apiId} could not be found.`,\n });\n continue;\n }\n\n const selectedValue = selectApiLoaderValue({ api, resource, loader, diagnostics, targetPath });\n if (selectedValue === undefined) {\n continue;\n }\n\n const stateValue = toStateValue(selectedValue);\n if (stateValue === undefined) {\n diagnostics.push({\n code: 'invalid-record',\n apiId: loader.apiId,\n targetPath,\n message: `Generated API ${loader.apiId} produced a value that cannot be represented as runtime state.`,\n });\n continue;\n }\n\n const setResult = writeObjectPath(state, targetPath, stateValue);\n if (!setResult.ok) {\n diagnostics.push({\n code: 'state-set-failed',\n apiId: loader.apiId,\n targetPath,\n message: setResult.error.message,\n });\n }\n }\n\n return { state, diagnostics };\n}\n\nexport function executeRuntimeApiLoaders(args: {\n readonly data?: AppDataManifest;\n readonly loaders: readonly RuntimeApiLoaderDefinition[];\n readonly stateAdapter: StateAdapter;\n}): RuntimeApiLoaderExecutionResult {\n const materialized = materializeRuntimeApiLoaderState({\n data: args.data,\n loaders: args.loaders,\n });\n const diagnostics: RuntimeApiLoaderDiagnostic[] = [...materialized.diagnostics];\n\n for (const [targetPath, value] of flattenStateEntries(materialized.state)) {\n const result = args.stateAdapter.set(targetPath, value);\n if (!result.ok) {\n diagnostics.push({\n code: 'state-set-failed',\n targetPath,\n message: result.error.message,\n });\n }\n }\n\n return { diagnostics };\n}\n\nexport function useRuntimeApiStateLoaders(args: {\n readonly data?: AppDataManifest;\n readonly loaders?: readonly RuntimeApiLoaderDefinition[];\n readonly stateAdapter?: StateAdapter;\n}): {\n readonly diagnostics: readonly RuntimeApiLoaderDiagnostic[];\n readonly stateAdapter: StateAdapter;\n readonly stateVersion: number;\n} {\n const fallbackStateAdapter = React.useMemo(() => createRuntimeMemoryStateAdapter(), []);\n const stateAdapter = args.stateAdapter ?? fallbackStateAdapter;\n const loaders = args.loaders ?? [];\n const [stateVersion, setStateVersion] = React.useState(0);\n const [diagnostics, setDiagnostics] = React.useState<readonly RuntimeApiLoaderDiagnostic[]>([]);\n\n React.useEffect(() => {\n const result = executeRuntimeApiLoaders({\n data: args.data,\n loaders,\n stateAdapter,\n });\n setDiagnostics(result.diagnostics);\n setStateVersion((current) => current + 1);\n }, [args.data, loaders, stateAdapter]);\n\n return {\n diagnostics,\n stateAdapter,\n stateVersion,\n };\n}\n\nfunction getGeneratedApiCollectionResource(\n api: AppApiDefinition | undefined,\n): AppApiCollectionResourceDefinition | null {\n if (api?.kind !== 'generated' || api.resource?.kind !== 'collection') {\n return null;\n }\n\n return api.resource;\n}\n\nfunction selectApiLoaderValue(args: {\n readonly api: AppGeneratedApiDefinition;\n readonly resource: AppApiCollectionResourceDefinition;\n readonly loader: RuntimeApiLoaderDefinition;\n readonly diagnostics: RuntimeApiLoaderDiagnostic[];\n readonly targetPath: string;\n}): unknown {\n const seed = args.resource.seed ?? [];\n const primaryKey = args.resource.collection.primaryKey ?? 'id';\n\n switch (args.loader.mode) {\n case 'list':\n return seed;\n case 'one':\n return seed[0];\n case 'random': {\n const index = deterministicIndex(args.api.id, seed.length);\n return index === undefined ? undefined : seed[index];\n }\n case 'byId': {\n const loaderId = normalizePrimitiveId(args.loader.id);\n if (loaderId === undefined) return undefined;\n\n return seed.find((record) => {\n return normalizePrimitiveId(record[primaryKey]) === loaderId;\n });\n }\n default:\n args.diagnostics.push({\n code: 'unsupported-loader',\n apiId: args.loader.apiId,\n targetPath: args.targetPath,\n message: `API loader mode ${String(args.loader.mode)} is not supported.`,\n });\n return undefined;\n }\n}\n\nfunction normalizePrimitiveId(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value.toString() : undefined;\n if (typeof value === 'boolean') return value ? 'true' : 'false';\n return undefined;\n}\n\nfunction deterministicIndex(apiId: string, length: number): number | undefined {\n if (length === 0) return undefined;\n\n let hash = 0;\n for (const char of apiId) {\n hash = (hash * 31 + char.charCodeAt(0)) % length;\n }\n\n return hash;\n}\n\nfunction writeObjectPath(\n target: Record<string, StateValue>,\n path: string,\n value: StateValue,\n): StateResult {\n const parts = path\n .split('.')\n .map((part) => part.trim())\n .filter(Boolean);\n const [head, ...tail] = parts;\n\n if (head === undefined) {\n return {\n ok: false,\n error: {\n code: 'invalid_path',\n message: 'State target path must contain at least one segment.',\n },\n };\n }\n\n if (tail.length === 0) {\n target[head] = value;\n return { ok: true };\n }\n\n const existing = target[head];\n const child = existing === undefined ? {} : existing;\n if (!isMutableStateRecord(child)) {\n return {\n ok: false,\n error: {\n code: 'path_conflict',\n message: `Cannot set nested state below non-object path segment \"${head}\".`,\n },\n };\n }\n\n target[head] = child;\n return writeObjectPath(child, tail.join('.'), value);\n}\n\nfunction flattenStateEntries(\n source: Readonly<Record<string, StateValue>>,\n prefix = '',\n): readonly (readonly [string, StateValue])[] {\n return Object.entries(source).flatMap(([key, value]) => {\n const path = prefix.length > 0 ? `${prefix}.${key}` : key;\n return [[path, value]] as const;\n });\n}\n\nfunction toStateValue(value: unknown): StateValue | undefined {\n if (value === null) return null;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n\n if (Array.isArray(value)) {\n const items: StateValue[] = [];\n for (const item of value) {\n const stateItem = toStateValue(item);\n if (stateItem === undefined) return undefined;\n items.push(stateItem);\n }\n return items;\n }\n\n if (!isUnknownRecord(value)) return undefined;\n\n const record: Record<string, StateValue> = {};\n for (const [key, entry] of Object.entries(value)) {\n const stateEntry = toStateValue(entry);\n if (stateEntry === undefined) return undefined;\n record[key] = stateEntry;\n }\n\n return record;\n}\n\nfunction isUnknownRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value != null && !Array.isArray(value);\n}\n\nfunction isMutableStateRecord(value: StateValue): value is Record<string, StateValue> {\n return typeof value === 'object' && value != null && !Array.isArray(value);\n}\n"]}
|