@noodleseed/one 0.164.0 → 0.166.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/dist/commands/deploy-status-ops.d.ts +2 -0
- package/dist/commands/deploy-status-ops.d.ts.map +1 -1
- package/dist/commands/deploy-status-ops.js +11 -0
- package/dist/commands/deploy-status-ops.js.map +1 -1
- package/dist/commands/solutions-coordination.d.ts +5 -0
- package/dist/commands/solutions-coordination.d.ts.map +1 -0
- package/dist/commands/solutions-coordination.js +94 -0
- package/dist/commands/solutions-coordination.js.map +1 -0
- package/dist/commands/solutions-ops.d.ts.map +1 -1
- package/dist/commands/solutions-ops.js +8 -4
- package/dist/commands/solutions-ops.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +2 -0
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +2 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +4 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/embed-store.d.ts +4 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-embed-store.d.ts +4 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-embed-store.js +6 -2
- package/node_modules/@noodle-borg/authoring/dist/config.d.ts +8 -2
- package/node_modules/@noodle-borg/authoring/dist/config.js +28 -1
- package/node_modules/@noodle-borg/authoring/dist/connectors.d.ts +37 -0
- package/node_modules/@noodle-borg/authoring/dist/connectors.js +4 -0
- package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/authoring/dist/server.js +1 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-flags.js +83 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +70 -92
- package/node_modules/@noodle-borg/compiler/dist/artifact/operation-ref.d.ts +2 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +4 -1
- package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +4 -1
- package/node_modules/@noodle-borg/compiler/dist/catalog/types.d.ts +7 -0
- package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
- package/node_modules/@noodle-borg/compiler/dist/fulfilment-emit.js +15 -2
- package/node_modules/@noodle-borg/compiler/dist/nested-operations.js +71 -0
- package/node_modules/@noodle-borg/compute/dist/code-connector.js +14 -2
- package/node_modules/@noodle-borg/compute/dist/deterministic-helpers.js +138 -0
- package/node_modules/@noodle-borg/compute/dist/quickjs-engine.js +48 -0
- package/node_modules/@noodle-borg/compute/dist/sandbox-eval.js +39 -5
- package/node_modules/@noodle-borg/compute/dist/worker-entry.js +7 -0
- package/node_modules/@noodle-borg/compute/dist/worker-pool.js +14 -3
- package/node_modules/@noodle-borg/connector-defs/dist/compile-http.js +31 -2
- package/node_modules/@noodle-borg/connector-defs/dist/compile.d.ts +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/compile.js +20 -0
- package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts +84 -0
- package/node_modules/@noodle-borg/connector-defs/dist/schema.js +44 -1
- package/node_modules/@noodle-borg/connector-http/dist/http-connector.d.ts +7 -0
- package/node_modules/@noodle-borg/connector-http/dist/http-connector.js +34 -6
- package/node_modules/@noodle-borg/connector-http/dist/http-response.d.ts +8 -1
- package/node_modules/@noodle-borg/connector-http/dist/http-response.js +19 -10
- package/node_modules/@noodle-borg/connector-http/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/module/dist/customer-auth-access.d.ts +7 -0
- package/node_modules/@noodle-borg/module/dist/customer-auth-access.js +6 -0
- package/node_modules/@noodle-borg/module/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/module/dist/index.js +1 -0
- package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +7 -1
- package/node_modules/@noodle-borg/runtime/dist/connector-snapshot.js +8 -0
- package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +2 -0
- package/node_modules/@noodle-borg/runtime/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/runtime/dist/nested-operation-host.js +25 -0
- package/node_modules/@noodle-borg/runtime/dist/operation-coordination-execution.js +50 -0
- package/node_modules/@noodle-borg/runtime/dist/operation-coordination.d.ts +34 -0
- package/node_modules/@noodle-borg/runtime/dist/operation-evidence.d.ts +4 -0
- package/node_modules/@noodle-borg/runtime/dist/operation-execution.js +173 -94
- package/node_modules/@noodle-borg/service/dist/application-activity.js +152 -18
- package/node_modules/@noodle-borg/service/dist/application-runtime-target.js +15 -2
- package/node_modules/@noodle-borg/service/dist/business-information/definition-resolver.js +19 -0
- package/node_modules/@noodle-borg/service/dist/business-information/in-memory-store.js +1 -1
- package/node_modules/@noodle-borg/service/dist/business-information/managed-solution-blueprints.js +7 -0
- package/node_modules/@noodle-borg/service/dist/business-information/managed-solution-executable.js +11 -1
- package/node_modules/@noodle-borg/service/dist/business-information/model.js +2 -2
- package/node_modules/@noodle-borg/service/dist/business-information/portable.js +1 -1
- package/node_modules/@noodle-borg/service/dist/business-information/profiles.js +37 -1
- package/node_modules/@noodle-borg/service/dist/credential-binding-index.js +19 -1
- package/node_modules/@noodle-borg/service/dist/customer-auth-audience-binding.js +16 -5
- package/node_modules/@noodle-borg/service/dist/deployment-authentication.js +19 -0
- package/node_modules/@noodle-borg/service/dist/deployment-execution.js +1 -1
- package/node_modules/@noodle-borg/service/dist/deployment-record-version.js +31 -0
- package/node_modules/@noodle-borg/service/dist/developer-mcp/inspection.js +15 -1
- package/node_modules/@noodle-borg/service/dist/operation-coordination.js +185 -0
- package/node_modules/@noodle-borg/service/dist/operation-evidence.js +3 -1
- package/node_modules/@noodle-borg/service/dist/registry-access.js +18 -3
- package/node_modules/@noodle-borg/service/dist/registry-compile.js +42 -1
- package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +15 -0
- package/node_modules/@noodle-borg/service/dist/registry-rollback.js +2 -0
- package/node_modules/@noodle-borg/service/dist/registry-state.d.ts +1 -0
- package/node_modules/@noodle-borg/service/dist/registry-state.js +12 -5
- package/node_modules/@noodle-borg/service/dist/registry-status.js +3 -0
- package/node_modules/@noodle-borg/service/dist/registry-targets.js +8 -0
- package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +1 -1
- package/node_modules/@noodle-borg/service/dist/registry.js +31 -36
- package/node_modules/@noodle-borg/service/dist/routes/access.js +5 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-information-activity.js +48 -53
- package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +2 -1
- package/node_modules/@noodle-borg/service/dist/routes/business-information-installation.js +51 -17
- package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +6 -4
- package/node_modules/@noodle-borg/service/dist/routes/business-information-staff.js +4 -3
- package/node_modules/@noodle-borg/service/dist/routes/business-information.js +11 -6
- package/node_modules/@noodle-borg/service/dist/routes/deploy-public-embed.js +7 -2
- package/node_modules/@noodle-borg/service/dist/service.js +2 -1
- package/node_modules/@noodle-borg/service/dist/solution-installation-activation.js +58 -3
- package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +1 -0
- package/node_modules/@noodle-borg/service/dist/store/in-memory.js +10 -3
- package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +1 -0
- package/node_modules/@noodle-borg/service/dist/store/json-file.js +10 -3
- package/node_modules/@noodle-borg/service/dist/store/records.js +4 -2
- package/node_modules/@noodle-borg/service/dist/store.d.ts +5 -1
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/business-information.d.ts +52 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/business-information.js +6 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +23 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.js +34 -4
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/operation-coordination.d.ts +59 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/operation-coordination.js +51 -0
- package/package.json +2 -2
package/node_modules/@noodle-borg/service/dist/business-information/managed-solution-executable.js
CHANGED
|
@@ -10,6 +10,16 @@ export function managedSolutionManifest(definition) {
|
|
|
10
10
|
const collection = definition.collections.find((entry) => entry.key === blueprint.collection);
|
|
11
11
|
if (collection === undefined)
|
|
12
12
|
throw new Error('Managed solution executable collection is missing');
|
|
13
|
+
const recordInput = collectionToPublicWire(collection).recordSchema;
|
|
14
|
+
const inputSchema = 'followUpContact' in blueprint
|
|
15
|
+
? {
|
|
16
|
+
...recordInput,
|
|
17
|
+
required: [
|
|
18
|
+
...(Array.isArray(recordInput.required) ? recordInput.required : []),
|
|
19
|
+
'contact_email',
|
|
20
|
+
],
|
|
21
|
+
}
|
|
22
|
+
: recordInput;
|
|
13
23
|
return {
|
|
14
24
|
manifestVersion: '2',
|
|
15
25
|
server: {
|
|
@@ -46,7 +56,7 @@ export function managedSolutionManifest(definition) {
|
|
|
46
56
|
name: blueprint.tool,
|
|
47
57
|
title: blueprint.title,
|
|
48
58
|
description: blueprint.description,
|
|
49
|
-
inputSchema
|
|
59
|
+
inputSchema,
|
|
50
60
|
outputSchema: RECORD_OPERATION_SIGNATURES.submit_record?.output,
|
|
51
61
|
annotations: {
|
|
52
62
|
confirm: true,
|
|
@@ -47,13 +47,13 @@ export function businessGrantAllows(grant, permission) {
|
|
|
47
47
|
grant.revokedAt === undefined &&
|
|
48
48
|
permissionsForBusinessRole(grant.role).includes(permission));
|
|
49
49
|
}
|
|
50
|
-
export function normalizeInstallationInput(input) {
|
|
50
|
+
export function normalizeInstallationInput(input, resolveManaged = builtInDefinition) {
|
|
51
51
|
const scope = { ...validateScope(input.scope) };
|
|
52
52
|
if ((input.profileKey === undefined) === (input.definition === undefined)) {
|
|
53
53
|
throw new Error('installation requires exactly one managed profile or private definition');
|
|
54
54
|
}
|
|
55
55
|
const definition = input.definition ??
|
|
56
|
-
|
|
56
|
+
resolveManaged(input.profileKey);
|
|
57
57
|
validateDefinition(definition);
|
|
58
58
|
const managedCollections = [...new Set(input.managedCollections)].sort();
|
|
59
59
|
for (const collectionKey of managedCollections) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './collection-controls.js';
|
|
2
2
|
export * from './contracts.js';
|
|
3
|
-
export
|
|
3
|
+
export { privateDefinitionFromDeployment, } from './definition-resolver.js';
|
|
4
4
|
export * from './in-memory-store.js';
|
|
5
5
|
export * from './legacy-request-migration.js';
|
|
6
6
|
export * from './managed-releases.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { validateJsonSchema as validateSchema } from '@noodle-borg/compiler';
|
|
2
3
|
import { managedSolutionBlueprint, managedSolutionVariables, } from './managed-solution-blueprints.js';
|
|
3
4
|
import { PayloadValidationError, validateManagedPayload, validateScalar } from './validation.js';
|
|
4
5
|
const requestSchema = (required, properties) => ({
|
|
@@ -73,6 +74,12 @@ export const BUILT_IN_SOLUTION_PROFILE_RELEASES = {
|
|
|
73
74
|
{ key: 'travel', version: 1, label: 'Travel', collections: [travelV1] },
|
|
74
75
|
{ key: 'travel', version: 2, label: 'Travel', collections: [travelV2] },
|
|
75
76
|
{ key: 'travel', version: 3, label: 'Travel', collections: [lightweightCollection(travelV2)] },
|
|
77
|
+
{
|
|
78
|
+
key: 'travel',
|
|
79
|
+
version: 4,
|
|
80
|
+
label: 'Travel',
|
|
81
|
+
collections: [followUpCollection(lightweightCollection(travelV2))],
|
|
82
|
+
},
|
|
76
83
|
],
|
|
77
84
|
b2b_saas: [{ key: 'b2b_saas', version: 1, label: 'B2B SaaS', collections: [service] }],
|
|
78
85
|
ecommerce: [
|
|
@@ -92,6 +99,12 @@ export const BUILT_IN_SOLUTION_PROFILE_RELEASES = {
|
|
|
92
99
|
label: 'Restaurant',
|
|
93
100
|
collections: [lightweightCollection(restaurant)],
|
|
94
101
|
},
|
|
102
|
+
{
|
|
103
|
+
key: 'restaurant',
|
|
104
|
+
version: 3,
|
|
105
|
+
label: 'Restaurant',
|
|
106
|
+
collections: [followUpCollection(lightweightCollection(restaurant))],
|
|
107
|
+
},
|
|
95
108
|
],
|
|
96
109
|
};
|
|
97
110
|
export const BUILT_IN_SOLUTION_PROFILES = {
|
|
@@ -189,7 +202,7 @@ function collectionDefinition(profile) {
|
|
|
189
202
|
management: profile.management,
|
|
190
203
|
publicFields: profile.publicFields ?? [],
|
|
191
204
|
editableFields: Object.keys(profile.schema.properties),
|
|
192
|
-
fields: { status: { label: 'Status' } },
|
|
205
|
+
fields: { status: { label: 'Status' }, ...profile.fields },
|
|
193
206
|
filterFields: ['status'],
|
|
194
207
|
sortFields: ['status'],
|
|
195
208
|
}),
|
|
@@ -213,6 +226,26 @@ function lightweightCollection(previous) {
|
|
|
213
226
|
publicFields: Object.keys(previous.schema.properties),
|
|
214
227
|
};
|
|
215
228
|
}
|
|
229
|
+
/** Optional storage preserves historical records; the new application tool asks for a reply route. */
|
|
230
|
+
function followUpCollection(previous) {
|
|
231
|
+
return {
|
|
232
|
+
...previous,
|
|
233
|
+
...collection(previous.key, previous.labels, {
|
|
234
|
+
...previous.schema,
|
|
235
|
+
properties: {
|
|
236
|
+
...previous.schema.properties,
|
|
237
|
+
contact_email: { type: 'string', format: 'email', minLength: 3, maxLength: 254 },
|
|
238
|
+
},
|
|
239
|
+
}, previous.schemaVersion + 1),
|
|
240
|
+
publicFields: [...(previous.publicFields ?? []), 'contact_email'],
|
|
241
|
+
fields: {
|
|
242
|
+
contact_email: {
|
|
243
|
+
label: 'Reply email',
|
|
244
|
+
help: 'The address provided by the guest for staff follow-up. This address has not been verified.',
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
216
249
|
function validateJsonSchema(schema, payload) {
|
|
217
250
|
for (const required of schema.required) {
|
|
218
251
|
if (!(required in payload)) {
|
|
@@ -236,6 +269,9 @@ function validateJsonSchema(schema, payload) {
|
|
|
236
269
|
if (property.enum !== undefined && !property.enum.includes(value)) {
|
|
237
270
|
throw new PayloadValidationError('invalid_json', `managed payload field "${key}" has an unsupported value`);
|
|
238
271
|
}
|
|
272
|
+
if (property.format !== undefined && validateSchema(property, value).length > 0) {
|
|
273
|
+
throw new PayloadValidationError('invalid_json', `managed payload field "${key}" has an invalid format`);
|
|
274
|
+
}
|
|
239
275
|
}
|
|
240
276
|
}
|
|
241
277
|
function stableJson(value) {
|
|
@@ -27,7 +27,10 @@ export function buildCredentialBindingIndex(artifact) {
|
|
|
27
27
|
return [];
|
|
28
28
|
return source.scan.resolved ? [source.scan] : [];
|
|
29
29
|
});
|
|
30
|
-
const operationRefs = [
|
|
30
|
+
const operationRefs = nestedReferences([
|
|
31
|
+
...fulfilments.flatMap(resolvedOperationRefs),
|
|
32
|
+
...sourceRefs,
|
|
33
|
+
]);
|
|
31
34
|
for (const ref of operationRefs) {
|
|
32
35
|
const credentialBinding = ref.credentialBinding;
|
|
33
36
|
if (credentialBinding === undefined)
|
|
@@ -67,4 +70,19 @@ export function buildCredentialBindingIndex(artifact) {
|
|
|
67
70
|
googleWorkloadIdentity: [...googleWorkloadIdentity.values()],
|
|
68
71
|
};
|
|
69
72
|
}
|
|
73
|
+
function nestedReferences(roots) {
|
|
74
|
+
const output = [];
|
|
75
|
+
const queue = roots.map((ref) => ({ ref, depth: 0 }));
|
|
76
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
77
|
+
const entry = queue[index];
|
|
78
|
+
if (!entry)
|
|
79
|
+
continue;
|
|
80
|
+
if (entry.depth > 32 || queue.length > 65536)
|
|
81
|
+
throw new Error('Invalid compiled connector dependency graph');
|
|
82
|
+
output.push(entry.ref);
|
|
83
|
+
for (const child of entry.ref.calls ?? [])
|
|
84
|
+
queue.push({ ref: child, depth: entry.depth + 1 });
|
|
85
|
+
}
|
|
86
|
+
return output;
|
|
87
|
+
}
|
|
70
88
|
//# sourceMappingURL=credential-binding-index.js.map
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { usesCustomerAuthentication } from '@noodle-borg/module';
|
|
2
|
+
/** Match durable ownership, including an omitted stored projection when compilation declares auth. */
|
|
3
|
+
export function requiresCustomerAuthProjection(record, compiledAuth) {
|
|
4
|
+
return usesCustomerAuthentication({
|
|
5
|
+
schemaVersion: record.schemaVersion,
|
|
6
|
+
accessMode: record.accessMode,
|
|
7
|
+
hasServerAuth: record.serverAuth != null || compiledAuth !== undefined,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
1
10
|
/** Safe lifecycle error; tenant identities and configured values never enter its public message. */
|
|
2
11
|
export class CustomerAuthAudienceConflictError extends Error {
|
|
3
12
|
code = 'customer_auth_audience_conflict';
|
|
@@ -51,7 +60,7 @@ export function isTenantAuthConfig(value) {
|
|
|
51
60
|
}
|
|
52
61
|
/** Customer-serving records must exactly project the compiler-authoritative auth declaration. */
|
|
53
62
|
export function hasExactCustomerAuthProjection(record, compiledAuth) {
|
|
54
|
-
if (record
|
|
63
|
+
if (!requiresCustomerAuthProjection(record, compiledAuth))
|
|
55
64
|
return true;
|
|
56
65
|
if (compiledAuth === undefined || !isTenantAuthConfig(record.serverAuth))
|
|
57
66
|
return false;
|
|
@@ -65,7 +74,7 @@ export function hasExactCustomerAuthProjection(record, compiledAuth) {
|
|
|
65
74
|
}
|
|
66
75
|
/** Require an exact compiler-authenticated snapshot before clearing customer app archive stamps. */
|
|
67
76
|
export function assertCustomerAuthRestorePrecondition(records, precondition) {
|
|
68
|
-
const candidates = records.filter((record) => record.active && record
|
|
77
|
+
const candidates = records.filter((record) => record.active && requiresCustomerAuthProjection(record));
|
|
69
78
|
const projections = precondition?.customerAuthProjections ?? [];
|
|
70
79
|
if (candidates.length !== projections.length) {
|
|
71
80
|
throw new CustomerAuthAudienceProjectionError();
|
|
@@ -107,7 +116,7 @@ export function findActiveCustomerAuthAudienceConflict(records, tenant, auth) {
|
|
|
107
116
|
for (const record of records) {
|
|
108
117
|
if (!record.active ||
|
|
109
118
|
record.archivedAt !== undefined ||
|
|
110
|
-
record
|
|
119
|
+
!requiresCustomerAuthProjection(record) ||
|
|
111
120
|
sameTenant(record, tenant)) {
|
|
112
121
|
continue;
|
|
113
122
|
}
|
|
@@ -124,7 +133,9 @@ export function findActiveCustomerAuthAudienceConflict(records, tenant, auth) {
|
|
|
124
133
|
export function assertUniqueActiveCustomerAuthAudienceBindings(records) {
|
|
125
134
|
const owners = new Map();
|
|
126
135
|
for (const record of records) {
|
|
127
|
-
if (!record.active ||
|
|
136
|
+
if (!record.active ||
|
|
137
|
+
record.archivedAt !== undefined ||
|
|
138
|
+
!requiresCustomerAuthProjection(record)) {
|
|
128
139
|
continue;
|
|
129
140
|
}
|
|
130
141
|
if (!isTenantAuthConfig(record.serverAuth)) {
|
|
@@ -143,7 +154,7 @@ export function assertUniqueActiveCustomerAuthAudienceBindings(records) {
|
|
|
143
154
|
}
|
|
144
155
|
/** Shared registry guard with an optimized durable-store path and an in-memory fallback. */
|
|
145
156
|
export async function hasActiveCustomerAuthAudienceConflict(store, records, record, auth) {
|
|
146
|
-
if (record
|
|
157
|
+
if (!requiresCustomerAuthProjection(record, auth) || auth === undefined || auth.kind === 'bridge')
|
|
147
158
|
return false;
|
|
148
159
|
const tenant = { org: record.orgSlug, app: record.appSlug, env: record.environment };
|
|
149
160
|
const conflict = store?.findActiveCustomerAuthAudienceConflict !== undefined
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { usesCustomerAuthentication } from '@noodle-borg/module';
|
|
2
|
+
import { deploymentRecordVersionError } from './deployment-record-version.js';
|
|
3
|
+
/** Read the effective authentication authority only when this runtime understands the record. */
|
|
4
|
+
export function deploymentAuthenticationFor(record) {
|
|
5
|
+
if (deploymentRecordVersionError(record) !== undefined || record.accessMode === undefined) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
if (record.accessMode === 'public')
|
|
9
|
+
return 'none';
|
|
10
|
+
if (usesCustomerAuthentication({
|
|
11
|
+
schemaVersion: record.schemaVersion,
|
|
12
|
+
accessMode: record.accessMode,
|
|
13
|
+
hasServerAuth: record.serverAuth !== undefined,
|
|
14
|
+
})) {
|
|
15
|
+
return 'customer';
|
|
16
|
+
}
|
|
17
|
+
return 'platform';
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=deployment-authentication.js.map
|
|
@@ -266,7 +266,7 @@ export async function executeAuthorizedDeployment(input, dependencies) {
|
|
|
266
266
|
assets: hostedAssets,
|
|
267
267
|
});
|
|
268
268
|
}
|
|
269
|
-
const embedId = await provisionPublicEmbed(registry, options, tenant, result.deploymentId);
|
|
269
|
+
const embedId = await provisionPublicEmbed(registry, options, tenant, result.deploymentId, input.allowRevokedEmbedReplacement);
|
|
270
270
|
return { ok: true, result, ...(embedId === undefined ? {} : { embedId }) };
|
|
271
271
|
}
|
|
272
272
|
function reject(status, body) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sameDeploymentScope } from './deployment-versioning.js';
|
|
2
|
+
const VERSION_ERROR = {
|
|
3
|
+
code: 'unsupported_deployment_record_version',
|
|
4
|
+
path: 'schemaVersion',
|
|
5
|
+
message: 'Deployment record version is not supported by this runtime.',
|
|
6
|
+
};
|
|
7
|
+
export class UnsupportedDeploymentRecordVersionError extends Error {
|
|
8
|
+
code = VERSION_ERROR.code;
|
|
9
|
+
constructor() {
|
|
10
|
+
super(VERSION_ERROR.message);
|
|
11
|
+
this.name = 'UnsupportedDeploymentRecordVersionError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** R0 understands newer ownership records but must never serve their authentication semantics. */
|
|
15
|
+
export function deploymentRecordVersionError(record) {
|
|
16
|
+
return record.schemaVersion === 1 ? undefined : VERSION_ERROR;
|
|
17
|
+
}
|
|
18
|
+
/** A stale writer cannot replace or deactivate a newer policy, even after registry preflight. */
|
|
19
|
+
export function assertDeploymentAppendVersion(records, candidate) {
|
|
20
|
+
if (records.some((record) => record.schemaVersion > candidate.schemaVersion &&
|
|
21
|
+
(record.deploymentId === candidate.deploymentId ||
|
|
22
|
+
(candidate.active &&
|
|
23
|
+
record.active &&
|
|
24
|
+
record.archivedAt === undefined &&
|
|
25
|
+
sameDeploymentScope(record, candidate)))))
|
|
26
|
+
throw new UnsupportedDeploymentRecordVersionError();
|
|
27
|
+
}
|
|
28
|
+
export function unsupportedDeploymentRecordFailure() {
|
|
29
|
+
return { ok: false, errors: [VERSION_ERROR] };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=deployment-record-version.js.map
|
|
@@ -8,7 +8,21 @@ export async function inspectServiceDeployment(registry, tenant, baseUrl, endpoi
|
|
|
8
8
|
return {
|
|
9
9
|
ok: true,
|
|
10
10
|
target: status.target,
|
|
11
|
-
|
|
11
|
+
// Keep the Developer MCP port projection explicit as status gains additive operator fields.
|
|
12
|
+
deployment: {
|
|
13
|
+
deploymentId: status.deployment.deploymentId,
|
|
14
|
+
endpointUrl: status.deployment.endpointUrl,
|
|
15
|
+
active: status.deployment.active,
|
|
16
|
+
serverName: status.deployment.serverName,
|
|
17
|
+
createdAt: status.deployment.createdAt,
|
|
18
|
+
...(status.deployment.createdByEmail === undefined
|
|
19
|
+
? {}
|
|
20
|
+
: { createdByEmail: status.deployment.createdByEmail }),
|
|
21
|
+
accessMode: status.deployment.accessMode,
|
|
22
|
+
...(status.deployment.ownerSubject === undefined
|
|
23
|
+
? {}
|
|
24
|
+
: { ownerSubject: status.deployment.ownerSubject }),
|
|
25
|
+
},
|
|
12
26
|
health: {
|
|
13
27
|
state: status.health.state,
|
|
14
28
|
missingSecrets: status.config.missingSecrets,
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createHash, createHmac, randomUUID } from 'node:crypto';
|
|
2
|
+
import { canonicalJson } from '@noodle-borg/compiler';
|
|
3
|
+
/** The same opaque resource identity is used by ordinary dispatch and reviewed custody migrations. */
|
|
4
|
+
export function deriveOperationCoordinationResource(input) {
|
|
5
|
+
if (typeof input.identityKey !== 'string' ||
|
|
6
|
+
input.identityKey.length < 32 ||
|
|
7
|
+
!/^[A-Za-z0-9_-]{1,128}$/.test(input.connectionId) ||
|
|
8
|
+
!/^[A-Za-z0-9_-]{1,80}$/.test(input.namespace) ||
|
|
9
|
+
typeof input.key !== 'string' ||
|
|
10
|
+
input.key.length < 1 ||
|
|
11
|
+
input.key.length > 2048 ||
|
|
12
|
+
![input.scope.org, input.scope.app, input.scope.env, input.scope.installationId].every((value) => typeof value === 'string' && value.length > 0 && value.length <= 256))
|
|
13
|
+
throw new Error('Invalid coordination resource authority');
|
|
14
|
+
return createHmac('sha256', input.identityKey)
|
|
15
|
+
.update(canonicalJson({
|
|
16
|
+
scope: input.scope,
|
|
17
|
+
connectionId: input.connectionId,
|
|
18
|
+
namespace: input.namespace,
|
|
19
|
+
key: input.key,
|
|
20
|
+
}))
|
|
21
|
+
.digest('hex');
|
|
22
|
+
}
|
|
23
|
+
/** No business payloads, provider-specific logic, automatic retries or time-based unlocks. */
|
|
24
|
+
export function createOperationCoordinationPort(options) {
|
|
25
|
+
if (options.identityKey.length < 32 || !/^[A-Za-z0-9_-]{16,128}$/.test(options.epoch))
|
|
26
|
+
throw new Error('Invalid coordination authority configuration');
|
|
27
|
+
const now = () => options.now?.() ?? Date.now();
|
|
28
|
+
const authority = async (intent, generation) => {
|
|
29
|
+
const current = options.connectionGeneration(intent.connectionId);
|
|
30
|
+
if (!current ||
|
|
31
|
+
(generation !== undefined && current !== generation) ||
|
|
32
|
+
!(await options.authorize()))
|
|
33
|
+
throw new Error('Original operation authority unavailable');
|
|
34
|
+
return current;
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
async acquire(intent) {
|
|
38
|
+
validateIntent(intent);
|
|
39
|
+
const generation = await authority(intent);
|
|
40
|
+
// Deliberately excludes deployment, connection generation and execution epoch: replacing an
|
|
41
|
+
// artifact or reconnecting cannot bypass a held resource. Tenant/app/environment remain isolated.
|
|
42
|
+
const resource = deriveOperationCoordinationResource({
|
|
43
|
+
scope: options.scope,
|
|
44
|
+
connectionId: intent.connectionId,
|
|
45
|
+
namespace: intent.namespace,
|
|
46
|
+
key: intent.key,
|
|
47
|
+
identityKey: options.identityKey,
|
|
48
|
+
});
|
|
49
|
+
const startedAt = now();
|
|
50
|
+
const record = {
|
|
51
|
+
scope: options.scope,
|
|
52
|
+
resource,
|
|
53
|
+
token: randomUUID(),
|
|
54
|
+
epoch: options.epoch,
|
|
55
|
+
generation,
|
|
56
|
+
reference: intent.reference,
|
|
57
|
+
operationDigest: createHash('sha256').update(intent.id).digest('hex'),
|
|
58
|
+
startedAt,
|
|
59
|
+
deadline: startedAt + intent.executionBoundMs,
|
|
60
|
+
state: 'executing',
|
|
61
|
+
};
|
|
62
|
+
const claimed = await options.store.claim(record);
|
|
63
|
+
const previous = claimed.previous;
|
|
64
|
+
const recoverable = !claimed.acquired &&
|
|
65
|
+
previous !== undefined &&
|
|
66
|
+
previous.generation === generation &&
|
|
67
|
+
previous.epoch === options.epoch &&
|
|
68
|
+
(previous.state === 'unknown' || previous.deadline <= startedAt);
|
|
69
|
+
let resolved = false;
|
|
70
|
+
let finished = false;
|
|
71
|
+
return {
|
|
72
|
+
acquired: claimed.acquired,
|
|
73
|
+
...(recoverable
|
|
74
|
+
? {
|
|
75
|
+
previous: {
|
|
76
|
+
reference: previous.reference,
|
|
77
|
+
operationDigest: previous.operationDigest,
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
: {}),
|
|
81
|
+
async finish(evidence) {
|
|
82
|
+
if (!claimed.acquired || finished)
|
|
83
|
+
return;
|
|
84
|
+
finished = true;
|
|
85
|
+
// No expiry authorizes a retry. Only proven completion or proof of no dispatched effect
|
|
86
|
+
// releases custody; an outer success message without explicit evidence remains unknown.
|
|
87
|
+
if (evidence.outcome === 'completed' || evidence.outcome === 'rejected') {
|
|
88
|
+
await options.store.release(resource, record.token, evidence.outcome);
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
await options.store.markUnknown(resource, record.token);
|
|
92
|
+
},
|
|
93
|
+
async resolvePrevious() {
|
|
94
|
+
if (!recoverable || !previous || resolved)
|
|
95
|
+
throw new Error('Prior operation cannot be resolved');
|
|
96
|
+
await authority(intent, generation);
|
|
97
|
+
if (now() > record.deadline)
|
|
98
|
+
throw new Error('Recovery execution expired');
|
|
99
|
+
if (!(await options.store.release(resource, previous.token, 'source_verified')))
|
|
100
|
+
throw new Error('Prior operation changed before resolution');
|
|
101
|
+
resolved = true;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function validateIntent(intent) {
|
|
108
|
+
if (!/^[a-f0-9]{64}$/.test(intent.id) ||
|
|
109
|
+
!/^[A-Za-z0-9_-]{1,128}$/.test(intent.connectionId) ||
|
|
110
|
+
!/^[A-Za-z0-9_-]{1,80}$/.test(intent.namespace) ||
|
|
111
|
+
typeof intent.key !== 'string' ||
|
|
112
|
+
intent.key.length < 1 ||
|
|
113
|
+
intent.key.length > 2048 ||
|
|
114
|
+
typeof intent.reference !== 'string' ||
|
|
115
|
+
!/^[A-Za-z0-9._:@/-]{1,256}$/.test(intent.reference) ||
|
|
116
|
+
intent.reference.includes('://') ||
|
|
117
|
+
!Number.isSafeInteger(intent.executionBoundMs) ||
|
|
118
|
+
intent.executionBoundMs < 1 ||
|
|
119
|
+
intent.executionBoundMs > 120000)
|
|
120
|
+
throw new Error('Invalid coordination intent');
|
|
121
|
+
}
|
|
122
|
+
/** Explicit local/test composition; hosted production supplies the PostgreSQL implementation. */
|
|
123
|
+
export class InMemoryOperationCoordinationStore {
|
|
124
|
+
now;
|
|
125
|
+
records = new Map();
|
|
126
|
+
receipts = [];
|
|
127
|
+
constructor(now = Date.now) {
|
|
128
|
+
this.now = now;
|
|
129
|
+
}
|
|
130
|
+
async claim(record) {
|
|
131
|
+
const previous = this.records.get(record.resource);
|
|
132
|
+
if (previous && canonicalJson(previous.scope) !== canonicalJson(record.scope))
|
|
133
|
+
throw new Error('Coordination scope mismatch');
|
|
134
|
+
if (previous)
|
|
135
|
+
return { acquired: false, previous: structuredClone(previous) };
|
|
136
|
+
this.records.set(record.resource, structuredClone(record));
|
|
137
|
+
return { acquired: true };
|
|
138
|
+
}
|
|
139
|
+
async markUnknown(resource, token) {
|
|
140
|
+
const record = this.records.get(resource);
|
|
141
|
+
if (record?.token === token)
|
|
142
|
+
this.records.set(resource, { ...record, state: 'unknown' });
|
|
143
|
+
}
|
|
144
|
+
async release(resource, token, resolution) {
|
|
145
|
+
if (!['completed', 'rejected', 'source_verified'].includes(resolution))
|
|
146
|
+
throw new Error('Invalid coordination resolution');
|
|
147
|
+
const record = this.records.get(resource);
|
|
148
|
+
if (!record || record.token !== token)
|
|
149
|
+
return false;
|
|
150
|
+
if (resolution === 'source_verified')
|
|
151
|
+
this.receipts.push({ record: structuredClone(record), resolution, resolvedAt: this.now() });
|
|
152
|
+
return this.records.delete(resource);
|
|
153
|
+
}
|
|
154
|
+
async list(scope, limit = 100, beforeResource) {
|
|
155
|
+
if (!Number.isSafeInteger(limit) ||
|
|
156
|
+
limit < 1 ||
|
|
157
|
+
limit > 100 ||
|
|
158
|
+
(beforeResource !== undefined && !/^[a-f0-9]{64}$/.test(beforeResource)))
|
|
159
|
+
throw new Error('Invalid coordination paging');
|
|
160
|
+
return [...this.records.values()]
|
|
161
|
+
.filter((record) => canonicalJson(record.scope) === canonicalJson(scope) &&
|
|
162
|
+
(!beforeResource || record.resource > beforeResource))
|
|
163
|
+
.sort((a, b) => a.resource.localeCompare(b.resource))
|
|
164
|
+
.slice(0, limit)
|
|
165
|
+
.map((record) => structuredClone(record));
|
|
166
|
+
}
|
|
167
|
+
async resolve(scope, resource, token, review) {
|
|
168
|
+
if (![review.reviewer, review.reason].every((value) => typeof value === 'string' && value.trim().length > 0 && value.length <= 256))
|
|
169
|
+
throw new Error('Invalid coordination review');
|
|
170
|
+
const record = this.records.get(resource);
|
|
171
|
+
if (!record ||
|
|
172
|
+
record.token !== token ||
|
|
173
|
+
canonicalJson(record.scope) !== canonicalJson(scope) ||
|
|
174
|
+
(record.state === 'executing' && record.deadline > this.now()))
|
|
175
|
+
return false;
|
|
176
|
+
this.receipts.push({
|
|
177
|
+
record: structuredClone(record),
|
|
178
|
+
resolution: 'reviewed',
|
|
179
|
+
...review,
|
|
180
|
+
resolvedAt: this.now(),
|
|
181
|
+
});
|
|
182
|
+
return this.records.delete(resource);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=operation-coordination.js.map
|
|
@@ -20,13 +20,14 @@ export function createOperationEvidencePort(options) {
|
|
|
20
20
|
bound >= days * 86_400_000 ||
|
|
21
21
|
!(await options.authorize(intent)))
|
|
22
22
|
throw new Error('Operation evidence authority unavailable');
|
|
23
|
-
const connectionId = intent.operation.credentialBinding?.connectionId;
|
|
23
|
+
const connectionId = intent.operation.credentialBinding?.connectionId ?? intent.connectionId;
|
|
24
24
|
const generation = connectionId === undefined ? undefined : options.connectionGeneration(connectionId);
|
|
25
25
|
if (connectionId !== undefined && generation === undefined)
|
|
26
26
|
throw new Error('Original connected account unavailable');
|
|
27
27
|
const record = {
|
|
28
28
|
scope: options.scope,
|
|
29
29
|
id: intent.id,
|
|
30
|
+
...(intent.parentId === undefined ? {} : { parentId: intent.parentId }),
|
|
30
31
|
lease: randomUUID(),
|
|
31
32
|
epoch: options.epoch,
|
|
32
33
|
deploymentId: options.deploymentId,
|
|
@@ -44,6 +45,7 @@ export function createOperationEvidencePort(options) {
|
|
|
44
45
|
input: intent.arguments,
|
|
45
46
|
caller: intent.caller ?? null,
|
|
46
47
|
revision: intent.executionRevision ?? null,
|
|
48
|
+
parentId: intent.parentId ?? null,
|
|
47
49
|
generation: generation ?? null,
|
|
48
50
|
}),
|
|
49
51
|
startedAt,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { assertUniqueActiveCustomerAuthAudienceBindings, CustomerAuthAudienceConflictError, } from './customer-auth-audience-binding.js';
|
|
1
|
+
import { assertUniqueActiveCustomerAuthAudienceBindings, CustomerAuthAudienceConflictError, requiresCustomerAuthProjection, } from './customer-auth-audience-binding.js';
|
|
2
|
+
import { deploymentRecordVersionError } from './deployment-record-version.js';
|
|
2
3
|
import { deploymentOwnerSubject } from './registry-helpers.js';
|
|
3
4
|
import { activeRecord, activeRecordVersion } from './registry-state.js';
|
|
4
5
|
import { tenantDeploymentKey, tryServedTargetFor } from './registry-targets.js';
|
|
@@ -41,6 +42,15 @@ export async function updateActiveAccess(state, tenant, options, loadServed, cus
|
|
|
41
42
|
: await activeRecordVersion(state, tenant, options.serverVersion);
|
|
42
43
|
if (record === undefined)
|
|
43
44
|
return noActiveDeployment();
|
|
45
|
+
const versionError = deploymentRecordVersionError(record);
|
|
46
|
+
if (versionError !== undefined) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
status: 409,
|
|
50
|
+
code: 'unsupported_deployment_record_version',
|
|
51
|
+
message: versionError.message,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
44
54
|
const nextAccessMode = options.accessMode;
|
|
45
55
|
const previousAccessMode = record.accessMode ?? 'owner-only';
|
|
46
56
|
const previousOwnerSubject = deploymentOwnerSubject(record);
|
|
@@ -64,12 +74,14 @@ export async function updateActiveAccess(state, tenant, options, loadServed, cus
|
|
|
64
74
|
? commitInMemoryAccessUpdate(state, tenant, record, {
|
|
65
75
|
accessMode: nextAccessMode,
|
|
66
76
|
expectedAccessMode: record.accessMode,
|
|
77
|
+
expectedSchemaVersion: record.schemaVersion,
|
|
67
78
|
expectedOwnerSubject: previousOwnerSubject,
|
|
68
79
|
...(serverAuth !== undefined ? { serverAuth } : {}),
|
|
69
80
|
})
|
|
70
81
|
: await state.store.updateActiveAccess(tenant, record.deploymentId, {
|
|
71
82
|
accessMode: nextAccessMode,
|
|
72
83
|
expectedAccessMode: record.accessMode,
|
|
84
|
+
expectedSchemaVersion: record.schemaVersion,
|
|
73
85
|
expectedOwnerSubject: previousOwnerSubject,
|
|
74
86
|
...(serverAuth !== undefined ? { serverAuth } : {}),
|
|
75
87
|
});
|
|
@@ -120,6 +132,7 @@ export async function updateActiveAccess(state, tenant, options, loadServed, cus
|
|
|
120
132
|
? { ownerSubject: nextOwnerSubject }
|
|
121
133
|
: {}),
|
|
122
134
|
expectedAccessMode: record.accessMode,
|
|
135
|
+
expectedSchemaVersion: record.schemaVersion,
|
|
123
136
|
expectedOwnerSubject: previousOwnerSubject,
|
|
124
137
|
...(serverAuth !== undefined ? { serverAuth } : {}),
|
|
125
138
|
})
|
|
@@ -129,6 +142,7 @@ export async function updateActiveAccess(state, tenant, options, loadServed, cus
|
|
|
129
142
|
? { ownerSubject: nextOwnerSubject }
|
|
130
143
|
: {}),
|
|
131
144
|
expectedAccessMode: record.accessMode,
|
|
145
|
+
expectedSchemaVersion: record.schemaVersion,
|
|
132
146
|
expectedOwnerSubject: previousOwnerSubject,
|
|
133
147
|
...(serverAuth !== undefined ? { serverAuth } : {}),
|
|
134
148
|
});
|
|
@@ -155,19 +169,20 @@ function commitInMemoryAccessUpdate(state, tenant, observed, input) {
|
|
|
155
169
|
current.appSlug !== tenant.app ||
|
|
156
170
|
current.environment !== tenant.env ||
|
|
157
171
|
current.accessMode !== input.expectedAccessMode ||
|
|
172
|
+
current.schemaVersion !== (input.expectedSchemaVersion ?? 1) ||
|
|
158
173
|
deploymentOwnerSubject(current) !== input.expectedOwnerSubject) {
|
|
159
174
|
return undefined;
|
|
160
175
|
}
|
|
161
176
|
if ((current.accessMode ?? 'owner-only') === input.accessMode &&
|
|
162
177
|
(input.ownerSubject === undefined || deploymentOwnerSubject(current) === input.ownerSubject) &&
|
|
163
|
-
input.accessMode
|
|
178
|
+
!requiresCustomerAuthProjection({ ...current, accessMode: input.accessMode }, input.serverAuth)) {
|
|
164
179
|
return current;
|
|
165
180
|
}
|
|
166
181
|
const committed = {
|
|
167
182
|
...current,
|
|
168
183
|
accessMode: input.accessMode,
|
|
169
184
|
...(input.ownerSubject !== undefined ? { ownerSubject: input.ownerSubject } : {}),
|
|
170
|
-
...(input.accessMode
|
|
185
|
+
...(requiresCustomerAuthProjection({ ...current, accessMode: input.accessMode }, input.serverAuth) && input.serverAuth !== undefined
|
|
171
186
|
? { serverAuth: input.serverAuth }
|
|
172
187
|
: {}),
|
|
173
188
|
};
|
|
@@ -4,12 +4,24 @@ import { compileConnectors, delegatedTokenExchangeIdentityErrors, } from '@noodl
|
|
|
4
4
|
import { InMemoryConnectorRegistry, resolveManagedOrigins, resolveVariableEnvironment, } from '@noodle-borg/runtime';
|
|
5
5
|
import { AppPackageSnapshotError, createAppPackageSnapshot, } from './app-package-snapshot.js';
|
|
6
6
|
import { missingServerConfigErrors } from './assistant-bindings.js';
|
|
7
|
+
import { normalizePersistedConnectorsForCompile } from './connector-normalize.js';
|
|
7
8
|
import { ManagedConfigBroker } from './credential-broker.js';
|
|
8
9
|
import { deploymentCredentialBrokerOptions } from './credential-broker-options.js';
|
|
10
|
+
import { deploymentRecordVersionError } from './deployment-record-version.js';
|
|
9
11
|
import { normalizePersistedManifestForCompile } from './manifest-normalize.js';
|
|
10
|
-
import { missingSecretErrors, missingVariableErrors } from './registry-helpers.js';
|
|
12
|
+
import { missingCapabilityErrors, missingSecretErrors, missingVariableErrors, } from './registry-helpers.js';
|
|
13
|
+
import { servedTargetFor } from './registry-targets.js';
|
|
11
14
|
import { createDeploymentStateConnector, } from './state-connector-factory.js';
|
|
12
15
|
import { resolveConfigScope, } from './store.js';
|
|
16
|
+
/** Reject unsupported persisted semantics before constructing executable runtime dependencies. */
|
|
17
|
+
export async function compilePersistedRegistryRecord(record, compileTarget) {
|
|
18
|
+
const versionError = deploymentRecordVersionError(record);
|
|
19
|
+
if (versionError !== undefined)
|
|
20
|
+
return { ok: false, errors: [versionError] };
|
|
21
|
+
return compileTarget({ org: record.orgSlug, app: record.appSlug, env: record.environment }, record.manifest, record.connectors === undefined
|
|
22
|
+
? undefined
|
|
23
|
+
: normalizePersistedConnectorsForCompile(record.connectors), record.hostedAssets, false, record.deploymentId);
|
|
24
|
+
}
|
|
13
25
|
export async function compileRegistryTarget(context, input) {
|
|
14
26
|
const { tenant, manifest, connectors, hostedAssets, deploymentId } = input;
|
|
15
27
|
let catalogConnectors = [];
|
|
@@ -216,4 +228,33 @@ export async function compileRegistryTarget(context, input) {
|
|
|
216
228
|
...(appPackageSnapshot !== undefined ? { appPackageSnapshot } : {}),
|
|
217
229
|
};
|
|
218
230
|
}
|
|
231
|
+
/** Load and validate persisted semantics before publishing an executable target to caches. */
|
|
232
|
+
export async function loadPersistedRegistryTarget(source, context) {
|
|
233
|
+
const { state } = context;
|
|
234
|
+
const record = typeof source === 'string'
|
|
235
|
+
? state.store
|
|
236
|
+
? await state.store.get(source)
|
|
237
|
+
: state.records.get(source)
|
|
238
|
+
: source;
|
|
239
|
+
if (!record ||
|
|
240
|
+
record.archivedAt !== undefined ||
|
|
241
|
+
deploymentRecordVersionError(record) !== undefined)
|
|
242
|
+
return undefined;
|
|
243
|
+
const deploymentId = record.deploymentId;
|
|
244
|
+
const built = await context.compile(record);
|
|
245
|
+
if (!built.ok) {
|
|
246
|
+
throw new Error(`deployment ${deploymentId} failed to recompile (${built.errors.map((e) => e.code).join(',')})`);
|
|
247
|
+
}
|
|
248
|
+
const missingCapabilities = missingCapabilityErrors(built.served.artifact.requirements?.capabilities ?? [], context.capabilities);
|
|
249
|
+
if (missingCapabilities.length > 0) {
|
|
250
|
+
throw new Error(`deployment ${deploymentId} failed capability requirements (${missingCapabilities.map((e) => e.path).join(',')})`);
|
|
251
|
+
}
|
|
252
|
+
if (record.active &&
|
|
253
|
+
(await context.hasCustomerAuthConflict(record, built.served.artifact.server.auth)))
|
|
254
|
+
return undefined;
|
|
255
|
+
const target = servedTargetFor(record, built.served, context.customerVerifierFactory);
|
|
256
|
+
state.servers.set(deploymentId, target);
|
|
257
|
+
state.records.set(deploymentId, record);
|
|
258
|
+
return target;
|
|
259
|
+
}
|
|
219
260
|
//# sourceMappingURL=registry-compile.js.map
|