@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.mjs
CHANGED
|
@@ -6508,6 +6508,11 @@ var Session = class {
|
|
|
6508
6508
|
}
|
|
6509
6509
|
}
|
|
6510
6510
|
buildDirectedInvocationEffectContext(params, feedbackContext) {
|
|
6511
|
+
const commitUnavailable = async () => {
|
|
6512
|
+
throw new Error(
|
|
6513
|
+
"This directed browser tool invocation has no product commit transport"
|
|
6514
|
+
);
|
|
6515
|
+
};
|
|
6511
6516
|
return {
|
|
6512
6517
|
effectClientId: this.clientId,
|
|
6513
6518
|
sandboxId: params.sandboxId || "",
|
|
@@ -6520,6 +6525,10 @@ var Session = class {
|
|
|
6520
6525
|
userId: "",
|
|
6521
6526
|
subjectId: ""
|
|
6522
6527
|
},
|
|
6528
|
+
commit: {
|
|
6529
|
+
effect: commitUnavailable,
|
|
6530
|
+
transition: commitUnavailable
|
|
6531
|
+
},
|
|
6523
6532
|
...feedbackContext ? {
|
|
6524
6533
|
feedback: feedbackContext.feedback,
|
|
6525
6534
|
transientFeedback: feedbackContext.transientFeedback
|
|
@@ -12291,6 +12300,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12291
12300
|
summary: external_exports.string().optional()
|
|
12292
12301
|
}).strict()
|
|
12293
12302
|
]);
|
|
12303
|
+
var StateTransitionOutcomeSchema = external_exports.object({
|
|
12304
|
+
label: external_exports.string().min(1).optional(),
|
|
12305
|
+
to: external_exports.string().min(1),
|
|
12306
|
+
primary: external_exports.boolean().optional(),
|
|
12307
|
+
disposition: external_exports.enum(["continue", "error"])
|
|
12308
|
+
}).strict();
|
|
12294
12309
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12295
12310
|
name: external_exports.string().min(1),
|
|
12296
12311
|
from: external_exports.string().min(1),
|
|
@@ -12302,7 +12317,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12302
12317
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12303
12318
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12304
12319
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12305
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12320
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12321
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12306
12322
|
}).strict();
|
|
12307
12323
|
external_exports.object({
|
|
12308
12324
|
name: external_exports.string().min(1),
|
|
@@ -12983,6 +12999,238 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
12983
12999
|
}
|
|
12984
13000
|
});
|
|
12985
13001
|
|
|
13002
|
+
// src/commit.ts
|
|
13003
|
+
var MAX_PROJECTION_CHANGES = 1e3;
|
|
13004
|
+
var MAX_SAFE_SUMMARY_KEYS = 32;
|
|
13005
|
+
var MAX_SAFE_TEXT_LENGTH = 2e3;
|
|
13006
|
+
var definedTransitionMetadata = /* @__PURE__ */ new WeakMap();
|
|
13007
|
+
function getDefinedTransitionMetadata(transition) {
|
|
13008
|
+
return definedTransitionMetadata.get(transition);
|
|
13009
|
+
}
|
|
13010
|
+
function requireNonEmptyString(value, path2) {
|
|
13011
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13012
|
+
throw new Error(`${path2} must be a non-empty string`);
|
|
13013
|
+
}
|
|
13014
|
+
return value.trim();
|
|
13015
|
+
}
|
|
13016
|
+
function validateObjectReference(value, path2) {
|
|
13017
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13018
|
+
throw new Error(`${path2} must be an object reference`);
|
|
13019
|
+
}
|
|
13020
|
+
const reference = value;
|
|
13021
|
+
requireNonEmptyString(reference.className, `${path2}.className`);
|
|
13022
|
+
requireNonEmptyString(reference.id, `${path2}.id`);
|
|
13023
|
+
if (reference.path !== void 0) {
|
|
13024
|
+
requireNonEmptyString(reference.path, `${path2}.path`);
|
|
13025
|
+
}
|
|
13026
|
+
}
|
|
13027
|
+
function validateScalarRecord(value, path2) {
|
|
13028
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
13029
|
+
throw new Error(`${path2} must be an object of scalar values`);
|
|
13030
|
+
}
|
|
13031
|
+
for (const [key, item] of Object.entries(value)) {
|
|
13032
|
+
if (item !== null && typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean") {
|
|
13033
|
+
throw new Error(
|
|
13034
|
+
`${path2}.${key} must be a string, finite number, boolean, or null`
|
|
13035
|
+
);
|
|
13036
|
+
}
|
|
13037
|
+
if (typeof item === "number" && !Number.isFinite(item)) {
|
|
13038
|
+
throw new Error(`${path2}.${key} must be finite`);
|
|
13039
|
+
}
|
|
13040
|
+
}
|
|
13041
|
+
}
|
|
13042
|
+
function validateProjectedRecord(value, path2) {
|
|
13043
|
+
validateObjectReference(value, path2);
|
|
13044
|
+
const record = value;
|
|
13045
|
+
if (record.label !== void 0) {
|
|
13046
|
+
if (typeof record.label !== "string") {
|
|
13047
|
+
throw new Error(`${path2}.label must be a string`);
|
|
13048
|
+
}
|
|
13049
|
+
if (record.label.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13050
|
+
throw new Error(
|
|
13051
|
+
`${path2}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
|
|
13052
|
+
);
|
|
13053
|
+
}
|
|
13054
|
+
}
|
|
13055
|
+
validateScalarRecord(record.fields, `${path2}.fields`);
|
|
13056
|
+
}
|
|
13057
|
+
function validateBoundedJson(value, path2, depth = 0) {
|
|
13058
|
+
if (depth > 12) throw new Error(`${path2} is nested too deeply`);
|
|
13059
|
+
if (value === null || typeof value === "boolean") return;
|
|
13060
|
+
if (typeof value === "number") {
|
|
13061
|
+
if (!Number.isFinite(value)) throw new Error(`${path2} must be finite`);
|
|
13062
|
+
return;
|
|
13063
|
+
}
|
|
13064
|
+
if (typeof value === "string") {
|
|
13065
|
+
if (value.length > MAX_SAFE_TEXT_LENGTH) {
|
|
13066
|
+
throw new Error(`${path2} exceeds ${MAX_SAFE_TEXT_LENGTH} characters`);
|
|
13067
|
+
}
|
|
13068
|
+
return;
|
|
13069
|
+
}
|
|
13070
|
+
if (Array.isArray(value)) {
|
|
13071
|
+
if (value.length > MAX_PROJECTION_CHANGES) {
|
|
13072
|
+
throw new Error(`${path2} contains too many values`);
|
|
13073
|
+
}
|
|
13074
|
+
value.forEach(
|
|
13075
|
+
(item, index) => validateBoundedJson(item, `${path2}[${index}]`, depth + 1)
|
|
13076
|
+
);
|
|
13077
|
+
return;
|
|
13078
|
+
}
|
|
13079
|
+
if (!value || typeof value !== "object") {
|
|
13080
|
+
throw new Error(`${path2} contains an unsupported value`);
|
|
13081
|
+
}
|
|
13082
|
+
const entries = Object.entries(value);
|
|
13083
|
+
if (entries.length > 256) throw new Error(`${path2} contains too many keys`);
|
|
13084
|
+
for (const [key, item] of entries) {
|
|
13085
|
+
if (/token|secret|password|authorization|cookie/i.test(key)) {
|
|
13086
|
+
throw new Error(`${path2}.${key} is not allowed in a commit projection`);
|
|
13087
|
+
}
|
|
13088
|
+
validateBoundedJson(item, `${path2}.${key}`, depth + 1);
|
|
13089
|
+
}
|
|
13090
|
+
}
|
|
13091
|
+
function validateProjectionResult(declaration, projection) {
|
|
13092
|
+
if (!projection || typeof projection !== "object") {
|
|
13093
|
+
throw new Error("Projection mapper must return an object");
|
|
13094
|
+
}
|
|
13095
|
+
requireNonEmptyString(
|
|
13096
|
+
projection.source?.reference,
|
|
13097
|
+
"projection.source.reference"
|
|
13098
|
+
);
|
|
13099
|
+
if (projection.source.version !== void 0) {
|
|
13100
|
+
requireNonEmptyString(
|
|
13101
|
+
projection.source.version,
|
|
13102
|
+
"projection.source.version"
|
|
13103
|
+
);
|
|
13104
|
+
}
|
|
13105
|
+
if (!Array.isArray(projection.changes)) {
|
|
13106
|
+
throw new Error("projection.changes must be an array");
|
|
13107
|
+
}
|
|
13108
|
+
if (projection.changes.length > MAX_PROJECTION_CHANGES) {
|
|
13109
|
+
throw new Error(
|
|
13110
|
+
`projection.changes exceeds the ${MAX_PROJECTION_CHANGES} change limit`
|
|
13111
|
+
);
|
|
13112
|
+
}
|
|
13113
|
+
if (projection.primaryTarget) {
|
|
13114
|
+
validateObjectReference(
|
|
13115
|
+
projection.primaryTarget,
|
|
13116
|
+
"projection.primaryTarget"
|
|
13117
|
+
);
|
|
13118
|
+
}
|
|
13119
|
+
if (projection.safeSummary) {
|
|
13120
|
+
const entries = Object.entries(projection.safeSummary);
|
|
13121
|
+
if (entries.length > MAX_SAFE_SUMMARY_KEYS) {
|
|
13122
|
+
throw new Error(
|
|
13123
|
+
`projection.safeSummary exceeds the ${MAX_SAFE_SUMMARY_KEYS} key limit`
|
|
13124
|
+
);
|
|
13125
|
+
}
|
|
13126
|
+
validateBoundedJson(projection.safeSummary, "projection.safeSummary");
|
|
13127
|
+
validateScalarRecord(projection.safeSummary, "projection.safeSummary");
|
|
13128
|
+
}
|
|
13129
|
+
projection.changes.forEach((change, index) => {
|
|
13130
|
+
const changePath = `projection.changes[${index}]`;
|
|
13131
|
+
validateBoundedJson(change, changePath);
|
|
13132
|
+
if (!change || typeof change !== "object" || Array.isArray(change)) {
|
|
13133
|
+
throw new Error(`${changePath} must be an object`);
|
|
13134
|
+
}
|
|
13135
|
+
const rawChange = change;
|
|
13136
|
+
const kind = requireNonEmptyString(rawChange.kind, `${changePath}.kind`);
|
|
13137
|
+
if (kind === "object") {
|
|
13138
|
+
const operation = requireNonEmptyString(
|
|
13139
|
+
rawChange.operation,
|
|
13140
|
+
`${changePath}.operation`
|
|
13141
|
+
);
|
|
13142
|
+
if (operation !== "created" && operation !== "updated" && operation !== "deleted") {
|
|
13143
|
+
throw new Error(
|
|
13144
|
+
`${changePath}.operation must be created, updated, or deleted`
|
|
13145
|
+
);
|
|
13146
|
+
}
|
|
13147
|
+
if (operation === "deleted") {
|
|
13148
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13149
|
+
} else {
|
|
13150
|
+
validateProjectedRecord(rawChange.record, `${changePath}.record`);
|
|
13151
|
+
}
|
|
13152
|
+
} else if (kind === "relationship") {
|
|
13153
|
+
const operation = requireNonEmptyString(
|
|
13154
|
+
rawChange.operation,
|
|
13155
|
+
`${changePath}.operation`
|
|
13156
|
+
);
|
|
13157
|
+
if (operation !== "connected" && operation !== "disconnected") {
|
|
13158
|
+
throw new Error(
|
|
13159
|
+
`${changePath}.operation must be connected or disconnected`
|
|
13160
|
+
);
|
|
13161
|
+
}
|
|
13162
|
+
requireNonEmptyString(
|
|
13163
|
+
rawChange.relationship,
|
|
13164
|
+
`${changePath}.relationship`
|
|
13165
|
+
);
|
|
13166
|
+
validateObjectReference(rawChange.from, `${changePath}.from`);
|
|
13167
|
+
validateObjectReference(rawChange.to, `${changePath}.to`);
|
|
13168
|
+
} else if (kind === "state_observation") {
|
|
13169
|
+
if (rawChange.operation !== void 0) {
|
|
13170
|
+
throw new Error(
|
|
13171
|
+
`${changePath}.operation is not valid for an observation`
|
|
13172
|
+
);
|
|
13173
|
+
}
|
|
13174
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
13175
|
+
requireNonEmptyString(rawChange.machine, `${changePath}.machine`);
|
|
13176
|
+
requireNonEmptyString(rawChange.state, `${changePath}.state`);
|
|
13177
|
+
} else {
|
|
13178
|
+
throw new Error(`${changePath}.kind is unsupported: ${kind}`);
|
|
13179
|
+
}
|
|
13180
|
+
});
|
|
13181
|
+
const objectOperations = /* @__PURE__ */ new Map();
|
|
13182
|
+
for (const change of projection.changes) {
|
|
13183
|
+
if (change.kind !== "object") continue;
|
|
13184
|
+
const reference = change.operation === "deleted" ? change.target : change.record;
|
|
13185
|
+
const key = `${reference.className}\0${reference.id}`;
|
|
13186
|
+
const operations = objectOperations.get(key) || {
|
|
13187
|
+
deleted: false,
|
|
13188
|
+
upserted: false
|
|
13189
|
+
};
|
|
13190
|
+
if (change.operation === "deleted") operations.deleted = true;
|
|
13191
|
+
else operations.upserted = true;
|
|
13192
|
+
if (operations.deleted && operations.upserted) {
|
|
13193
|
+
throw new Error(
|
|
13194
|
+
`projection.changes cannot both delete and upsert ${reference.className}/${reference.id}; return only its canonical final state`
|
|
13195
|
+
);
|
|
13196
|
+
}
|
|
13197
|
+
objectOperations.set(key, operations);
|
|
13198
|
+
}
|
|
13199
|
+
const outcome = projection.outcome;
|
|
13200
|
+
if (declaration.kind === "transition") {
|
|
13201
|
+
if (!outcome) {
|
|
13202
|
+
throw new Error("A transition projection must return an outcome");
|
|
13203
|
+
}
|
|
13204
|
+
requireNonEmptyString(outcome.key, "projection.outcome.key");
|
|
13205
|
+
if (outcome.error) {
|
|
13206
|
+
validateBoundedJson(outcome.error, "projection.outcome.error");
|
|
13207
|
+
}
|
|
13208
|
+
} else if (projection.outcome !== void 0) {
|
|
13209
|
+
throw new Error("An effect projection cannot declare a transition outcome");
|
|
13210
|
+
}
|
|
13211
|
+
}
|
|
13212
|
+
function canonicalizeCommitValue(value) {
|
|
13213
|
+
const normalize = (current) => {
|
|
13214
|
+
if (current === null || typeof current === "boolean" || typeof current === "string") {
|
|
13215
|
+
return typeof current === "string" ? current.normalize("NFC") : current;
|
|
13216
|
+
}
|
|
13217
|
+
if (typeof current === "number") {
|
|
13218
|
+
if (!Number.isFinite(current)) {
|
|
13219
|
+
throw new Error("Cannot canonicalize a non-finite number");
|
|
13220
|
+
}
|
|
13221
|
+
return Object.is(current, -0) ? 0 : current;
|
|
13222
|
+
}
|
|
13223
|
+
if (Array.isArray(current)) return current.map(normalize);
|
|
13224
|
+
if (current && typeof current === "object") {
|
|
13225
|
+
return Object.fromEntries(
|
|
13226
|
+
Object.entries(current).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key.normalize("NFC"), normalize(item)])
|
|
13227
|
+
);
|
|
13228
|
+
}
|
|
13229
|
+
throw new Error(`Cannot canonicalize ${typeof current}`);
|
|
13230
|
+
};
|
|
13231
|
+
return JSON.stringify(normalize(value));
|
|
13232
|
+
}
|
|
13233
|
+
|
|
12986
13234
|
// src/effect-runtime.ts
|
|
12987
13235
|
function computeEffectKey(effect) {
|
|
12988
13236
|
const attachedClass = effect.className?.trim();
|
|
@@ -13047,6 +13295,45 @@ function resolveInvocationMode(context) {
|
|
|
13047
13295
|
}
|
|
13048
13296
|
return "execute";
|
|
13049
13297
|
}
|
|
13298
|
+
async function sha256Hex(value) {
|
|
13299
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
13300
|
+
"SHA-256",
|
|
13301
|
+
new TextEncoder().encode(value)
|
|
13302
|
+
);
|
|
13303
|
+
return Array.from(
|
|
13304
|
+
new Uint8Array(digest),
|
|
13305
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
13306
|
+
).join("");
|
|
13307
|
+
}
|
|
13308
|
+
async function resolveInvocationIdempotencyKey(request) {
|
|
13309
|
+
const supplied = request.context?.idempotencyKey?.trim();
|
|
13310
|
+
if (supplied) return supplied;
|
|
13311
|
+
const invocationId = request.context?.invocationId?.trim();
|
|
13312
|
+
if (!invocationId) {
|
|
13313
|
+
throw new Error(
|
|
13314
|
+
`Committed effect ${request.effectKey} requires an invocation id`
|
|
13315
|
+
);
|
|
13316
|
+
}
|
|
13317
|
+
const digest = await sha256Hex(
|
|
13318
|
+
canonicalizeCommitValue({
|
|
13319
|
+
sandboxId: request.context?.sandboxId || "",
|
|
13320
|
+
environmentId: request.context?.environmentId || "",
|
|
13321
|
+
effectKey: request.effectKey,
|
|
13322
|
+
invocationId,
|
|
13323
|
+
input: request.input
|
|
13324
|
+
})
|
|
13325
|
+
);
|
|
13326
|
+
return `gci_${digest}`;
|
|
13327
|
+
}
|
|
13328
|
+
function createUnavailableCommitContext(message) {
|
|
13329
|
+
const unavailable = async () => {
|
|
13330
|
+
throw new Error(message);
|
|
13331
|
+
};
|
|
13332
|
+
return {
|
|
13333
|
+
effect: unavailable,
|
|
13334
|
+
transition: unavailable
|
|
13335
|
+
};
|
|
13336
|
+
}
|
|
13050
13337
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13051
13338
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13052
13339
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13118,22 +13405,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13118
13405
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13119
13406
|
}
|
|
13120
13407
|
if (mode === "reverse") {
|
|
13408
|
+
const sourceEffectKey = request.context?.invocation?.sourceEffectKey?.trim();
|
|
13409
|
+
if (sourceEffectKey && sourceEffectKey !== request.effectKey) {
|
|
13410
|
+
return { effect, mode, handler: effect.handler };
|
|
13411
|
+
}
|
|
13412
|
+
const namedReverseHandler = request.context?.invocation?.reverseHandler?.trim() || behaviors.reverse?.handler?.trim();
|
|
13413
|
+
if (namedReverseHandler) {
|
|
13414
|
+
const reverseEffect = resolveReverseEffect(
|
|
13415
|
+
effectMap,
|
|
13416
|
+
effect,
|
|
13417
|
+
request,
|
|
13418
|
+
behaviors
|
|
13419
|
+
);
|
|
13420
|
+
if (reverseEffect) {
|
|
13421
|
+
return {
|
|
13422
|
+
effect: reverseEffect,
|
|
13423
|
+
mode,
|
|
13424
|
+
handler: reverseEffect.handler
|
|
13425
|
+
};
|
|
13426
|
+
}
|
|
13427
|
+
throw new Error(
|
|
13428
|
+
`Reverse effect ${namedReverseHandler} is not registered for ${request.effectKey}`
|
|
13429
|
+
);
|
|
13430
|
+
}
|
|
13121
13431
|
if (effect.reverseHandler) {
|
|
13122
13432
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13123
13433
|
}
|
|
13124
|
-
const reverseEffect = resolveReverseEffect(
|
|
13125
|
-
effectMap,
|
|
13126
|
-
effect,
|
|
13127
|
-
request,
|
|
13128
|
-
behaviors
|
|
13129
|
-
);
|
|
13130
|
-
if (reverseEffect) {
|
|
13131
|
-
return {
|
|
13132
|
-
effect: reverseEffect,
|
|
13133
|
-
mode,
|
|
13134
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13135
|
-
};
|
|
13136
|
-
}
|
|
13137
13434
|
throw new Error(
|
|
13138
13435
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13139
13436
|
);
|
|
@@ -13234,18 +13531,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13234
13531
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13235
13532
|
}
|
|
13236
13533
|
const resolved = resolveHandlerForMode(effectMap, effect, request);
|
|
13534
|
+
const isPreResolvedNamedReverse = resolved.mode === "reverse" && Boolean(request.context?.invocation?.sourceEffectKey?.trim()) && request.context?.invocation?.sourceEffectKey?.trim() !== request.effectKey;
|
|
13535
|
+
const resolvedEffectKey = computeEffectKey(resolved.effect);
|
|
13536
|
+
if (resolved.mode === "reverse" && resolved.effect !== effect && (effect.commit || resolved.effect.commit)) {
|
|
13537
|
+
throw new Error(
|
|
13538
|
+
`Committed named reverse effect ${resolvedEffectKey} must be reserved and dispatched using its own effect key; customer code was not invoked`
|
|
13539
|
+
);
|
|
13540
|
+
}
|
|
13541
|
+
const declaration = resolved.effect.commit;
|
|
13542
|
+
const commitTransport = options.commit;
|
|
13543
|
+
const commitRequired = Boolean(declaration) && (resolved.mode === "execute" || resolved.mode === "reverse");
|
|
13544
|
+
if (commitRequired && !commitTransport) {
|
|
13545
|
+
throw new Error(
|
|
13546
|
+
`Committed effect ${request.effectKey} has no durable commit transport; customer code was not invoked`
|
|
13547
|
+
);
|
|
13548
|
+
}
|
|
13549
|
+
if (commitRequired && declaration?.kind === "transition" && !request.context?.invocation?.transition) {
|
|
13550
|
+
throw new Error(
|
|
13551
|
+
`Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
|
|
13552
|
+
);
|
|
13553
|
+
}
|
|
13554
|
+
const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
|
|
13555
|
+
let commitStarted = false;
|
|
13556
|
+
let commitPromise = null;
|
|
13557
|
+
const beginCommit = (requestedKind, productResult) => {
|
|
13558
|
+
if (!declaration || !commitRequired || !commitTransport) {
|
|
13559
|
+
return Promise.reject(
|
|
13560
|
+
new Error(
|
|
13561
|
+
`Effect ${request.effectKey} does not declare an active ${requestedKind} commit`
|
|
13562
|
+
)
|
|
13563
|
+
);
|
|
13564
|
+
}
|
|
13565
|
+
if (declaration.kind !== requestedKind) {
|
|
13566
|
+
return Promise.reject(
|
|
13567
|
+
new Error(
|
|
13568
|
+
`Effect ${request.effectKey} declares ${declaration.kind} commit, not ${requestedKind}`
|
|
13569
|
+
)
|
|
13570
|
+
);
|
|
13571
|
+
}
|
|
13572
|
+
if (commitStarted) {
|
|
13573
|
+
return Promise.reject(
|
|
13574
|
+
new Error(
|
|
13575
|
+
`Effect invocation ${request.context?.invocationId || request.effectKey} already emitted its commit`
|
|
13576
|
+
)
|
|
13577
|
+
);
|
|
13578
|
+
}
|
|
13579
|
+
commitStarted = true;
|
|
13580
|
+
commitPromise = (async () => {
|
|
13581
|
+
let projection;
|
|
13582
|
+
try {
|
|
13583
|
+
projection = declaration.project(productResult);
|
|
13584
|
+
validateProjectionResult(declaration, projection);
|
|
13585
|
+
} catch (error) {
|
|
13586
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13587
|
+
if (commitTransport.mappingFailed) {
|
|
13588
|
+
await commitTransport.mappingFailed({
|
|
13589
|
+
effectKey: request.effectKey,
|
|
13590
|
+
effectName: request.effectName,
|
|
13591
|
+
invocationId: request.context?.invocationId || "",
|
|
13592
|
+
idempotencyKey: idempotencyKey || "",
|
|
13593
|
+
environmentId: request.context?.environmentId || "",
|
|
13594
|
+
message
|
|
13595
|
+
});
|
|
13596
|
+
}
|
|
13597
|
+
throw new Error(
|
|
13598
|
+
`Product mutation may have succeeded, but its commit projection is invalid: ${message}`
|
|
13599
|
+
);
|
|
13600
|
+
}
|
|
13601
|
+
if (requestedKind === "transition") {
|
|
13602
|
+
const transition = request.context?.invocation?.transition;
|
|
13603
|
+
const outcome = projection.outcome;
|
|
13604
|
+
if (!transition || !outcome?.key) {
|
|
13605
|
+
throw new Error(
|
|
13606
|
+
`Transition effect ${request.effectKey} did not resolve an authored outcome`
|
|
13607
|
+
);
|
|
13608
|
+
}
|
|
13609
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
13610
|
+
transition.outcomes,
|
|
13611
|
+
outcome.key
|
|
13612
|
+
)) {
|
|
13613
|
+
throw new Error(
|
|
13614
|
+
`Transition ${transition.machine}.${transition.transition} does not declare outcome ${outcome.key}`
|
|
13615
|
+
);
|
|
13616
|
+
}
|
|
13617
|
+
}
|
|
13618
|
+
const invocationId = request.context?.invocationId || "";
|
|
13619
|
+
const environmentId = request.context?.environmentId || "";
|
|
13620
|
+
const sandboxId = request.context?.sandboxId || "";
|
|
13621
|
+
if (!invocationId || !environmentId || !sandboxId || !idempotencyKey) {
|
|
13622
|
+
throw new Error(
|
|
13623
|
+
`Committed effect ${request.effectKey} is missing its trusted invocation scope`
|
|
13624
|
+
);
|
|
13625
|
+
}
|
|
13626
|
+
const commitRequest = {
|
|
13627
|
+
kind: requestedKind,
|
|
13628
|
+
effectKey: request.effectKey,
|
|
13629
|
+
effectName: request.effectName,
|
|
13630
|
+
operationLabel: resolved.effect.label || resolved.effect.name,
|
|
13631
|
+
invocationId,
|
|
13632
|
+
idempotencyKey,
|
|
13633
|
+
sandboxId,
|
|
13634
|
+
environmentId,
|
|
13635
|
+
...request.context?.sessionId ? { sessionId: request.context.sessionId } : {},
|
|
13636
|
+
...request.context?.jobId ? { jobId: request.context.jobId } : {},
|
|
13637
|
+
...request.context?.buildId ? { buildId: request.context.buildId } : {},
|
|
13638
|
+
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13639
|
+
projection,
|
|
13640
|
+
...requestedKind === "transition" && request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13641
|
+
};
|
|
13642
|
+
const receipt = await commitTransport.persist(commitRequest);
|
|
13643
|
+
await options.commitAcknowledged?.(receipt);
|
|
13644
|
+
return receipt;
|
|
13645
|
+
})();
|
|
13646
|
+
return commitPromise;
|
|
13647
|
+
};
|
|
13648
|
+
const commitContext = commitRequired ? {
|
|
13649
|
+
effect: (productResult) => beginCommit("effect", productResult),
|
|
13650
|
+
transition: (productResult) => beginCommit("transition", productResult)
|
|
13651
|
+
} : createUnavailableCommitContext(
|
|
13652
|
+
`Effect ${request.effectKey} is not executing a declared product commit`
|
|
13653
|
+
);
|
|
13237
13654
|
const context = {
|
|
13238
13655
|
...request.context || {},
|
|
13656
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13657
|
+
commit: commitContext,
|
|
13239
13658
|
behaviors: normalizeEffectBehaviors(
|
|
13240
13659
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13241
13660
|
),
|
|
13242
13661
|
invocation: {
|
|
13243
13662
|
mode: resolved.mode,
|
|
13244
|
-
|
|
13245
|
-
|
|
13663
|
+
...commitRequired && declaration ? { commitKind: declaration.kind } : {},
|
|
13664
|
+
sourceEffectKey: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectKey || request.effectKey : request.effectKey,
|
|
13665
|
+
sourceEffectName: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectName || request.effectName : request.effectName,
|
|
13246
13666
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13247
13667
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13248
|
-
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {}
|
|
13668
|
+
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {},
|
|
13669
|
+
...request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
13249
13670
|
}
|
|
13250
13671
|
};
|
|
13251
13672
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13269,6 +13690,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13269
13690
|
handlerFailed = true;
|
|
13270
13691
|
handlerError = error;
|
|
13271
13692
|
}
|
|
13693
|
+
let commitError;
|
|
13694
|
+
let commitFailed = false;
|
|
13695
|
+
if (commitPromise) {
|
|
13696
|
+
try {
|
|
13697
|
+
await commitPromise;
|
|
13698
|
+
} catch (error) {
|
|
13699
|
+
commitFailed = true;
|
|
13700
|
+
commitError = error;
|
|
13701
|
+
}
|
|
13702
|
+
}
|
|
13272
13703
|
let feedbackError;
|
|
13273
13704
|
let feedbackFailed = false;
|
|
13274
13705
|
if (feedbackContext) {
|
|
@@ -13282,9 +13713,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13282
13713
|
if (handlerFailed) {
|
|
13283
13714
|
throw handlerError;
|
|
13284
13715
|
}
|
|
13716
|
+
if (commitFailed) {
|
|
13717
|
+
throw commitError;
|
|
13718
|
+
}
|
|
13285
13719
|
if (feedbackFailed) {
|
|
13286
13720
|
throw feedbackError;
|
|
13287
13721
|
}
|
|
13722
|
+
if (commitRequired && !commitStarted) {
|
|
13723
|
+
throw new Error(
|
|
13724
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13725
|
+
);
|
|
13726
|
+
}
|
|
13288
13727
|
return handlerResult;
|
|
13289
13728
|
}
|
|
13290
13729
|
|
|
@@ -13353,12 +13792,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13353
13792
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13354
13793
|
);
|
|
13355
13794
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13356
|
-
const
|
|
13357
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path2)) {
|
|
13358
|
-
return null;
|
|
13359
|
-
}
|
|
13360
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13361
|
-
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path2) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
13795
|
+
const label = typeof node.label === "string" && node.label.trim() ? node.label.trim() : path2 || id;
|
|
13362
13796
|
return {
|
|
13363
13797
|
path: path2,
|
|
13364
13798
|
className,
|
|
@@ -13368,30 +13802,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13368
13802
|
fields
|
|
13369
13803
|
};
|
|
13370
13804
|
}
|
|
13371
|
-
function isPlaceholderRecordLabel(label, id, path2) {
|
|
13372
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13373
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path2);
|
|
13374
|
-
}
|
|
13375
|
-
function displayLabelFromFields(fields) {
|
|
13376
|
-
const preferredFieldNames = [
|
|
13377
|
-
"name",
|
|
13378
|
-
"title",
|
|
13379
|
-
"label",
|
|
13380
|
-
"display_name",
|
|
13381
|
-
"file_name",
|
|
13382
|
-
"number",
|
|
13383
|
-
"code"
|
|
13384
|
-
];
|
|
13385
|
-
for (const preferred of preferredFieldNames) {
|
|
13386
|
-
const match = fields.find(
|
|
13387
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13388
|
-
);
|
|
13389
|
-
if (typeof match?.value === "string") {
|
|
13390
|
-
return match.value.trim();
|
|
13391
|
-
}
|
|
13392
|
-
}
|
|
13393
|
-
return null;
|
|
13394
|
-
}
|
|
13395
13805
|
function normalizeRecordSearchText(value) {
|
|
13396
13806
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13397
13807
|
}
|
|
@@ -14354,7 +14764,8 @@ function normalizeStateMachines(values) {
|
|
|
14354
14764
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14355
14765
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14356
14766
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14357
|
-
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
14767
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json),
|
|
14768
|
+
outcomes: parseJsonRecord(transition?.outcomes) || parseJsonRecord(transition?.outcomes_json)
|
|
14358
14769
|
})).filter(
|
|
14359
14770
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14360
14771
|
);
|
|
@@ -14447,6 +14858,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14447
14858
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14448
14859
|
);
|
|
14449
14860
|
}
|
|
14861
|
+
if (transition.outcomes) {
|
|
14862
|
+
args.push(
|
|
14863
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14864
|
+
);
|
|
14865
|
+
}
|
|
14450
14866
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14451
14867
|
}
|
|
14452
14868
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14725,7 +15141,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14725
15141
|
name: String!
|
|
14726
15142
|
state_machine: StateMachine!
|
|
14727
15143
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
14728
|
-
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!
|
|
15144
|
+
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!
|
|
14729
15145
|
activate_transition(name: String!): StateMachineMutation!
|
|
14730
15146
|
}
|
|
14731
15147
|
|
|
@@ -14742,6 +15158,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14742
15158
|
type StateMachineSnapshotMutation {
|
|
14743
15159
|
snapshot: StateMachineSnapshot!
|
|
14744
15160
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15161
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14745
15162
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14746
15163
|
}
|
|
14747
15164
|
|
|
@@ -14788,6 +15205,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14788
15205
|
permission_json: String
|
|
14789
15206
|
risk: String
|
|
14790
15207
|
expected_outcome_json: String
|
|
15208
|
+
outcomes_json: String
|
|
14791
15209
|
}
|
|
14792
15210
|
|
|
14793
15211
|
type StateMachinePath {
|
|
@@ -14798,6 +15216,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14798
15216
|
type StateMachineTransitionEvent {
|
|
14799
15217
|
sequence: Int!
|
|
14800
15218
|
occurred_at: Float!
|
|
15219
|
+
commit_id: String
|
|
15220
|
+
outcome: String
|
|
15221
|
+
source_version: String
|
|
15222
|
+
projected_from_mismatch: String
|
|
14801
15223
|
transition: StateMachineTransition!
|
|
14802
15224
|
from: StateMachineState!
|
|
14803
15225
|
to: StateMachineState!
|
|
@@ -14863,7 +15285,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14863
15285
|
requirements_json,
|
|
14864
15286
|
permission_json,
|
|
14865
15287
|
risk,
|
|
14866
|
-
expected_outcome_json
|
|
15288
|
+
expected_outcome_json,
|
|
15289
|
+
outcomes_json
|
|
14867
15290
|
}) => {
|
|
14868
15291
|
await run(
|
|
14869
15292
|
value.target.add_state_machine_transition(
|
|
@@ -14879,7 +15302,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14879
15302
|
requirementsJson: requirements_json,
|
|
14880
15303
|
permissionJson: permission_json,
|
|
14881
15304
|
risk,
|
|
14882
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15305
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15306
|
+
outcomesJson: outcomes_json
|
|
14883
15307
|
}
|
|
14884
15308
|
)
|
|
14885
15309
|
);
|
|
@@ -14900,6 +15324,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14900
15324
|
);
|
|
14901
15325
|
return value;
|
|
14902
15326
|
},
|
|
15327
|
+
commit_transition: async (value, { name, outcome, to, commit_id, source_version }) => {
|
|
15328
|
+
await run(
|
|
15329
|
+
value.target.commit_state_machine_transition(
|
|
15330
|
+
value.name,
|
|
15331
|
+
name,
|
|
15332
|
+
outcome,
|
|
15333
|
+
to,
|
|
15334
|
+
commit_id,
|
|
15335
|
+
source_version
|
|
15336
|
+
)
|
|
15337
|
+
);
|
|
15338
|
+
return value;
|
|
15339
|
+
},
|
|
14903
15340
|
observe_state: async (value, { state, force, source }) => {
|
|
14904
15341
|
await run(
|
|
14905
15342
|
value.target.observe_state_machine_state(
|
|
@@ -14929,7 +15366,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14929
15366
|
requirements_json: (value) => value.requirements_json || null,
|
|
14930
15367
|
permission_json: (value) => value.permission_json || null,
|
|
14931
15368
|
risk: (value) => value.risk || null,
|
|
14932
|
-
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
15369
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null,
|
|
15370
|
+
outcomes_json: (value) => value.outcomes_json || null
|
|
14933
15371
|
},
|
|
14934
15372
|
StateMachinePath: {
|
|
14935
15373
|
states: (value) => value.states,
|
|
@@ -14938,6 +15376,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14938
15376
|
StateMachineTransitionEvent: {
|
|
14939
15377
|
sequence: (value) => value.sequence,
|
|
14940
15378
|
occurred_at: (value) => value.occurred_at,
|
|
15379
|
+
commit_id: (value) => value.commit_id || null,
|
|
15380
|
+
outcome: (value) => value.outcome || null,
|
|
15381
|
+
source_version: (value) => value.source_version || null,
|
|
15382
|
+
projected_from_mismatch: (value) => value.projected_from_mismatch || null,
|
|
14941
15383
|
transition: (value) => value.transition,
|
|
14942
15384
|
from: (value) => value.from,
|
|
14943
15385
|
to: (value) => value.to
|
|
@@ -15011,6 +15453,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15011
15453
|
permission_json
|
|
15012
15454
|
risk
|
|
15013
15455
|
expected_outcome_json
|
|
15456
|
+
outcomes_json
|
|
15014
15457
|
}
|
|
15015
15458
|
}`
|
|
15016
15459
|
]
|
|
@@ -15640,6 +16083,133 @@ var Environment = class _Environment {
|
|
|
15640
16083
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15641
16084
|
};
|
|
15642
16085
|
}
|
|
16086
|
+
/**
|
|
16087
|
+
* Acknowledge a declared product mutation that already happened outside a
|
|
16088
|
+
* Granular-run effect (for example, in a webhook consumer). These methods
|
|
16089
|
+
* run the declaration's pure projection mapper; they never call its handler.
|
|
16090
|
+
*/
|
|
16091
|
+
get commit() {
|
|
16092
|
+
return {
|
|
16093
|
+
effect: async (effect, productResult, options = {}) => this.persistExternalEffect(effect, productResult, options),
|
|
16094
|
+
transition: async (transition, productResult, options = {}) => this.persistExternalTransition(transition, productResult, options),
|
|
16095
|
+
get: async (commitId) => this.controlPlaneRequest(
|
|
16096
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16097
|
+
commitId
|
|
16098
|
+
)}`
|
|
16099
|
+
),
|
|
16100
|
+
retry: async (commitId) => this.controlPlaneRequest(
|
|
16101
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
16102
|
+
commitId
|
|
16103
|
+
)}/retry`,
|
|
16104
|
+
{ method: "POST" }
|
|
16105
|
+
)
|
|
16106
|
+
};
|
|
16107
|
+
}
|
|
16108
|
+
/** Inspect or retry the agent synchronization of a product snapshot. */
|
|
16109
|
+
get observation() {
|
|
16110
|
+
return {
|
|
16111
|
+
get: async (observationId) => this.controlPlaneRequest(
|
|
16112
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16113
|
+
observationId
|
|
16114
|
+
)}`
|
|
16115
|
+
),
|
|
16116
|
+
retry: async (observationId) => this.controlPlaneRequest(
|
|
16117
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
16118
|
+
observationId
|
|
16119
|
+
)}/retry`,
|
|
16120
|
+
{ method: "POST" }
|
|
16121
|
+
)
|
|
16122
|
+
};
|
|
16123
|
+
}
|
|
16124
|
+
async persistExternalEffect(effect, productResult, options) {
|
|
16125
|
+
const projection = effect.commit.project(productResult);
|
|
16126
|
+
validateProjectionResult(effect.commit, projection);
|
|
16127
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16128
|
+
return this.controlPlaneRequest(
|
|
16129
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16130
|
+
{
|
|
16131
|
+
method: "POST",
|
|
16132
|
+
body: JSON.stringify({
|
|
16133
|
+
kind: "effect",
|
|
16134
|
+
effectKey: computeEffectKey2(effect),
|
|
16135
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16136
|
+
projection
|
|
16137
|
+
})
|
|
16138
|
+
}
|
|
16139
|
+
);
|
|
16140
|
+
}
|
|
16141
|
+
async persistExternalTransition(transition, productResult, options) {
|
|
16142
|
+
const metadata = getDefinedTransitionMetadata(transition);
|
|
16143
|
+
if (!metadata) {
|
|
16144
|
+
throw new Error(
|
|
16145
|
+
"environment.commit.transition requires a transition returned by defineStateMachine"
|
|
16146
|
+
);
|
|
16147
|
+
}
|
|
16148
|
+
const projection = transition.effect.commit.project(productResult);
|
|
16149
|
+
validateProjectionResult(transition.effect.commit, projection);
|
|
16150
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
16151
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
16152
|
+
transition.outcomes,
|
|
16153
|
+
projection.outcome.key
|
|
16154
|
+
)) {
|
|
16155
|
+
throw new Error(
|
|
16156
|
+
`Transition ${metadata.machine}.${metadata.transition} does not declare outcome ${projection.outcome.key}`
|
|
16157
|
+
);
|
|
16158
|
+
}
|
|
16159
|
+
if (!projection.primaryTarget) {
|
|
16160
|
+
throw new Error(
|
|
16161
|
+
"An external transition projection requires primaryTarget to identify the transitioned product record"
|
|
16162
|
+
);
|
|
16163
|
+
}
|
|
16164
|
+
return this.controlPlaneRequest(
|
|
16165
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
16166
|
+
{
|
|
16167
|
+
method: "POST",
|
|
16168
|
+
body: JSON.stringify({
|
|
16169
|
+
kind: "transition",
|
|
16170
|
+
effectKey: computeEffectKey2(transition.effect),
|
|
16171
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
16172
|
+
projection,
|
|
16173
|
+
transition: {
|
|
16174
|
+
className: projection.primaryTarget.className,
|
|
16175
|
+
objectId: projection.primaryTarget.id,
|
|
16176
|
+
...projection.primaryTarget.path ? { objectPath: projection.primaryTarget.path } : {},
|
|
16177
|
+
machine: metadata.machine,
|
|
16178
|
+
transition: metadata.transition,
|
|
16179
|
+
from: transition.from
|
|
16180
|
+
}
|
|
16181
|
+
})
|
|
16182
|
+
}
|
|
16183
|
+
);
|
|
16184
|
+
}
|
|
16185
|
+
requireExternalIdentity(sourceVersion, options) {
|
|
16186
|
+
if (!options.sourceEventId?.trim() && !sourceVersion?.trim()) {
|
|
16187
|
+
throw new Error(
|
|
16188
|
+
"An external commit requires sourceEventId or a source version from its projection mapper"
|
|
16189
|
+
);
|
|
16190
|
+
}
|
|
16191
|
+
}
|
|
16192
|
+
/**
|
|
16193
|
+
* Synchronize a versioned product snapshot without claiming an effect or
|
|
16194
|
+
* lifecycle transition. This records no transition history.
|
|
16195
|
+
*/
|
|
16196
|
+
async observe(mapper, productResult) {
|
|
16197
|
+
const declaration = { kind: "effect"};
|
|
16198
|
+
const projection = mapper(productResult);
|
|
16199
|
+
validateProjectionResult(declaration, projection);
|
|
16200
|
+
if (!projection.source.version?.trim()) {
|
|
16201
|
+
throw new Error(
|
|
16202
|
+
"environment.observe requires a monotonic source version or serialized adapter sequence"
|
|
16203
|
+
);
|
|
16204
|
+
}
|
|
16205
|
+
return this.controlPlaneRequest(
|
|
16206
|
+
`/control/environments/${this.environmentId}/observations`,
|
|
16207
|
+
{
|
|
16208
|
+
method: "POST",
|
|
16209
|
+
body: JSON.stringify({ projection })
|
|
16210
|
+
}
|
|
16211
|
+
);
|
|
16212
|
+
}
|
|
15643
16213
|
/**
|
|
15644
16214
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15645
16215
|
* own the customer application's state machine.
|
|
@@ -17046,6 +17616,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17046
17616
|
}
|
|
17047
17617
|
};
|
|
17048
17618
|
}
|
|
17619
|
+
get mutations() {
|
|
17620
|
+
return {
|
|
17621
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17622
|
+
"/mutations",
|
|
17623
|
+
options
|
|
17624
|
+
)
|
|
17625
|
+
};
|
|
17626
|
+
}
|
|
17049
17627
|
get artifacts() {
|
|
17050
17628
|
return {
|
|
17051
17629
|
list: (options = {}) => {
|
|
@@ -18545,6 +19123,7 @@ var Granular = class _Granular {
|
|
|
18545
19123
|
const serialized = {
|
|
18546
19124
|
effectKey: computeEffectKey2(effect),
|
|
18547
19125
|
name: effect.name,
|
|
19126
|
+
...effect.label ? { label: effect.label } : {},
|
|
18548
19127
|
description: effect.description,
|
|
18549
19128
|
inputSchema: effect.inputSchema,
|
|
18550
19129
|
stability: effect.stability || "stable",
|
|
@@ -18568,6 +19147,9 @@ var Granular = class _Granular {
|
|
|
18568
19147
|
if (effect.metamodels !== void 0) {
|
|
18569
19148
|
serialized.metamodels = effect.metamodels;
|
|
18570
19149
|
}
|
|
19150
|
+
if (effect.commit !== void 0) {
|
|
19151
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19152
|
+
}
|
|
18571
19153
|
return serialized;
|
|
18572
19154
|
}
|
|
18573
19155
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18788,16 +19370,60 @@ var Granular = class _Granular {
|
|
|
18788
19370
|
};
|
|
18789
19371
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18790
19372
|
const request = params;
|
|
18791
|
-
|
|
19373
|
+
let commitReceipt;
|
|
19374
|
+
const result = await invokeRegisteredEffect(
|
|
18792
19375
|
this.getSandboxEffectMap(sandboxId),
|
|
18793
19376
|
request,
|
|
18794
19377
|
{
|
|
19378
|
+
commitAcknowledged: (receipt) => {
|
|
19379
|
+
commitReceipt = receipt;
|
|
19380
|
+
},
|
|
19381
|
+
commit: {
|
|
19382
|
+
persist: (commitRequest) => this.request(
|
|
19383
|
+
`/control/environments/${encodeURIComponent(
|
|
19384
|
+
commitRequest.environmentId
|
|
19385
|
+
)}/commits`,
|
|
19386
|
+
{
|
|
19387
|
+
method: "POST",
|
|
19388
|
+
body: JSON.stringify({
|
|
19389
|
+
kind: commitRequest.kind,
|
|
19390
|
+
invocationId: commitRequest.invocationId,
|
|
19391
|
+
idempotencyKey: commitRequest.idempotencyKey,
|
|
19392
|
+
effectKey: commitRequest.effectKey,
|
|
19393
|
+
projection: commitRequest.projection
|
|
19394
|
+
})
|
|
19395
|
+
}
|
|
19396
|
+
),
|
|
19397
|
+
mappingFailed: (failure) => this.request(
|
|
19398
|
+
`/control/environments/${encodeURIComponent(
|
|
19399
|
+
failure.environmentId
|
|
19400
|
+
)}/commit-invocations/${encodeURIComponent(
|
|
19401
|
+
failure.invocationId
|
|
19402
|
+
)}`,
|
|
19403
|
+
{
|
|
19404
|
+
method: "PATCH",
|
|
19405
|
+
body: JSON.stringify({
|
|
19406
|
+
status: "mapping_failed",
|
|
19407
|
+
error: {
|
|
19408
|
+
code: "commit_projection_mapping_failed",
|
|
19409
|
+
message: failure.message,
|
|
19410
|
+
retryable: false
|
|
19411
|
+
}
|
|
19412
|
+
})
|
|
19413
|
+
}
|
|
19414
|
+
)
|
|
19415
|
+
},
|
|
18795
19416
|
feedback: {
|
|
18796
19417
|
invocationId: request.callId,
|
|
18797
19418
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18798
19419
|
}
|
|
18799
19420
|
}
|
|
18800
19421
|
);
|
|
19422
|
+
return {
|
|
19423
|
+
__granularEffectInvocationResult: true,
|
|
19424
|
+
result,
|
|
19425
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19426
|
+
};
|
|
18801
19427
|
});
|
|
18802
19428
|
wsClient.on("open", () => {
|
|
18803
19429
|
void this.synchronizeEffectHost(host).catch((error) => {
|