@granular-software/sdk 0.4.64 → 0.4.65
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/agent-evals.d.mts +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +679 -53
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +679 -53
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +679 -53
- package/dist/{client-IQeOoPnO.d.mts → client-S-z8bri1.d.mts} +47 -1
- package/dist/{client-BziqnDTl.d.ts → client-z6shQivt.d.ts} +47 -1
- package/dist/index.d.mts +72 -5
- package/dist/index.d.ts +72 -5
- package/dist/index.js +1024 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1018 -72
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-CDLmk-KW.d.mts → spend-CESUvrZC.d.mts} +227 -1
- package/dist/{spend-CDLmk-KW.d.ts → spend-CESUvrZC.d.ts} +227 -1
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +1 -1
package/dist/agent-evals.js
CHANGED
|
@@ -6534,6 +6534,11 @@ var Session = class {
|
|
|
6534
6534
|
}
|
|
6535
6535
|
}
|
|
6536
6536
|
buildDirectedInvocationEffectContext(params, feedbackContext) {
|
|
6537
|
+
const commitUnavailable = async () => {
|
|
6538
|
+
throw new Error(
|
|
6539
|
+
"This directed browser tool invocation has no product commit transport"
|
|
6540
|
+
);
|
|
6541
|
+
};
|
|
6537
6542
|
return {
|
|
6538
6543
|
effectClientId: this.clientId,
|
|
6539
6544
|
sandboxId: params.sandboxId || "",
|
|
@@ -6546,6 +6551,10 @@ var Session = class {
|
|
|
6546
6551
|
userId: "",
|
|
6547
6552
|
subjectId: ""
|
|
6548
6553
|
},
|
|
6554
|
+
commit: {
|
|
6555
|
+
effect: commitUnavailable,
|
|
6556
|
+
transition: commitUnavailable
|
|
6557
|
+
},
|
|
6549
6558
|
...feedbackContext ? {
|
|
6550
6559
|
feedback: feedbackContext.feedback,
|
|
6551
6560
|
transientFeedback: feedbackContext.transientFeedback
|
|
@@ -12317,6 +12326,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12317
12326
|
summary: external_exports.string().optional()
|
|
12318
12327
|
}).strict()
|
|
12319
12328
|
]);
|
|
12329
|
+
var StateTransitionOutcomeSchema = external_exports.object({
|
|
12330
|
+
label: external_exports.string().min(1).optional(),
|
|
12331
|
+
to: external_exports.string().min(1),
|
|
12332
|
+
primary: external_exports.boolean().optional(),
|
|
12333
|
+
disposition: external_exports.enum(["continue", "error"])
|
|
12334
|
+
}).strict();
|
|
12320
12335
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12321
12336
|
name: external_exports.string().min(1),
|
|
12322
12337
|
from: external_exports.string().min(1),
|
|
@@ -12328,7 +12343,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12328
12343
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12329
12344
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12330
12345
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12331
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12346
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12347
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12332
12348
|
}).strict();
|
|
12333
12349
|
external_exports.object({
|
|
12334
12350
|
name: external_exports.string().min(1),
|
|
@@ -13009,6 +13025,238 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
13009
13025
|
}
|
|
13010
13026
|
});
|
|
13011
13027
|
|
|
13028
|
+
// src/commit.ts
|
|
13029
|
+
var MAX_PROJECTION_CHANGES = 1e3;
|
|
13030
|
+
var MAX_SAFE_SUMMARY_KEYS = 32;
|
|
13031
|
+
var MAX_SAFE_TEXT_LENGTH = 2e3;
|
|
13032
|
+
var definedTransitionMetadata = /* @__PURE__ */ new WeakMap();
|
|
13033
|
+
function getDefinedTransitionMetadata(transition) {
|
|
13034
|
+
return definedTransitionMetadata.get(transition);
|
|
13035
|
+
}
|
|
13036
|
+
function requireNonEmptyString(value, path2) {
|
|
13037
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13038
|
+
throw new Error(`${path2} must be a non-empty string`);
|
|
13039
|
+
}
|
|
13040
|
+
return value.trim();
|
|
13041
|
+
}
|
|
13042
|
+
function validateObjectReference(value, path2) {
|
|
13043
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13044
|
+
throw new Error(`${path2} must be an object reference`);
|
|
13045
|
+
}
|
|
13046
|
+
const reference = value;
|
|
13047
|
+
requireNonEmptyString(reference.className, `${path2}.className`);
|
|
13048
|
+
requireNonEmptyString(reference.id, `${path2}.id`);
|
|
13049
|
+
if (reference.path !== void 0) {
|
|
13050
|
+
requireNonEmptyString(reference.path, `${path2}.path`);
|
|
13051
|
+
}
|
|
13052
|
+
}
|
|
13053
|
+
function validateScalarRecord(value, path2) {
|
|
13054
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
13055
|
+
throw new Error(`${path2} must be an object of scalar values`);
|
|
13056
|
+
}
|
|
13057
|
+
for (const [key, item] of Object.entries(value)) {
|
|
13058
|
+
if (item !== null && typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean") {
|
|
13059
|
+
throw new Error(
|
|
13060
|
+
`${path2}.${key} must be a string, finite number, boolean, or null`
|
|
13061
|
+
);
|
|
13062
|
+
}
|
|
13063
|
+
if (typeof item === "number" && !Number.isFinite(item)) {
|
|
13064
|
+
throw new Error(`${path2}.${key} must be finite`);
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
13067
|
+
}
|
|
13068
|
+
function validateProjectedRecord(value, path2) {
|
|
13069
|
+
validateObjectReference(value, path2);
|
|
13070
|
+
const record = value;
|
|
13071
|
+
if (record.label !== void 0) {
|
|
13072
|
+
if (typeof record.label !== "string") {
|
|
13073
|
+
throw new Error(`${path2}.label must be a string`);
|
|
13074
|
+
}
|
|
13075
|
+
if (record.label.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13076
|
+
throw new Error(
|
|
13077
|
+
`${path2}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
|
|
13078
|
+
);
|
|
13079
|
+
}
|
|
13080
|
+
}
|
|
13081
|
+
validateScalarRecord(record.fields, `${path2}.fields`);
|
|
13082
|
+
}
|
|
13083
|
+
function validateBoundedJson(value, path2, depth = 0) {
|
|
13084
|
+
if (depth > 12) throw new Error(`${path2} is nested too deeply`);
|
|
13085
|
+
if (value === null || typeof value === "boolean") return;
|
|
13086
|
+
if (typeof value === "number") {
|
|
13087
|
+
if (!Number.isFinite(value)) throw new Error(`${path2} must be finite`);
|
|
13088
|
+
return;
|
|
13089
|
+
}
|
|
13090
|
+
if (typeof value === "string") {
|
|
13091
|
+
if (value.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13092
|
+
throw new Error(`${path2} exceeds ${MAX_SAFE_TEXT_LENGTH} characters`);
|
|
13093
|
+
}
|
|
13094
|
+
return;
|
|
13095
|
+
}
|
|
13096
|
+
if (Array.isArray(value)) {
|
|
13097
|
+
if (value.length > MAX_PROJECTION_CHANGES) {
|
|
13098
|
+
throw new Error(`${path2} contains too many values`);
|
|
13099
|
+
}
|
|
13100
|
+
value.forEach(
|
|
13101
|
+
(item, index) => validateBoundedJson(item, `${path2}[${index}]`, depth + 1)
|
|
13102
|
+
);
|
|
13103
|
+
return;
|
|
13104
|
+
}
|
|
13105
|
+
if (!value || typeof value !== "object") {
|
|
13106
|
+
throw new Error(`${path2} contains an unsupported value`);
|
|
13107
|
+
}
|
|
13108
|
+
const entries = Object.entries(value);
|
|
13109
|
+
if (entries.length > 256) throw new Error(`${path2} contains too many keys`);
|
|
13110
|
+
for (const [key, item] of entries) {
|
|
13111
|
+
if (/token|secret|password|authorization|cookie/i.test(key)) {
|
|
13112
|
+
throw new Error(`${path2}.${key} is not allowed in a commit projection`);
|
|
13113
|
+
}
|
|
13114
|
+
validateBoundedJson(item, `${path2}.${key}`, depth + 1);
|
|
13115
|
+
}
|
|
13116
|
+
}
|
|
13117
|
+
function validateProjectionResult(declaration, projection) {
|
|
13118
|
+
if (!projection || typeof projection !== "object") {
|
|
13119
|
+
throw new Error("Projection mapper must return an object");
|
|
13120
|
+
}
|
|
13121
|
+
requireNonEmptyString(
|
|
13122
|
+
projection.source?.reference,
|
|
13123
|
+
"projection.source.reference"
|
|
13124
|
+
);
|
|
13125
|
+
if (projection.source.version !== void 0) {
|
|
13126
|
+
requireNonEmptyString(
|
|
13127
|
+
projection.source.version,
|
|
13128
|
+
"projection.source.version"
|
|
13129
|
+
);
|
|
13130
|
+
}
|
|
13131
|
+
if (!Array.isArray(projection.changes)) {
|
|
13132
|
+
throw new Error("projection.changes must be an array");
|
|
13133
|
+
}
|
|
13134
|
+
if (projection.changes.length > MAX_PROJECTION_CHANGES) {
|
|
13135
|
+
throw new Error(
|
|
13136
|
+
`projection.changes exceeds the ${MAX_PROJECTION_CHANGES} change limit`
|
|
13137
|
+
);
|
|
13138
|
+
}
|
|
13139
|
+
if (projection.primaryTarget) {
|
|
13140
|
+
validateObjectReference(
|
|
13141
|
+
projection.primaryTarget,
|
|
13142
|
+
"projection.primaryTarget"
|
|
13143
|
+
);
|
|
13144
|
+
}
|
|
13145
|
+
if (projection.safeSummary) {
|
|
13146
|
+
const entries = Object.entries(projection.safeSummary);
|
|
13147
|
+
if (entries.length > MAX_SAFE_SUMMARY_KEYS) {
|
|
13148
|
+
throw new Error(
|
|
13149
|
+
`projection.safeSummary exceeds the ${MAX_SAFE_SUMMARY_KEYS} key limit`
|
|
13150
|
+
);
|
|
13151
|
+
}
|
|
13152
|
+
validateBoundedJson(projection.safeSummary, "projection.safeSummary");
|
|
13153
|
+
validateScalarRecord(projection.safeSummary, "projection.safeSummary");
|
|
13154
|
+
}
|
|
13155
|
+
projection.changes.forEach((change, index) => {
|
|
13156
|
+
const changePath = `projection.changes[${index}]`;
|
|
13157
|
+
validateBoundedJson(change, changePath);
|
|
13158
|
+
if (!change || typeof change !== "object" || Array.isArray(change)) {
|
|
13159
|
+
throw new Error(`${changePath} must be an object`);
|
|
13160
|
+
}
|
|
13161
|
+
const rawChange = change;
|
|
13162
|
+
const kind = requireNonEmptyString(rawChange.kind, `${changePath}.kind`);
|
|
13163
|
+
if (kind === "object") {
|
|
13164
|
+
const operation = requireNonEmptyString(
|
|
13165
|
+
rawChange.operation,
|
|
13166
|
+
`${changePath}.operation`
|
|
13167
|
+
);
|
|
13168
|
+
if (operation !== "created" && operation !== "updated" && operation !== "deleted") {
|
|
13169
|
+
throw new Error(
|
|
13170
|
+
`${changePath}.operation must be created, updated, or deleted`
|
|
13171
|
+
);
|
|
13172
|
+
}
|
|
13173
|
+
if (operation === "deleted") {
|
|
13174
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13175
|
+
} else {
|
|
13176
|
+
validateProjectedRecord(rawChange.record, `${changePath}.record`);
|
|
13177
|
+
}
|
|
13178
|
+
} else if (kind === "relationship") {
|
|
13179
|
+
const operation = requireNonEmptyString(
|
|
13180
|
+
rawChange.operation,
|
|
13181
|
+
`${changePath}.operation`
|
|
13182
|
+
);
|
|
13183
|
+
if (operation !== "connected" && operation !== "disconnected") {
|
|
13184
|
+
throw new Error(
|
|
13185
|
+
`${changePath}.operation must be connected or disconnected`
|
|
13186
|
+
);
|
|
13187
|
+
}
|
|
13188
|
+
requireNonEmptyString(
|
|
13189
|
+
rawChange.relationship,
|
|
13190
|
+
`${changePath}.relationship`
|
|
13191
|
+
);
|
|
13192
|
+
validateObjectReference(rawChange.from, `${changePath}.from`);
|
|
13193
|
+
validateObjectReference(rawChange.to, `${changePath}.to`);
|
|
13194
|
+
} else if (kind === "state_observation") {
|
|
13195
|
+
if (rawChange.operation !== void 0) {
|
|
13196
|
+
throw new Error(
|
|
13197
|
+
`${changePath}.operation is not valid for an observation`
|
|
13198
|
+
);
|
|
13199
|
+
}
|
|
13200
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13201
|
+
requireNonEmptyString(rawChange.machine, `${changePath}.machine`);
|
|
13202
|
+
requireNonEmptyString(rawChange.state, `${changePath}.state`);
|
|
13203
|
+
} else {
|
|
13204
|
+
throw new Error(`${changePath}.kind is unsupported: ${kind}`);
|
|
13205
|
+
}
|
|
13206
|
+
});
|
|
13207
|
+
const objectOperations = /* @__PURE__ */ new Map();
|
|
13208
|
+
for (const change of projection.changes) {
|
|
13209
|
+
if (change.kind !== "object") continue;
|
|
13210
|
+
const reference = change.operation === "deleted" ? change.target : change.record;
|
|
13211
|
+
const key = `${reference.className}\0${reference.id}`;
|
|
13212
|
+
const operations = objectOperations.get(key) || {
|
|
13213
|
+
deleted: false,
|
|
13214
|
+
upserted: false
|
|
13215
|
+
};
|
|
13216
|
+
if (change.operation === "deleted") operations.deleted = true;
|
|
13217
|
+
else operations.upserted = true;
|
|
13218
|
+
if (operations.deleted && operations.upserted) {
|
|
13219
|
+
throw new Error(
|
|
13220
|
+
`projection.changes cannot both delete and upsert ${reference.className}/${reference.id}; return only its canonical final state`
|
|
13221
|
+
);
|
|
13222
|
+
}
|
|
13223
|
+
objectOperations.set(key, operations);
|
|
13224
|
+
}
|
|
13225
|
+
const outcome = projection.outcome;
|
|
13226
|
+
if (declaration.kind === "transition") {
|
|
13227
|
+
if (!outcome) {
|
|
13228
|
+
throw new Error("A transition projection must return an outcome");
|
|
13229
|
+
}
|
|
13230
|
+
requireNonEmptyString(outcome.key, "projection.outcome.key");
|
|
13231
|
+
if (outcome.error) {
|
|
13232
|
+
validateBoundedJson(outcome.error, "projection.outcome.error");
|
|
13233
|
+
}
|
|
13234
|
+
} else if (projection.outcome !== void 0) {
|
|
13235
|
+
throw new Error("An effect projection cannot declare a transition outcome");
|
|
13236
|
+
}
|
|
13237
|
+
}
|
|
13238
|
+
function canonicalizeCommitValue(value) {
|
|
13239
|
+
const normalize = (current) => {
|
|
13240
|
+
if (current === null || typeof current === "boolean" || typeof current === "string") {
|
|
13241
|
+
return typeof current === "string" ? current.normalize("NFC") : current;
|
|
13242
|
+
}
|
|
13243
|
+
if (typeof current === "number") {
|
|
13244
|
+
if (!Number.isFinite(current)) {
|
|
13245
|
+
throw new Error("Cannot canonicalize a non-finite number");
|
|
13246
|
+
}
|
|
13247
|
+
return Object.is(current, -0) ? 0 : current;
|
|
13248
|
+
}
|
|
13249
|
+
if (Array.isArray(current)) return current.map(normalize);
|
|
13250
|
+
if (current && typeof current === "object") {
|
|
13251
|
+
return Object.fromEntries(
|
|
13252
|
+
Object.entries(current).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key.normalize("NFC"), normalize(item)])
|
|
13253
|
+
);
|
|
13254
|
+
}
|
|
13255
|
+
throw new Error(`Cannot canonicalize ${typeof current}`);
|
|
13256
|
+
};
|
|
13257
|
+
return JSON.stringify(normalize(value));
|
|
13258
|
+
}
|
|
13259
|
+
|
|
13012
13260
|
// src/effect-runtime.ts
|
|
13013
13261
|
function computeEffectKey(effect) {
|
|
13014
13262
|
const attachedClass = effect.className?.trim();
|
|
@@ -13073,6 +13321,45 @@ function resolveInvocationMode(context) {
|
|
|
13073
13321
|
}
|
|
13074
13322
|
return "execute";
|
|
13075
13323
|
}
|
|
13324
|
+
async function sha256Hex(value) {
|
|
13325
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
13326
|
+
"SHA-256",
|
|
13327
|
+
new TextEncoder().encode(value)
|
|
13328
|
+
);
|
|
13329
|
+
return Array.from(
|
|
13330
|
+
new Uint8Array(digest),
|
|
13331
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
13332
|
+
).join("");
|
|
13333
|
+
}
|
|
13334
|
+
async function resolveInvocationIdempotencyKey(request) {
|
|
13335
|
+
const supplied = request.context?.idempotencyKey?.trim();
|
|
13336
|
+
if (supplied) return supplied;
|
|
13337
|
+
const invocationId = request.context?.invocationId?.trim();
|
|
13338
|
+
if (!invocationId) {
|
|
13339
|
+
throw new Error(
|
|
13340
|
+
`Committed effect ${request.effectKey} requires an invocation id`
|
|
13341
|
+
);
|
|
13342
|
+
}
|
|
13343
|
+
const digest = await sha256Hex(
|
|
13344
|
+
canonicalizeCommitValue({
|
|
13345
|
+
sandboxId: request.context?.sandboxId || "",
|
|
13346
|
+
environmentId: request.context?.environmentId || "",
|
|
13347
|
+
effectKey: request.effectKey,
|
|
13348
|
+
invocationId,
|
|
13349
|
+
input: request.input
|
|
13350
|
+
})
|
|
13351
|
+
);
|
|
13352
|
+
return `gci_${digest}`;
|
|
13353
|
+
}
|
|
13354
|
+
function createUnavailableCommitContext(message) {
|
|
13355
|
+
const unavailable = async () => {
|
|
13356
|
+
throw new Error(message);
|
|
13357
|
+
};
|
|
13358
|
+
return {
|
|
13359
|
+
effect: unavailable,
|
|
13360
|
+
transition: unavailable
|
|
13361
|
+
};
|
|
13362
|
+
}
|
|
13076
13363
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13077
13364
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13078
13365
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13144,22 +13431,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13144
13431
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13145
13432
|
}
|
|
13146
13433
|
if (mode === "reverse") {
|
|
13434
|
+
const sourceEffectKey = request.context?.invocation?.sourceEffectKey?.trim();
|
|
13435
|
+
if (sourceEffectKey && sourceEffectKey !== request.effectKey) {
|
|
13436
|
+
return { effect, mode, handler: effect.handler };
|
|
13437
|
+
}
|
|
13438
|
+
const namedReverseHandler = request.context?.invocation?.reverseHandler?.trim() || behaviors.reverse?.handler?.trim();
|
|
13439
|
+
if (namedReverseHandler) {
|
|
13440
|
+
const reverseEffect = resolveReverseEffect(
|
|
13441
|
+
effectMap,
|
|
13442
|
+
effect,
|
|
13443
|
+
request,
|
|
13444
|
+
behaviors
|
|
13445
|
+
);
|
|
13446
|
+
if (reverseEffect) {
|
|
13447
|
+
return {
|
|
13448
|
+
effect: reverseEffect,
|
|
13449
|
+
mode,
|
|
13450
|
+
handler: reverseEffect.handler
|
|
13451
|
+
};
|
|
13452
|
+
}
|
|
13453
|
+
throw new Error(
|
|
13454
|
+
`Reverse effect ${namedReverseHandler} is not registered for ${request.effectKey}`
|
|
13455
|
+
);
|
|
13456
|
+
}
|
|
13147
13457
|
if (effect.reverseHandler) {
|
|
13148
13458
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13149
13459
|
}
|
|
13150
|
-
const reverseEffect = resolveReverseEffect(
|
|
13151
|
-
effectMap,
|
|
13152
|
-
effect,
|
|
13153
|
-
request,
|
|
13154
|
-
behaviors
|
|
13155
|
-
);
|
|
13156
|
-
if (reverseEffect) {
|
|
13157
|
-
return {
|
|
13158
|
-
effect: reverseEffect,
|
|
13159
|
-
mode,
|
|
13160
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13161
|
-
};
|
|
13162
|
-
}
|
|
13163
13460
|
throw new Error(
|
|
13164
13461
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13165
13462
|
);
|
|
@@ -13260,18 +13557,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13260
13557
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13261
13558
|
}
|
|
13262
13559
|
const resolved = resolveHandlerForMode(effectMap, effect, request);
|
|
13560
|
+
const isPreResolvedNamedReverse = resolved.mode === "reverse" && Boolean(request.context?.invocation?.sourceEffectKey?.trim()) && request.context?.invocation?.sourceEffectKey?.trim() !== request.effectKey;
|
|
13561
|
+
const resolvedEffectKey = computeEffectKey(resolved.effect);
|
|
13562
|
+
if (resolved.mode === "reverse" && resolved.effect !== effect && (effect.commit || resolved.effect.commit)) {
|
|
13563
|
+
throw new Error(
|
|
13564
|
+
`Committed named reverse effect ${resolvedEffectKey} must be reserved and dispatched using its own effect key; customer code was not invoked`
|
|
13565
|
+
);
|
|
13566
|
+
}
|
|
13567
|
+
const declaration = resolved.effect.commit;
|
|
13568
|
+
const commitTransport = options.commit;
|
|
13569
|
+
const commitRequired = Boolean(declaration) && (resolved.mode === "execute" || resolved.mode === "reverse");
|
|
13570
|
+
if (commitRequired && !commitTransport) {
|
|
13571
|
+
throw new Error(
|
|
13572
|
+
`Committed effect ${request.effectKey} has no durable commit transport; customer code was not invoked`
|
|
13573
|
+
);
|
|
13574
|
+
}
|
|
13575
|
+
if (commitRequired && declaration?.kind === "transition" && !request.context?.invocation?.transition) {
|
|
13576
|
+
throw new Error(
|
|
13577
|
+
`Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
|
|
13578
|
+
);
|
|
13579
|
+
}
|
|
13580
|
+
const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
|
|
13581
|
+
let commitStarted = false;
|
|
13582
|
+
let commitPromise = null;
|
|
13583
|
+
const beginCommit = (requestedKind, productResult) => {
|
|
13584
|
+
if (!declaration || !commitRequired || !commitTransport) {
|
|
13585
|
+
return Promise.reject(
|
|
13586
|
+
new Error(
|
|
13587
|
+
`Effect ${request.effectKey} does not declare an active ${requestedKind} commit`
|
|
13588
|
+
)
|
|
13589
|
+
);
|
|
13590
|
+
}
|
|
13591
|
+
if (declaration.kind !== requestedKind) {
|
|
13592
|
+
return Promise.reject(
|
|
13593
|
+
new Error(
|
|
13594
|
+
`Effect ${request.effectKey} declares ${declaration.kind} commit, not ${requestedKind}`
|
|
13595
|
+
)
|
|
13596
|
+
);
|
|
13597
|
+
}
|
|
13598
|
+
if (commitStarted) {
|
|
13599
|
+
return Promise.reject(
|
|
13600
|
+
new Error(
|
|
13601
|
+
`Effect invocation ${request.context?.invocationId || request.effectKey} already emitted its commit`
|
|
13602
|
+
)
|
|
13603
|
+
);
|
|
13604
|
+
}
|
|
13605
|
+
commitStarted = true;
|
|
13606
|
+
commitPromise = (async () => {
|
|
13607
|
+
let projection;
|
|
13608
|
+
try {
|
|
13609
|
+
projection = declaration.project(productResult);
|
|
13610
|
+
validateProjectionResult(declaration, projection);
|
|
13611
|
+
} catch (error) {
|
|
13612
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13613
|
+
if (commitTransport.mappingFailed) {
|
|
13614
|
+
await commitTransport.mappingFailed({
|
|
13615
|
+
effectKey: request.effectKey,
|
|
13616
|
+
effectName: request.effectName,
|
|
13617
|
+
invocationId: request.context?.invocationId || "",
|
|
13618
|
+
idempotencyKey: idempotencyKey || "",
|
|
13619
|
+
environmentId: request.context?.environmentId || "",
|
|
13620
|
+
message
|
|
13621
|
+
});
|
|
13622
|
+
}
|
|
13623
|
+
throw new Error(
|
|
13624
|
+
`Product mutation may have succeeded, but its commit projection is invalid: ${message}`
|
|
13625
|
+
);
|
|
13626
|
+
}
|
|
13627
|
+
if (requestedKind === "transition") {
|
|
13628
|
+
const transition = request.context?.invocation?.transition;
|
|
13629
|
+
const outcome = projection.outcome;
|
|
13630
|
+
if (!transition || !outcome?.key) {
|
|
13631
|
+
throw new Error(
|
|
13632
|
+
`Transition effect ${request.effectKey} did not resolve an authored outcome`
|
|
13633
|
+
);
|
|
13634
|
+
}
|
|
13635
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
13636
|
+
transition.outcomes,
|
|
13637
|
+
outcome.key
|
|
13638
|
+
)) {
|
|
13639
|
+
throw new Error(
|
|
13640
|
+
`Transition ${transition.machine}.${transition.transition} does not declare outcome ${outcome.key}`
|
|
13641
|
+
);
|
|
13642
|
+
}
|
|
13643
|
+
}
|
|
13644
|
+
const invocationId = request.context?.invocationId || "";
|
|
13645
|
+
const environmentId = request.context?.environmentId || "";
|
|
13646
|
+
const sandboxId = request.context?.sandboxId || "";
|
|
13647
|
+
if (!invocationId || !environmentId || !sandboxId || !idempotencyKey) {
|
|
13648
|
+
throw new Error(
|
|
13649
|
+
`Committed effect ${request.effectKey} is missing its trusted invocation scope`
|
|
13650
|
+
);
|
|
13651
|
+
}
|
|
13652
|
+
const commitRequest = {
|
|
13653
|
+
kind: requestedKind,
|
|
13654
|
+
effectKey: request.effectKey,
|
|
13655
|
+
effectName: request.effectName,
|
|
13656
|
+
operationLabel: resolved.effect.label || resolved.effect.name,
|
|
13657
|
+
invocationId,
|
|
13658
|
+
idempotencyKey,
|
|
13659
|
+
sandboxId,
|
|
13660
|
+
environmentId,
|
|
13661
|
+
...request.context?.sessionId ? { sessionId: request.context.sessionId } : {},
|
|
13662
|
+
...request.context?.jobId ? { jobId: request.context.jobId } : {},
|
|
13663
|
+
...request.context?.buildId ? { buildId: request.context.buildId } : {},
|
|
13664
|
+
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13665
|
+
projection,
|
|
13666
|
+
...requestedKind === "transition" && request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13667
|
+
};
|
|
13668
|
+
const receipt = await commitTransport.persist(commitRequest);
|
|
13669
|
+
await options.commitAcknowledged?.(receipt);
|
|
13670
|
+
return receipt;
|
|
13671
|
+
})();
|
|
13672
|
+
return commitPromise;
|
|
13673
|
+
};
|
|
13674
|
+
const commitContext = commitRequired ? {
|
|
13675
|
+
effect: (productResult) => beginCommit("effect", productResult),
|
|
13676
|
+
transition: (productResult) => beginCommit("transition", productResult)
|
|
13677
|
+
} : createUnavailableCommitContext(
|
|
13678
|
+
`Effect ${request.effectKey} is not executing a declared product commit`
|
|
13679
|
+
);
|
|
13263
13680
|
const context = {
|
|
13264
13681
|
...request.context || {},
|
|
13682
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13683
|
+
commit: commitContext,
|
|
13265
13684
|
behaviors: normalizeEffectBehaviors(
|
|
13266
13685
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13267
13686
|
),
|
|
13268
13687
|
invocation: {
|
|
13269
13688
|
mode: resolved.mode,
|
|
13270
|
-
|
|
13271
|
-
|
|
13689
|
+
...commitRequired && declaration ? { commitKind: declaration.kind } : {},
|
|
13690
|
+
sourceEffectKey: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectKey || request.effectKey : request.effectKey,
|
|
13691
|
+
sourceEffectName: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectName || request.effectName : request.effectName,
|
|
13272
13692
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13273
13693
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13274
|
-
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {}
|
|
13694
|
+
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {},
|
|
13695
|
+
...request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13275
13696
|
}
|
|
13276
13697
|
};
|
|
13277
13698
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13295,6 +13716,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13295
13716
|
handlerFailed = true;
|
|
13296
13717
|
handlerError = error;
|
|
13297
13718
|
}
|
|
13719
|
+
let commitError;
|
|
13720
|
+
let commitFailed = false;
|
|
13721
|
+
if (commitPromise) {
|
|
13722
|
+
try {
|
|
13723
|
+
await commitPromise;
|
|
13724
|
+
} catch (error) {
|
|
13725
|
+
commitFailed = true;
|
|
13726
|
+
commitError = error;
|
|
13727
|
+
}
|
|
13728
|
+
}
|
|
13298
13729
|
let feedbackError;
|
|
13299
13730
|
let feedbackFailed = false;
|
|
13300
13731
|
if (feedbackContext) {
|
|
@@ -13308,9 +13739,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13308
13739
|
if (handlerFailed) {
|
|
13309
13740
|
throw handlerError;
|
|
13310
13741
|
}
|
|
13742
|
+
if (commitFailed) {
|
|
13743
|
+
throw commitError;
|
|
13744
|
+
}
|
|
13311
13745
|
if (feedbackFailed) {
|
|
13312
13746
|
throw feedbackError;
|
|
13313
13747
|
}
|
|
13748
|
+
if (commitRequired && !commitStarted) {
|
|
13749
|
+
throw new Error(
|
|
13750
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13751
|
+
);
|
|
13752
|
+
}
|
|
13314
13753
|
return handlerResult;
|
|
13315
13754
|
}
|
|
13316
13755
|
|
|
@@ -13379,12 +13818,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13379
13818
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13380
13819
|
);
|
|
13381
13820
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13382
|
-
const
|
|
13383
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path2)) {
|
|
13384
|
-
return null;
|
|
13385
|
-
}
|
|
13386
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13387
|
-
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path2) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
13821
|
+
const label = typeof node.label === "string" && node.label.trim() ? node.label.trim() : path2 || id;
|
|
13388
13822
|
return {
|
|
13389
13823
|
path: path2,
|
|
13390
13824
|
className,
|
|
@@ -13394,30 +13828,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13394
13828
|
fields
|
|
13395
13829
|
};
|
|
13396
13830
|
}
|
|
13397
|
-
function isPlaceholderRecordLabel(label, id, path2) {
|
|
13398
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13399
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path2);
|
|
13400
|
-
}
|
|
13401
|
-
function displayLabelFromFields(fields) {
|
|
13402
|
-
const preferredFieldNames = [
|
|
13403
|
-
"name",
|
|
13404
|
-
"title",
|
|
13405
|
-
"label",
|
|
13406
|
-
"display_name",
|
|
13407
|
-
"file_name",
|
|
13408
|
-
"number",
|
|
13409
|
-
"code"
|
|
13410
|
-
];
|
|
13411
|
-
for (const preferred of preferredFieldNames) {
|
|
13412
|
-
const match = fields.find(
|
|
13413
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13414
|
-
);
|
|
13415
|
-
if (typeof match?.value === "string") {
|
|
13416
|
-
return match.value.trim();
|
|
13417
|
-
}
|
|
13418
|
-
}
|
|
13419
|
-
return null;
|
|
13420
|
-
}
|
|
13421
13831
|
function normalizeRecordSearchText(value) {
|
|
13422
13832
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13423
13833
|
}
|
|
@@ -14380,7 +14790,8 @@ function normalizeStateMachines(values) {
|
|
|
14380
14790
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14381
14791
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14382
14792
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14383
|
-
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
14793
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json),
|
|
14794
|
+
outcomes: parseJsonRecord(transition?.outcomes) || parseJsonRecord(transition?.outcomes_json)
|
|
14384
14795
|
})).filter(
|
|
14385
14796
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14386
14797
|
);
|
|
@@ -14473,6 +14884,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14473
14884
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14474
14885
|
);
|
|
14475
14886
|
}
|
|
14887
|
+
if (transition.outcomes) {
|
|
14888
|
+
args.push(
|
|
14889
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14890
|
+
);
|
|
14891
|
+
}
|
|
14476
14892
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14477
14893
|
}
|
|
14478
14894
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14751,7 +15167,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14751
15167
|
name: String!
|
|
14752
15168
|
state_machine: StateMachine!
|
|
14753
15169
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
14754
|
-
add_transition(name: String!, from: String!, to: String!, label: String, description: String, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String): StateMachineMutation!
|
|
15170
|
+
add_transition(name: String!, from: String!, to: String!, label: String, description: String, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String, outcomes_json: String): StateMachineMutation!
|
|
14755
15171
|
activate_transition(name: String!): StateMachineMutation!
|
|
14756
15172
|
}
|
|
14757
15173
|
|
|
@@ -14768,6 +15184,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14768
15184
|
type StateMachineSnapshotMutation {
|
|
14769
15185
|
snapshot: StateMachineSnapshot!
|
|
14770
15186
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15187
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14771
15188
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14772
15189
|
}
|
|
14773
15190
|
|
|
@@ -14814,6 +15231,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14814
15231
|
permission_json: String
|
|
14815
15232
|
risk: String
|
|
14816
15233
|
expected_outcome_json: String
|
|
15234
|
+
outcomes_json: String
|
|
14817
15235
|
}
|
|
14818
15236
|
|
|
14819
15237
|
type StateMachinePath {
|
|
@@ -14824,6 +15242,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14824
15242
|
type StateMachineTransitionEvent {
|
|
14825
15243
|
sequence: Int!
|
|
14826
15244
|
occurred_at: Float!
|
|
15245
|
+
commit_id: String
|
|
15246
|
+
outcome: String
|
|
15247
|
+
source_version: String
|
|
15248
|
+
projected_from_mismatch: String
|
|
14827
15249
|
transition: StateMachineTransition!
|
|
14828
15250
|
from: StateMachineState!
|
|
14829
15251
|
to: StateMachineState!
|
|
@@ -14889,7 +15311,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14889
15311
|
requirements_json,
|
|
14890
15312
|
permission_json,
|
|
14891
15313
|
risk,
|
|
14892
|
-
expected_outcome_json
|
|
15314
|
+
expected_outcome_json,
|
|
15315
|
+
outcomes_json
|
|
14893
15316
|
}) => {
|
|
14894
15317
|
await run(
|
|
14895
15318
|
value.target.add_state_machine_transition(
|
|
@@ -14905,7 +15328,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14905
15328
|
requirementsJson: requirements_json,
|
|
14906
15329
|
permissionJson: permission_json,
|
|
14907
15330
|
risk,
|
|
14908
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15331
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15332
|
+
outcomesJson: outcomes_json
|
|
14909
15333
|
}
|
|
14910
15334
|
)
|
|
14911
15335
|
);
|
|
@@ -14926,6 +15350,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14926
15350
|
);
|
|
14927
15351
|
return value;
|
|
14928
15352
|
},
|
|
15353
|
+
commit_transition: async (value, { name, outcome, to, commit_id, source_version }) => {
|
|
15354
|
+
await run(
|
|
15355
|
+
value.target.commit_state_machine_transition(
|
|
15356
|
+
value.name,
|
|
15357
|
+
name,
|
|
15358
|
+
outcome,
|
|
15359
|
+
to,
|
|
15360
|
+
commit_id,
|
|
15361
|
+
source_version
|
|
15362
|
+
)
|
|
15363
|
+
);
|
|
15364
|
+
return value;
|
|
15365
|
+
},
|
|
14929
15366
|
observe_state: async (value, { state, force, source }) => {
|
|
14930
15367
|
await run(
|
|
14931
15368
|
value.target.observe_state_machine_state(
|
|
@@ -14955,7 +15392,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14955
15392
|
requirements_json: (value) => value.requirements_json || null,
|
|
14956
15393
|
permission_json: (value) => value.permission_json || null,
|
|
14957
15394
|
risk: (value) => value.risk || null,
|
|
14958
|
-
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
15395
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null,
|
|
15396
|
+
outcomes_json: (value) => value.outcomes_json || null
|
|
14959
15397
|
},
|
|
14960
15398
|
StateMachinePath: {
|
|
14961
15399
|
states: (value) => value.states,
|
|
@@ -14964,6 +15402,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14964
15402
|
StateMachineTransitionEvent: {
|
|
14965
15403
|
sequence: (value) => value.sequence,
|
|
14966
15404
|
occurred_at: (value) => value.occurred_at,
|
|
15405
|
+
commit_id: (value) => value.commit_id || null,
|
|
15406
|
+
outcome: (value) => value.outcome || null,
|
|
15407
|
+
source_version: (value) => value.source_version || null,
|
|
15408
|
+
projected_from_mismatch: (value) => value.projected_from_mismatch || null,
|
|
14967
15409
|
transition: (value) => value.transition,
|
|
14968
15410
|
from: (value) => value.from,
|
|
14969
15411
|
to: (value) => value.to
|
|
@@ -15037,6 +15479,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15037
15479
|
permission_json
|
|
15038
15480
|
risk
|
|
15039
15481
|
expected_outcome_json
|
|
15482
|
+
outcomes_json
|
|
15040
15483
|
}
|
|
15041
15484
|
}`
|
|
15042
15485
|
]
|
|
@@ -15666,6 +16109,133 @@ var Environment = class _Environment {
|
|
|
15666
16109
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15667
16110
|
};
|
|
15668
16111
|
}
|
|
16112
|
+
/**
|
|
16113
|
+
* Acknowledge a declared product mutation that already happened outside a
|
|
16114
|
+
* Granular-run effect (for example, in a webhook consumer). These methods
|
|
16115
|
+
* run the declaration's pure projection mapper; they never call its handler.
|
|
16116
|
+
*/
|
|
16117
|
+
get commit() {
|
|
16118
|
+
return {
|
|
16119
|
+
effect: async (effect, productResult, options = {}) => this.persistExternalEffect(effect, productResult, options),
|
|
16120
|
+
transition: async (transition, productResult, options = {}) => this.persistExternalTransition(transition, productResult, options),
|
|
16121
|
+
get: async (commitId) => this.controlPlaneRequest(
|
|
16122
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16123
|
+
commitId
|
|
16124
|
+
)}`
|
|
16125
|
+
),
|
|
16126
|
+
retry: async (commitId) => this.controlPlaneRequest(
|
|
16127
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16128
|
+
commitId
|
|
16129
|
+
)}/retry`,
|
|
16130
|
+
{ method: "POST" }
|
|
16131
|
+
)
|
|
16132
|
+
};
|
|
16133
|
+
}
|
|
16134
|
+
/** Inspect or retry the agent synchronization of a product snapshot. */
|
|
16135
|
+
get observation() {
|
|
16136
|
+
return {
|
|
16137
|
+
get: async (observationId) => this.controlPlaneRequest(
|
|
16138
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16139
|
+
observationId
|
|
16140
|
+
)}`
|
|
16141
|
+
),
|
|
16142
|
+
retry: async (observationId) => this.controlPlaneRequest(
|
|
16143
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16144
|
+
observationId
|
|
16145
|
+
)}/retry`,
|
|
16146
|
+
{ method: "POST" }
|
|
16147
|
+
)
|
|
16148
|
+
};
|
|
16149
|
+
}
|
|
16150
|
+
async persistExternalEffect(effect, productResult, options) {
|
|
16151
|
+
const projection = effect.commit.project(productResult);
|
|
16152
|
+
validateProjectionResult(effect.commit, projection);
|
|
16153
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16154
|
+
return this.controlPlaneRequest(
|
|
16155
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16156
|
+
{
|
|
16157
|
+
method: "POST",
|
|
16158
|
+
body: JSON.stringify({
|
|
16159
|
+
kind: "effect",
|
|
16160
|
+
effectKey: computeEffectKey2(effect),
|
|
16161
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16162
|
+
projection
|
|
16163
|
+
})
|
|
16164
|
+
}
|
|
16165
|
+
);
|
|
16166
|
+
}
|
|
16167
|
+
async persistExternalTransition(transition, productResult, options) {
|
|
16168
|
+
const metadata = getDefinedTransitionMetadata(transition);
|
|
16169
|
+
if (!metadata) {
|
|
16170
|
+
throw new Error(
|
|
16171
|
+
"environment.commit.transition requires a transition returned by defineStateMachine"
|
|
16172
|
+
);
|
|
16173
|
+
}
|
|
16174
|
+
const projection = transition.effect.commit.project(productResult);
|
|
16175
|
+
validateProjectionResult(transition.effect.commit, projection);
|
|
16176
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16177
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
16178
|
+
transition.outcomes,
|
|
16179
|
+
projection.outcome.key
|
|
16180
|
+
)) {
|
|
16181
|
+
throw new Error(
|
|
16182
|
+
`Transition ${metadata.machine}.${metadata.transition} does not declare outcome ${projection.outcome.key}`
|
|
16183
|
+
);
|
|
16184
|
+
}
|
|
16185
|
+
if (!projection.primaryTarget) {
|
|
16186
|
+
throw new Error(
|
|
16187
|
+
"An external transition projection requires primaryTarget to identify the transitioned product record"
|
|
16188
|
+
);
|
|
16189
|
+
}
|
|
16190
|
+
return this.controlPlaneRequest(
|
|
16191
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16192
|
+
{
|
|
16193
|
+
method: "POST",
|
|
16194
|
+
body: JSON.stringify({
|
|
16195
|
+
kind: "transition",
|
|
16196
|
+
effectKey: computeEffectKey2(transition.effect),
|
|
16197
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16198
|
+
projection,
|
|
16199
|
+
transition: {
|
|
16200
|
+
className: projection.primaryTarget.className,
|
|
16201
|
+
objectId: projection.primaryTarget.id,
|
|
16202
|
+
...projection.primaryTarget.path ? { objectPath: projection.primaryTarget.path } : {},
|
|
16203
|
+
machine: metadata.machine,
|
|
16204
|
+
transition: metadata.transition,
|
|
16205
|
+
from: transition.from
|
|
16206
|
+
}
|
|
16207
|
+
})
|
|
16208
|
+
}
|
|
16209
|
+
);
|
|
16210
|
+
}
|
|
16211
|
+
requireExternalIdentity(sourceVersion, options) {
|
|
16212
|
+
if (!options.sourceEventId?.trim() && !sourceVersion?.trim()) {
|
|
16213
|
+
throw new Error(
|
|
16214
|
+
"An external commit requires sourceEventId or a source version from its projection mapper"
|
|
16215
|
+
);
|
|
16216
|
+
}
|
|
16217
|
+
}
|
|
16218
|
+
/**
|
|
16219
|
+
* Synchronize a versioned product snapshot without claiming an effect or
|
|
16220
|
+
* lifecycle transition. This records no transition history.
|
|
16221
|
+
*/
|
|
16222
|
+
async observe(mapper, productResult) {
|
|
16223
|
+
const declaration = { kind: "effect"};
|
|
16224
|
+
const projection = mapper(productResult);
|
|
16225
|
+
validateProjectionResult(declaration, projection);
|
|
16226
|
+
if (!projection.source.version?.trim()) {
|
|
16227
|
+
throw new Error(
|
|
16228
|
+
"environment.observe requires a monotonic source version or serialized adapter sequence"
|
|
16229
|
+
);
|
|
16230
|
+
}
|
|
16231
|
+
return this.controlPlaneRequest(
|
|
16232
|
+
`/control/environments/${this.environmentId}/observations`,
|
|
16233
|
+
{
|
|
16234
|
+
method: "POST",
|
|
16235
|
+
body: JSON.stringify({ projection })
|
|
16236
|
+
}
|
|
16237
|
+
);
|
|
16238
|
+
}
|
|
15669
16239
|
/**
|
|
15670
16240
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15671
16241
|
* own the customer application's state machine.
|
|
@@ -17072,6 +17642,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17072
17642
|
}
|
|
17073
17643
|
};
|
|
17074
17644
|
}
|
|
17645
|
+
get mutations() {
|
|
17646
|
+
return {
|
|
17647
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17648
|
+
"/mutations",
|
|
17649
|
+
options
|
|
17650
|
+
)
|
|
17651
|
+
};
|
|
17652
|
+
}
|
|
17075
17653
|
get artifacts() {
|
|
17076
17654
|
return {
|
|
17077
17655
|
list: (options = {}) => {
|
|
@@ -18571,6 +19149,7 @@ var Granular = class _Granular {
|
|
|
18571
19149
|
const serialized = {
|
|
18572
19150
|
effectKey: computeEffectKey2(effect),
|
|
18573
19151
|
name: effect.name,
|
|
19152
|
+
...effect.label ? { label: effect.label } : {},
|
|
18574
19153
|
description: effect.description,
|
|
18575
19154
|
inputSchema: effect.inputSchema,
|
|
18576
19155
|
stability: effect.stability || "stable",
|
|
@@ -18594,6 +19173,9 @@ var Granular = class _Granular {
|
|
|
18594
19173
|
if (effect.metamodels !== void 0) {
|
|
18595
19174
|
serialized.metamodels = effect.metamodels;
|
|
18596
19175
|
}
|
|
19176
|
+
if (effect.commit !== void 0) {
|
|
19177
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19178
|
+
}
|
|
18597
19179
|
return serialized;
|
|
18598
19180
|
}
|
|
18599
19181
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18814,16 +19396,60 @@ var Granular = class _Granular {
|
|
|
18814
19396
|
};
|
|
18815
19397
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18816
19398
|
const request = params;
|
|
18817
|
-
|
|
19399
|
+
let commitReceipt;
|
|
19400
|
+
const result = await invokeRegisteredEffect(
|
|
18818
19401
|
this.getSandboxEffectMap(sandboxId),
|
|
18819
19402
|
request,
|
|
18820
19403
|
{
|
|
19404
|
+
commitAcknowledged: (receipt) => {
|
|
19405
|
+
commitReceipt = receipt;
|
|
19406
|
+
},
|
|
19407
|
+
commit: {
|
|
19408
|
+
persist: (commitRequest) => this.request(
|
|
19409
|
+
`/control/environments/${encodeURIComponent(
|
|
19410
|
+
commitRequest.environmentId
|
|
19411
|
+
)}/commits`,
|
|
19412
|
+
{
|
|
19413
|
+
method: "POST",
|
|
19414
|
+
body: JSON.stringify({
|
|
19415
|
+
kind: commitRequest.kind,
|
|
19416
|
+
invocationId: commitRequest.invocationId,
|
|
19417
|
+
idempotencyKey: commitRequest.idempotencyKey,
|
|
19418
|
+
effectKey: commitRequest.effectKey,
|
|
19419
|
+
projection: commitRequest.projection
|
|
19420
|
+
})
|
|
19421
|
+
}
|
|
19422
|
+
),
|
|
19423
|
+
mappingFailed: (failure) => this.request(
|
|
19424
|
+
`/control/environments/${encodeURIComponent(
|
|
19425
|
+
failure.environmentId
|
|
19426
|
+
)}/commit-invocations/${encodeURIComponent(
|
|
19427
|
+
failure.invocationId
|
|
19428
|
+
)}`,
|
|
19429
|
+
{
|
|
19430
|
+
method: "PATCH",
|
|
19431
|
+
body: JSON.stringify({
|
|
19432
|
+
status: "mapping_failed",
|
|
19433
|
+
error: {
|
|
19434
|
+
code: "commit_projection_mapping_failed",
|
|
19435
|
+
message: failure.message,
|
|
19436
|
+
retryable: false
|
|
19437
|
+
}
|
|
19438
|
+
})
|
|
19439
|
+
}
|
|
19440
|
+
)
|
|
19441
|
+
},
|
|
18821
19442
|
feedback: {
|
|
18822
19443
|
invocationId: request.callId,
|
|
18823
19444
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18824
19445
|
}
|
|
18825
19446
|
}
|
|
18826
19447
|
);
|
|
19448
|
+
return {
|
|
19449
|
+
__granularEffectInvocationResult: true,
|
|
19450
|
+
result,
|
|
19451
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19452
|
+
};
|
|
18827
19453
|
});
|
|
18828
19454
|
wsClient.on("open", () => {
|
|
18829
19455
|
void this.synchronizeEffectHost(host).catch((error) => {
|