@ankhorage/runtime 1.0.0 → 2.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 +2 -2
- package/dist/RuntimeRenderer.d.ts +5 -2
- package/dist/RuntimeRenderer.d.ts.map +1 -1
- package/dist/RuntimeRenderer.js +40 -21
- package/dist/RuntimeRenderer.js.map +1 -1
- package/dist/RuntimeRendererConfig.d.ts +5 -2
- package/dist/RuntimeRendererConfig.d.ts.map +1 -1
- package/dist/RuntimeRendererConfig.js +3 -1
- package/dist/RuntimeRendererConfig.js.map +1 -1
- package/dist/RuntimeScreen.d.ts.map +1 -1
- package/dist/RuntimeScreen.js +3 -2
- package/dist/RuntimeScreen.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/readme-usage.js +1 -1
- package/dist/readme-usage.js.map +1 -1
- package/dist/runtimeActionRegistry.d.ts +3 -3
- package/dist/runtimeActionRegistry.d.ts.map +1 -1
- package/dist/runtimeActionRegistry.js +9 -24
- package/dist/runtimeActionRegistry.js.map +1 -1
- package/dist/runtimeApiOperations.d.ts +8 -0
- package/dist/runtimeApiOperations.d.ts.map +1 -0
- package/dist/runtimeApiOperations.js +38 -0
- package/dist/runtimeApiOperations.js.map +1 -0
- package/dist/runtimeApiSelection.d.ts +8 -0
- package/dist/runtimeApiSelection.d.ts.map +1 -0
- package/dist/runtimeApiSelection.js +50 -0
- package/dist/runtimeApiSelection.js.map +1 -0
- package/dist/runtimeBindings.d.ts +4 -9
- package/dist/runtimeBindings.d.ts.map +1 -1
- package/dist/runtimeBindings.js +13 -73
- package/dist/runtimeBindings.js.map +1 -1
- package/dist/runtimeMedia.d.ts +13 -0
- package/dist/runtimeMedia.d.ts.map +1 -0
- package/dist/runtimeMedia.js +80 -0
- package/dist/runtimeMedia.js.map +1 -0
- package/dist/runtimeMediaCache.d.ts +8 -0
- package/dist/runtimeMediaCache.d.ts.map +1 -0
- package/dist/runtimeMediaCache.js +14 -0
- package/dist/runtimeMediaCache.js.map +1 -0
- package/dist/runtimeNodeProps.d.ts +2 -2
- package/dist/runtimeNodeProps.d.ts.map +1 -1
- package/dist/runtimeNodeProps.js +1 -1
- package/dist/runtimeNodeProps.js.map +1 -1
- package/dist/runtimeRepeat.d.ts.map +1 -1
- package/dist/runtimeRepeat.js +9 -8
- package/dist/runtimeRepeat.js.map +1 -1
- package/dist/runtimeScreenLoaders.d.ts +2 -2
- package/dist/runtimeScreenLoaders.d.ts.map +1 -1
- package/dist/runtimeScreenLoaders.js +8 -7
- package/dist/runtimeScreenLoaders.js.map +1 -1
- package/dist/useRuntimeMediaProps.d.ts +9 -0
- package/dist/useRuntimeMediaProps.d.ts.map +1 -0
- package/dist/useRuntimeMediaProps.js +61 -0
- package/dist/useRuntimeMediaProps.js.map +1 -0
- package/package.json +8 -7
- package/paradox/badges/npm.svg +3 -3
- package/paradox/components.md +5 -3
- package/paradox/diagrams/architecture-overview.mmd +52 -18
- package/paradox/diagrams/export-graph.mmd +31 -31
- package/paradox/diagrams/module-relationships.mmd +38 -13
- package/paradox/diagrams/sequences/validate-runtime-binding-operation-ref.mmd +9 -6
- package/paradox/exports.json +262 -336
- package/paradox/exports.md +199 -230
- package/paradox/index.html +1627 -1381
- package/paradox/paradox.json +1605 -1458
- package/dist/runtimeDataSourceOperations.d.ts +0 -10
- package/dist/runtimeDataSourceOperations.d.ts.map +0 -1
- package/dist/runtimeDataSourceOperations.js +0 -63
- package/dist/runtimeDataSourceOperations.js.map +0 -1
- package/dist/runtimeDatabaseOperationExecutor.d.ts +0 -9
- package/dist/runtimeDatabaseOperationExecutor.d.ts.map +0 -1
- package/dist/runtimeDatabaseOperationExecutor.js +0 -228
- package/dist/runtimeDatabaseOperationExecutor.js.map +0 -1
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { DbAdapter } from '@ankhorage/contracts';
|
|
2
|
-
import type { EndpointTestCredentialResolver, EndpointTestFetch } from '@ankhorage/data-sources';
|
|
3
|
-
import type { RuntimeBindingOperationExecutor } from './runtimeBindings';
|
|
4
|
-
export interface RuntimeDataSourceOperationExecutorOptions {
|
|
5
|
-
readonly fetch?: EndpointTestFetch;
|
|
6
|
-
readonly credentialResolver?: EndpointTestCredentialResolver;
|
|
7
|
-
readonly databaseAdapters?: Readonly<Record<string, DbAdapter>>;
|
|
8
|
-
}
|
|
9
|
-
export declare function createRuntimeDataSourceOperationExecutor(options: RuntimeDataSourceOperationExecutorOptions): RuntimeBindingOperationExecutor;
|
|
10
|
-
//# sourceMappingURL=runtimeDataSourceOperations.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeDataSourceOperations.d.ts","sourceRoot":"","sources":["../src/runtimeDataSourceOperations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsC,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,KAAK,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGjG,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AAGzE,MAAM,WAAW,yCAAyC;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,8BAA8B,CAAC;IAC7D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;CACjE;AAED,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,yCAAyC,GACjD,+BAA+B,CAsCjC"}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { testEndpoint } from '@ankhorage/data-sources';
|
|
2
|
-
import { executeRuntimeDatabaseOperation } from './runtimeDatabaseOperationExecutor';
|
|
3
|
-
export function createRuntimeDataSourceOperationExecutor(options) {
|
|
4
|
-
return async ({ dataSource, endpoint, input, operation }) => {
|
|
5
|
-
if (endpoint === undefined) {
|
|
6
|
-
return missingEndpoint(operation.dataSourceId, operation.endpointId, operation.operationId);
|
|
7
|
-
}
|
|
8
|
-
const operationConfig = endpoint.operations[operation.operationId];
|
|
9
|
-
if (operationConfig === undefined) {
|
|
10
|
-
return missingOperation(operation.dataSourceId, endpoint.id, operation.operationId);
|
|
11
|
-
}
|
|
12
|
-
const values = asBindingValueRecord(input);
|
|
13
|
-
if (input !== undefined && values === undefined) {
|
|
14
|
-
return invalidInput(operation.dataSourceId, endpoint.id, operation.operationId);
|
|
15
|
-
}
|
|
16
|
-
if (operationConfig.protocol === 'database') {
|
|
17
|
-
return executeRuntimeDatabaseOperation({
|
|
18
|
-
dataSource,
|
|
19
|
-
operation: operationConfig,
|
|
20
|
-
input: values,
|
|
21
|
-
databaseAdapters: options.databaseAdapters,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
const result = await testEndpoint({
|
|
25
|
-
credentialResolver: options.credentialResolver,
|
|
26
|
-
dataSource,
|
|
27
|
-
endpointId: endpoint.id,
|
|
28
|
-
fetch: options.fetch,
|
|
29
|
-
operationId: operation.operationId,
|
|
30
|
-
values,
|
|
31
|
-
});
|
|
32
|
-
return result.ok
|
|
33
|
-
? { ok: true, data: result.data ?? null, diagnostics: result.diagnostics }
|
|
34
|
-
: { ok: false, diagnostics: result.diagnostics };
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function missingEndpoint(dataSourceId, endpointId, operationId) {
|
|
38
|
-
return failure(dataSourceId, endpointId, operationId, 'missing-endpoint', `Endpoint '${endpointId ?? '<default>'}' could not be found.`);
|
|
39
|
-
}
|
|
40
|
-
function missingOperation(dataSourceId, endpointId, operationId) {
|
|
41
|
-
return failure(dataSourceId, endpointId, operationId, 'missing-operation', `Operation '${operationId}' could not be found.`);
|
|
42
|
-
}
|
|
43
|
-
function invalidInput(dataSourceId, endpointId, operationId) {
|
|
44
|
-
return failure(dataSourceId, endpointId, operationId, 'invalid-config', 'Operation input must resolve to an object.');
|
|
45
|
-
}
|
|
46
|
-
function failure(dataSourceId, endpointId, operationId, code, message) {
|
|
47
|
-
const diagnostic = {
|
|
48
|
-
code,
|
|
49
|
-
dataSourceId,
|
|
50
|
-
endpointId,
|
|
51
|
-
operationId,
|
|
52
|
-
message,
|
|
53
|
-
severity: 'error',
|
|
54
|
-
};
|
|
55
|
-
return { ok: false, diagnostics: [diagnostic] };
|
|
56
|
-
}
|
|
57
|
-
function asBindingValueRecord(value) {
|
|
58
|
-
return isBindingValueRecord(value) ? value : undefined;
|
|
59
|
-
}
|
|
60
|
-
function isBindingValueRecord(value) {
|
|
61
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=runtimeDataSourceOperations.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeDataSourceOperations.js","sourceRoot":"","sources":["../src/runtimeDataSourceOperations.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGvD,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAC;AAQrF,MAAM,UAAU,wCAAwC,CACtD,OAAkD;IAElD,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE;QAC1D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,eAAe,CAAC,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QAC9F,CAAC;QAED,MAAM,eAAe,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,gBAAgB,CAAC,SAAS,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QACtF,CAAC;QAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAChD,OAAO,YAAY,CAAC,SAAS,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QAClF,CAAC;QAED,IAAI,eAAe,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;YAC5C,OAAO,+BAA+B,CAAC;gBACrC,UAAU;gBACV,SAAS,EAAE,eAAe;gBAC1B,KAAK,EAAE,MAAM;gBACb,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;YAChC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;YAC9C,UAAU;YACV,UAAU,EAAE,QAAQ,CAAC,EAAE;YACvB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,MAAM;SACP,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,EAAE;YACd,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE;YAC1E,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CACtB,YAAoB,EACpB,UAA8B,EAC9B,WAAmB;IAEnB,OAAO,OAAO,CACZ,YAAY,EACZ,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,aAAa,UAAU,IAAI,WAAW,uBAAuB,CAC9D,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,YAAoB,EAAE,UAAkB,EAAE,WAAmB;IACrF,OAAO,OAAO,CACZ,YAAY,EACZ,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,cAAc,WAAW,uBAAuB,CACjD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,YAAoB,EAAE,UAAkB,EAAE,WAAmB;IACjF,OAAO,OAAO,CACZ,YAAY,EACZ,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,4CAA4C,CAC7C,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CACd,YAAoB,EACpB,UAA8B,EAC9B,WAAmB,EACnB,IAAY,EACZ,OAAe;IAEf,MAAM,UAAU,GAAyB;QACvC,IAAI;QACJ,YAAY;QACZ,UAAU;QACV,WAAW;QACX,OAAO;QACP,QAAQ,EAAE,OAAO;KAClB,CAAC;IACF,OAAO,EAAE,EAAE,EAAE,KAAc,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,oBAAoB,CAC3B,KAA+B;IAE/B,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,SAAS,oBAAoB,CAC3B,KAA+B;IAE/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["import type { BindingValue, DataSourceDiagnostic, DbAdapter } from '@ankhorage/contracts';\nimport type { EndpointTestCredentialResolver, EndpointTestFetch } from '@ankhorage/data-sources';\nimport { testEndpoint } from '@ankhorage/data-sources';\n\nimport type { RuntimeBindingOperationExecutor } from './runtimeBindings';\nimport { executeRuntimeDatabaseOperation } from './runtimeDatabaseOperationExecutor';\n\nexport interface RuntimeDataSourceOperationExecutorOptions {\n readonly fetch?: EndpointTestFetch;\n readonly credentialResolver?: EndpointTestCredentialResolver;\n readonly databaseAdapters?: Readonly<Record<string, DbAdapter>>;\n}\n\nexport function createRuntimeDataSourceOperationExecutor(\n options: RuntimeDataSourceOperationExecutorOptions,\n): RuntimeBindingOperationExecutor {\n return async ({ dataSource, endpoint, input, operation }) => {\n if (endpoint === undefined) {\n return missingEndpoint(operation.dataSourceId, operation.endpointId, operation.operationId);\n }\n\n const operationConfig = endpoint.operations[operation.operationId];\n if (operationConfig === undefined) {\n return missingOperation(operation.dataSourceId, endpoint.id, operation.operationId);\n }\n\n const values = asBindingValueRecord(input);\n if (input !== undefined && values === undefined) {\n return invalidInput(operation.dataSourceId, endpoint.id, operation.operationId);\n }\n\n if (operationConfig.protocol === 'database') {\n return executeRuntimeDatabaseOperation({\n dataSource,\n operation: operationConfig,\n input: values,\n databaseAdapters: options.databaseAdapters,\n });\n }\n\n const result = await testEndpoint({\n credentialResolver: options.credentialResolver,\n dataSource,\n endpointId: endpoint.id,\n fetch: options.fetch,\n operationId: operation.operationId,\n values,\n });\n\n return result.ok\n ? { ok: true, data: result.data ?? null, diagnostics: result.diagnostics }\n : { ok: false, diagnostics: result.diagnostics };\n };\n}\n\nfunction missingEndpoint(\n dataSourceId: string,\n endpointId: string | undefined,\n operationId: string,\n) {\n return failure(\n dataSourceId,\n endpointId,\n operationId,\n 'missing-endpoint',\n `Endpoint '${endpointId ?? '<default>'}' could not be found.`,\n );\n}\n\nfunction missingOperation(dataSourceId: string, endpointId: string, operationId: string) {\n return failure(\n dataSourceId,\n endpointId,\n operationId,\n 'missing-operation',\n `Operation '${operationId}' could not be found.`,\n );\n}\n\nfunction invalidInput(dataSourceId: string, endpointId: string, operationId: string) {\n return failure(\n dataSourceId,\n endpointId,\n operationId,\n 'invalid-config',\n 'Operation input must resolve to an object.',\n );\n}\n\nfunction failure(\n dataSourceId: string,\n endpointId: string | undefined,\n operationId: string,\n code: string,\n message: string,\n) {\n const diagnostic: DataSourceDiagnostic = {\n code,\n dataSourceId,\n endpointId,\n operationId,\n message,\n severity: 'error',\n };\n return { ok: false as const, diagnostics: [diagnostic] };\n}\n\nfunction asBindingValueRecord(\n value: BindingValue | undefined,\n): Readonly<Record<string, BindingValue>> | undefined {\n return isBindingValueRecord(value) ? value : undefined;\n}\n\nfunction isBindingValueRecord(\n value: BindingValue | undefined,\n): value is Readonly<Record<string, BindingValue>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { BindingValue, DataOperationConfig, DataSourceConfig, DbAdapter } from '@ankhorage/contracts';
|
|
2
|
-
import type { RuntimeBindingOperationExecutionResult } from './runtimeBindings';
|
|
3
|
-
export declare function executeRuntimeDatabaseOperation(args: {
|
|
4
|
-
readonly dataSource: DataSourceConfig;
|
|
5
|
-
readonly operation: DataOperationConfig;
|
|
6
|
-
readonly input: Readonly<Record<string, BindingValue>> | undefined;
|
|
7
|
-
readonly databaseAdapters: Readonly<Record<string, DbAdapter>> | undefined;
|
|
8
|
-
}): Promise<RuntimeBindingOperationExecutionResult>;
|
|
9
|
-
//# sourceMappingURL=runtimeDatabaseOperationExecutor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeDatabaseOperationExecutor.d.ts","sourceRoot":"","sources":["../src/runtimeDatabaseOperationExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAEhB,SAAS,EAGV,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,sCAAsC,EAAE,MAAM,mBAAmB,CAAC;AAEhF,wBAAsB,+BAA+B,CAAC,IAAI,EAAE;IAC1D,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,GAAG,SAAS,CAAC;IACnE,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;CAC5E,GAAG,OAAO,CAAC,sCAAsC,CAAC,CA0BlD"}
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
export async function executeRuntimeDatabaseOperation(args) {
|
|
2
|
-
const plan = resolveOperationPlan(args.dataSource, args.operation);
|
|
3
|
-
if (!plan.ok)
|
|
4
|
-
return plan.result;
|
|
5
|
-
const adapter = args.databaseAdapters?.[plan.adapterId];
|
|
6
|
-
if (adapter === undefined) {
|
|
7
|
-
return failure(args, 'missing-adapter', `Database adapter '${plan.adapterId}' is not available.`);
|
|
8
|
-
}
|
|
9
|
-
const input = args.input ?? {};
|
|
10
|
-
switch (plan.operation) {
|
|
11
|
-
case 'list':
|
|
12
|
-
return executeList(adapter, plan, input, args);
|
|
13
|
-
case 'read':
|
|
14
|
-
return executeRead(adapter, plan, input, args);
|
|
15
|
-
case 'create':
|
|
16
|
-
return executeCreate(adapter, plan, input, args);
|
|
17
|
-
case 'update':
|
|
18
|
-
return executeUpdate(adapter, plan, input, args);
|
|
19
|
-
case 'delete':
|
|
20
|
-
return executeDelete(adapter, plan, input, args);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function resolveOperationPlan(dataSource, operation) {
|
|
24
|
-
const metadata = asRecord(operation.metadata);
|
|
25
|
-
const adapterId = resolveAdapterId(dataSource);
|
|
26
|
-
const collection = metadata?.collection;
|
|
27
|
-
const operationKind = metadata?.operation;
|
|
28
|
-
const schema = metadata?.schema;
|
|
29
|
-
if (adapterId === undefined) {
|
|
30
|
-
return invalidPlan(dataSource, operation, 'Database operation source does not reference an adapter.');
|
|
31
|
-
}
|
|
32
|
-
if (typeof collection !== 'string' || collection.trim().length === 0) {
|
|
33
|
-
return invalidPlan(dataSource, operation, 'Database operation metadata requires a collection.');
|
|
34
|
-
}
|
|
35
|
-
if (!isGeneratedCrudOperation(operationKind)) {
|
|
36
|
-
return invalidPlan(dataSource, operation, 'Database operation metadata requires a generated CRUD operation.');
|
|
37
|
-
}
|
|
38
|
-
if (schema !== undefined && schema !== null && typeof schema !== 'string') {
|
|
39
|
-
return invalidPlan(dataSource, operation, 'Database operation schema metadata must be a string or null.');
|
|
40
|
-
}
|
|
41
|
-
const metadataAdapterId = metadata?.adapterId;
|
|
42
|
-
if (metadataAdapterId !== undefined && metadataAdapterId !== adapterId) {
|
|
43
|
-
return invalidPlan(dataSource, operation, 'Database operation adapter metadata does not match its data source.');
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
ok: true,
|
|
47
|
-
adapterId,
|
|
48
|
-
collection,
|
|
49
|
-
schema: typeof schema === 'string' ? schema : undefined,
|
|
50
|
-
operation: operationKind,
|
|
51
|
-
primaryKey: resolvePrimaryKey(operation),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
async function executeList(adapter, plan, input, args) {
|
|
55
|
-
const page = resolvePage(input);
|
|
56
|
-
if (!page.ok)
|
|
57
|
-
return failure(args, 'invalid-config', page.message);
|
|
58
|
-
const result = await adapter.select({
|
|
59
|
-
table: plan.collection,
|
|
60
|
-
schema: plan.schema,
|
|
61
|
-
page: page.value,
|
|
62
|
-
});
|
|
63
|
-
return resolveDbResult(args, result, (records) => records);
|
|
64
|
-
}
|
|
65
|
-
async function executeRead(adapter, plan, input, args) {
|
|
66
|
-
const identity = resolveIdentity(plan, input);
|
|
67
|
-
if (!identity.ok)
|
|
68
|
-
return failure(args, 'invalid-config', identity.message);
|
|
69
|
-
const result = await adapter.findById({
|
|
70
|
-
table: plan.collection,
|
|
71
|
-
schema: plan.schema,
|
|
72
|
-
id: identity.value,
|
|
73
|
-
idField: plan.primaryKey,
|
|
74
|
-
});
|
|
75
|
-
return resolveDbResult(args, result, (record) => record);
|
|
76
|
-
}
|
|
77
|
-
async function executeCreate(adapter, plan, input, args) {
|
|
78
|
-
const result = await adapter.insert({
|
|
79
|
-
table: plan.collection,
|
|
80
|
-
schema: plan.schema,
|
|
81
|
-
values: { ...input },
|
|
82
|
-
});
|
|
83
|
-
return resolveDbResult(args, result, (records) => records[0] ?? null);
|
|
84
|
-
}
|
|
85
|
-
async function executeUpdate(adapter, plan, input, args) {
|
|
86
|
-
const identity = resolveIdentity(plan, input);
|
|
87
|
-
if (!identity.ok)
|
|
88
|
-
return failure(args, 'invalid-config', identity.message);
|
|
89
|
-
const values = omitKey(input, plan.primaryKey);
|
|
90
|
-
if (Object.keys(values).length === 0) {
|
|
91
|
-
return failure(args, 'invalid-config', 'Generated update operation requires at least one value to update.');
|
|
92
|
-
}
|
|
93
|
-
const result = await adapter.update({
|
|
94
|
-
table: plan.collection,
|
|
95
|
-
schema: plan.schema,
|
|
96
|
-
values,
|
|
97
|
-
filters: [createIdentityFilter(plan.primaryKey, identity.value)],
|
|
98
|
-
});
|
|
99
|
-
return resolveDbResult(args, result, (records) => records[0] ?? null);
|
|
100
|
-
}
|
|
101
|
-
async function executeDelete(adapter, plan, input, args) {
|
|
102
|
-
const identity = resolveIdentity(plan, input);
|
|
103
|
-
if (!identity.ok)
|
|
104
|
-
return failure(args, 'invalid-config', identity.message);
|
|
105
|
-
const result = await adapter.delete({
|
|
106
|
-
table: plan.collection,
|
|
107
|
-
schema: plan.schema,
|
|
108
|
-
filters: [createIdentityFilter(plan.primaryKey, identity.value)],
|
|
109
|
-
});
|
|
110
|
-
return resolveDbResult(args, result, (records) => ({ deleted: records.length > 0 }));
|
|
111
|
-
}
|
|
112
|
-
function resolveDbResult(args, result, select) {
|
|
113
|
-
if (!result.ok)
|
|
114
|
-
return failure(args, 'database-operation-failed', result.error.message);
|
|
115
|
-
const value = toBindingValue(select(result.data));
|
|
116
|
-
if (value === undefined) {
|
|
117
|
-
return failure(args, 'invalid-config', 'Database adapter returned a non-serializable binding value.');
|
|
118
|
-
}
|
|
119
|
-
return { ok: true, data: value, diagnostics: [] };
|
|
120
|
-
}
|
|
121
|
-
function resolveAdapterId(dataSource) {
|
|
122
|
-
if (dataSource.kind === 'database')
|
|
123
|
-
return dataSource.adapter.id;
|
|
124
|
-
if (dataSource.origin === 'generated')
|
|
125
|
-
return dataSource.adapter.id;
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
function resolvePrimaryKey(operation) {
|
|
129
|
-
return operation.request?.parameters?.find((parameter) => parameter.location === 'path')?.name;
|
|
130
|
-
}
|
|
131
|
-
function resolveIdentity(plan, input) {
|
|
132
|
-
if (plan.primaryKey === undefined) {
|
|
133
|
-
return {
|
|
134
|
-
ok: false,
|
|
135
|
-
message: 'Generated read/update/delete operation requires primary-key metadata.',
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
const value = input[plan.primaryKey];
|
|
139
|
-
return typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value))
|
|
140
|
-
? { ok: true, value }
|
|
141
|
-
: {
|
|
142
|
-
ok: false,
|
|
143
|
-
message: `Generated operation requires '${plan.primaryKey}' as a string or number.`,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
function resolvePage(input) {
|
|
147
|
-
const { limit, offset } = input;
|
|
148
|
-
if (!isOptionalPageNumber(limit) || !isOptionalPageNumber(offset)) {
|
|
149
|
-
return {
|
|
150
|
-
ok: false,
|
|
151
|
-
message: 'Generated list operation limit/offset must be non-negative integers.',
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
if (limit === undefined && offset === undefined)
|
|
155
|
-
return { ok: true, value: undefined };
|
|
156
|
-
return { ok: true, value: { limit, offset } };
|
|
157
|
-
}
|
|
158
|
-
function isOptionalPageNumber(value) {
|
|
159
|
-
return (value === undefined || (typeof value === 'number' && Number.isInteger(value) && value >= 0));
|
|
160
|
-
}
|
|
161
|
-
function createIdentityFilter(field, value) {
|
|
162
|
-
return { field: field ?? 'id', operator: 'eq', value };
|
|
163
|
-
}
|
|
164
|
-
function omitKey(input, key) {
|
|
165
|
-
const values = {};
|
|
166
|
-
for (const [field, value] of Object.entries(input)) {
|
|
167
|
-
if (field !== key)
|
|
168
|
-
values[field] = value;
|
|
169
|
-
}
|
|
170
|
-
return values;
|
|
171
|
-
}
|
|
172
|
-
function isGeneratedCrudOperation(value) {
|
|
173
|
-
return (value === 'list' ||
|
|
174
|
-
value === 'read' ||
|
|
175
|
-
value === 'create' ||
|
|
176
|
-
value === 'update' ||
|
|
177
|
-
value === 'delete');
|
|
178
|
-
}
|
|
179
|
-
function invalidPlan(dataSource, operation, message) {
|
|
180
|
-
return {
|
|
181
|
-
ok: false,
|
|
182
|
-
result: failure({ dataSource, operation }, 'invalid-config', message),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
function failure(args, code, message) {
|
|
186
|
-
const diagnostic = {
|
|
187
|
-
code,
|
|
188
|
-
dataSourceId: args.dataSource.id,
|
|
189
|
-
endpointId: args.operation.endpointId,
|
|
190
|
-
operationId: args.operation.id,
|
|
191
|
-
message,
|
|
192
|
-
severity: 'error',
|
|
193
|
-
};
|
|
194
|
-
return { ok: false, diagnostics: [diagnostic] };
|
|
195
|
-
}
|
|
196
|
-
function asRecord(value) {
|
|
197
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
198
|
-
? value
|
|
199
|
-
: undefined;
|
|
200
|
-
}
|
|
201
|
-
function toBindingValue(value) {
|
|
202
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
203
|
-
return value;
|
|
204
|
-
if (typeof value === 'number')
|
|
205
|
-
return Number.isFinite(value) ? value : undefined;
|
|
206
|
-
if (Array.isArray(value)) {
|
|
207
|
-
const items = [];
|
|
208
|
-
for (const item of value) {
|
|
209
|
-
const converted = toBindingValue(item);
|
|
210
|
-
if (converted === undefined)
|
|
211
|
-
return undefined;
|
|
212
|
-
items.push(converted);
|
|
213
|
-
}
|
|
214
|
-
return items;
|
|
215
|
-
}
|
|
216
|
-
const record = asRecord(value);
|
|
217
|
-
if (record === undefined)
|
|
218
|
-
return undefined;
|
|
219
|
-
const converted = {};
|
|
220
|
-
for (const [key, item] of Object.entries(record)) {
|
|
221
|
-
const bindingValue = toBindingValue(item);
|
|
222
|
-
if (bindingValue === undefined)
|
|
223
|
-
return undefined;
|
|
224
|
-
converted[key] = bindingValue;
|
|
225
|
-
}
|
|
226
|
-
return converted;
|
|
227
|
-
}
|
|
228
|
-
//# sourceMappingURL=runtimeDatabaseOperationExecutor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeDatabaseOperationExecutor.js","sourceRoot":"","sources":["../src/runtimeDatabaseOperationExecutor.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BindingValue,\n DataOperationConfig,\n DataSourceConfig,\n DataSourceDiagnostic,\n DbAdapter,\n DbFilter,\n DbRecord,\n} from '@ankhorage/contracts';\n\nimport type { RuntimeBindingOperationExecutionResult } from './runtimeBindings';\n\nexport async function executeRuntimeDatabaseOperation(args: {\n readonly dataSource: DataSourceConfig;\n readonly operation: DataOperationConfig;\n readonly input: Readonly<Record<string, BindingValue>> | undefined;\n readonly databaseAdapters: Readonly<Record<string, DbAdapter>> | undefined;\n}): Promise<RuntimeBindingOperationExecutionResult> {\n const plan = resolveOperationPlan(args.dataSource, args.operation);\n if (!plan.ok) return plan.result;\n\n const adapter = args.databaseAdapters?.[plan.adapterId];\n if (adapter === undefined) {\n return failure(\n args,\n 'missing-adapter',\n `Database adapter '${plan.adapterId}' is not available.`,\n );\n }\n\n const input = args.input ?? {};\n switch (plan.operation) {\n case 'list':\n return executeList(adapter, plan, input, args);\n case 'read':\n return executeRead(adapter, plan, input, args);\n case 'create':\n return executeCreate(adapter, plan, input, args);\n case 'update':\n return executeUpdate(adapter, plan, input, args);\n case 'delete':\n return executeDelete(adapter, plan, input, args);\n }\n}\n\ntype GeneratedCrudOperation = 'list' | 'read' | 'create' | 'update' | 'delete';\n\ninterface DatabaseOperationPlan {\n readonly adapterId: string;\n readonly collection: string;\n readonly schema?: string;\n readonly operation: GeneratedCrudOperation;\n readonly primaryKey?: string;\n}\n\ntype OperationPlanResult =\n | {\n readonly ok: true;\n readonly adapterId: string;\n readonly collection: string;\n readonly schema?: string;\n readonly operation: GeneratedCrudOperation;\n readonly primaryKey?: string;\n }\n | { readonly ok: false; readonly result: RuntimeBindingOperationExecutionResult };\n\nfunction resolveOperationPlan(\n dataSource: DataSourceConfig,\n operation: DataOperationConfig,\n): OperationPlanResult {\n const metadata = asRecord(operation.metadata);\n const adapterId = resolveAdapterId(dataSource);\n const collection = metadata?.collection;\n const operationKind = metadata?.operation;\n const schema = metadata?.schema;\n\n if (adapterId === undefined) {\n return invalidPlan(\n dataSource,\n operation,\n 'Database operation source does not reference an adapter.',\n );\n }\n if (typeof collection !== 'string' || collection.trim().length === 0) {\n return invalidPlan(dataSource, operation, 'Database operation metadata requires a collection.');\n }\n if (!isGeneratedCrudOperation(operationKind)) {\n return invalidPlan(\n dataSource,\n operation,\n 'Database operation metadata requires a generated CRUD operation.',\n );\n }\n if (schema !== undefined && schema !== null && typeof schema !== 'string') {\n return invalidPlan(\n dataSource,\n operation,\n 'Database operation schema metadata must be a string or null.',\n );\n }\n\n const metadataAdapterId = metadata?.adapterId;\n if (metadataAdapterId !== undefined && metadataAdapterId !== adapterId) {\n return invalidPlan(\n dataSource,\n operation,\n 'Database operation adapter metadata does not match its data source.',\n );\n }\n\n return {\n ok: true,\n adapterId,\n collection,\n schema: typeof schema === 'string' ? schema : undefined,\n operation: operationKind,\n primaryKey: resolvePrimaryKey(operation),\n };\n}\n\nasync function executeList(\n adapter: DbAdapter,\n plan: DatabaseOperationPlan,\n input: Readonly<Record<string, BindingValue>>,\n args: DatabaseExecutionArgs,\n): Promise<RuntimeBindingOperationExecutionResult> {\n const page = resolvePage(input);\n if (!page.ok) return failure(args, 'invalid-config', page.message);\n\n const result = await adapter.select({\n table: plan.collection,\n schema: plan.schema,\n page: page.value,\n });\n return resolveDbResult(args, result, (records) => records);\n}\n\nasync function executeRead(\n adapter: DbAdapter,\n plan: DatabaseOperationPlan,\n input: Readonly<Record<string, BindingValue>>,\n args: DatabaseExecutionArgs,\n): Promise<RuntimeBindingOperationExecutionResult> {\n const identity = resolveIdentity(plan, input);\n if (!identity.ok) return failure(args, 'invalid-config', identity.message);\n\n const result = await adapter.findById({\n table: plan.collection,\n schema: plan.schema,\n id: identity.value,\n idField: plan.primaryKey,\n });\n return resolveDbResult(args, result, (record) => record);\n}\n\nasync function executeCreate(\n adapter: DbAdapter,\n plan: DatabaseOperationPlan,\n input: Readonly<Record<string, BindingValue>>,\n args: DatabaseExecutionArgs,\n): Promise<RuntimeBindingOperationExecutionResult> {\n const result = await adapter.insert({\n table: plan.collection,\n schema: plan.schema,\n values: { ...input },\n });\n return resolveDbResult(args, result, (records) => records[0] ?? null);\n}\n\nasync function executeUpdate(\n adapter: DbAdapter,\n plan: DatabaseOperationPlan,\n input: Readonly<Record<string, BindingValue>>,\n args: DatabaseExecutionArgs,\n): Promise<RuntimeBindingOperationExecutionResult> {\n const identity = resolveIdentity(plan, input);\n if (!identity.ok) return failure(args, 'invalid-config', identity.message);\n\n const values = omitKey(input, plan.primaryKey);\n if (Object.keys(values).length === 0) {\n return failure(\n args,\n 'invalid-config',\n 'Generated update operation requires at least one value to update.',\n );\n }\n\n const result = await adapter.update({\n table: plan.collection,\n schema: plan.schema,\n values,\n filters: [createIdentityFilter(plan.primaryKey, identity.value)],\n });\n return resolveDbResult(args, result, (records) => records[0] ?? null);\n}\n\nasync function executeDelete(\n adapter: DbAdapter,\n plan: DatabaseOperationPlan,\n input: Readonly<Record<string, BindingValue>>,\n args: DatabaseExecutionArgs,\n): Promise<RuntimeBindingOperationExecutionResult> {\n const identity = resolveIdentity(plan, input);\n if (!identity.ok) return failure(args, 'invalid-config', identity.message);\n\n const result = await adapter.delete({\n table: plan.collection,\n schema: plan.schema,\n filters: [createIdentityFilter(plan.primaryKey, identity.value)],\n });\n return resolveDbResult(args, result, (records) => ({ deleted: records.length > 0 }));\n}\n\ntype DatabaseExecutionArgs = Pick<\n Parameters<typeof executeRuntimeDatabaseOperation>[0],\n 'dataSource' | 'operation'\n>;\n\nfunction resolveDbResult<TData>(\n args: DatabaseExecutionArgs,\n result:\n | { readonly ok: true; readonly data: TData }\n | { readonly ok: false; readonly error: { readonly message: string } },\n select: (data: TData) => unknown,\n): RuntimeBindingOperationExecutionResult {\n if (!result.ok) return failure(args, 'database-operation-failed', result.error.message);\n\n const value = toBindingValue(select(result.data));\n if (value === undefined) {\n return failure(\n args,\n 'invalid-config',\n 'Database adapter returned a non-serializable binding value.',\n );\n }\n return { ok: true, data: value, diagnostics: [] };\n}\n\nfunction resolveAdapterId(dataSource: DataSourceConfig): string | undefined {\n if (dataSource.kind === 'database') return dataSource.adapter.id;\n if (dataSource.origin === 'generated') return dataSource.adapter.id;\n return undefined;\n}\n\nfunction resolvePrimaryKey(operation: DataOperationConfig): string | undefined {\n return operation.request?.parameters?.find((parameter) => parameter.location === 'path')?.name;\n}\n\nfunction resolveIdentity(\n plan: DatabaseOperationPlan,\n input: Readonly<Record<string, BindingValue>>,\n):\n | { readonly ok: true; readonly value: string | number }\n | { readonly ok: false; readonly message: string } {\n if (plan.primaryKey === undefined) {\n return {\n ok: false,\n message: 'Generated read/update/delete operation requires primary-key metadata.',\n };\n }\n const value = input[plan.primaryKey];\n return typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value))\n ? { ok: true, value }\n : {\n ok: false,\n message: `Generated operation requires '${plan.primaryKey}' as a string or number.`,\n };\n}\n\nfunction resolvePage(input: Readonly<Record<string, BindingValue>>):\n | {\n readonly ok: true;\n readonly value: { readonly limit?: number; readonly offset?: number } | undefined;\n }\n | { readonly ok: false; readonly message: string } {\n const { limit, offset } = input;\n if (!isOptionalPageNumber(limit) || !isOptionalPageNumber(offset)) {\n return {\n ok: false,\n message: 'Generated list operation limit/offset must be non-negative integers.',\n };\n }\n if (limit === undefined && offset === undefined) return { ok: true, value: undefined };\n return { ok: true, value: { limit, offset } };\n}\n\nfunction isOptionalPageNumber(value: BindingValue | undefined): value is number | undefined {\n return (\n value === undefined || (typeof value === 'number' && Number.isInteger(value) && value >= 0)\n );\n}\n\nfunction createIdentityFilter(field: string | undefined, value: string | number): DbFilter {\n return { field: field ?? 'id', operator: 'eq', value };\n}\n\nfunction omitKey(input: Readonly<Record<string, BindingValue>>, key: string | undefined): DbRecord {\n const values: DbRecord = {};\n for (const [field, value] of Object.entries(input)) {\n if (field !== key) values[field] = value;\n }\n return values;\n}\n\nfunction isGeneratedCrudOperation(value: unknown): value is GeneratedCrudOperation {\n return (\n value === 'list' ||\n value === 'read' ||\n value === 'create' ||\n value === 'update' ||\n value === 'delete'\n );\n}\n\nfunction invalidPlan(\n dataSource: DataSourceConfig,\n operation: DataOperationConfig,\n message: string,\n): OperationPlanResult {\n return {\n ok: false,\n result: failure({ dataSource, operation }, 'invalid-config', message),\n };\n}\n\nfunction failure(\n args: DatabaseExecutionArgs,\n code: string,\n message: string,\n): RuntimeBindingOperationExecutionResult {\n const diagnostic: DataSourceDiagnostic = {\n code,\n dataSourceId: args.dataSource.id,\n endpointId: args.operation.endpointId,\n operationId: args.operation.id,\n message,\n severity: 'error',\n };\n return { ok: false, diagnostics: [diagnostic] };\n}\n\nfunction asRecord(value: unknown): Readonly<Record<string, unknown>> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Readonly<Record<string, unknown>>)\n : undefined;\n}\n\nfunction toBindingValue(value: unknown): BindingValue | undefined {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n if (Array.isArray(value)) {\n const items: BindingValue[] = [];\n for (const item of value) {\n const converted = toBindingValue(item);\n if (converted === undefined) return undefined;\n items.push(converted);\n }\n return items;\n }\n const record = asRecord(value);\n if (record === undefined) return undefined;\n const converted: Record<string, BindingValue> = {};\n for (const [key, item] of Object.entries(record)) {\n const bindingValue = toBindingValue(item);\n if (bindingValue === undefined) return undefined;\n converted[key] = bindingValue;\n }\n return converted;\n}\n"]}
|