@granular-software/sdk 0.4.63 → 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 +713 -65
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +713 -65
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +713 -65
- package/dist/{client-lk-dmdKR.d.mts → client-S-z8bri1.d.mts} +47 -1
- package/dist/{client-KqJIH7WE.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 +1058 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1052 -84
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-DtsHRNb5.d.mts → spend-CESUvrZC.d.mts} +239 -4
- package/dist/{spend-DtsHRNb5.d.ts → spend-CESUvrZC.d.ts} +239 -4
- 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
|
|
@@ -12198,8 +12207,12 @@ external_exports.union([
|
|
|
12198
12207
|
external_exports.array(external_exports.string()),
|
|
12199
12208
|
external_exports.object({
|
|
12200
12209
|
values: external_exports.array(external_exports.string()),
|
|
12210
|
+
labels: external_exports.array(external_exports.string().min(1)).optional(),
|
|
12201
12211
|
message: external_exports.string().optional()
|
|
12202
|
-
}).strict()
|
|
12212
|
+
}).strict().refine(
|
|
12213
|
+
(rule) => !rule.labels || rule.labels.length === rule.values.length,
|
|
12214
|
+
{ message: "Enum labels must match enum values one-for-one" }
|
|
12215
|
+
)
|
|
12203
12216
|
]);
|
|
12204
12217
|
external_exports.union([
|
|
12205
12218
|
external_exports.boolean(),
|
|
@@ -12227,7 +12240,7 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
12227
12240
|
external_exports.string(),
|
|
12228
12241
|
external_exports.object({
|
|
12229
12242
|
name: external_exports.string().min(1),
|
|
12230
|
-
label: external_exports.string().optional(),
|
|
12243
|
+
label: external_exports.string().min(1).optional(),
|
|
12231
12244
|
description: external_exports.string().optional(),
|
|
12232
12245
|
isFinal: external_exports.boolean().optional()
|
|
12233
12246
|
}).strict()
|
|
@@ -12313,6 +12326,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12313
12326
|
summary: external_exports.string().optional()
|
|
12314
12327
|
}).strict()
|
|
12315
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();
|
|
12316
12335
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12317
12336
|
name: external_exports.string().min(1),
|
|
12318
12337
|
from: external_exports.string().min(1),
|
|
@@ -12324,7 +12343,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12324
12343
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12325
12344
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12326
12345
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12327
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12346
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12347
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12328
12348
|
}).strict();
|
|
12329
12349
|
external_exports.object({
|
|
12330
12350
|
name: external_exports.string().min(1),
|
|
@@ -13005,6 +13025,238 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
13005
13025
|
}
|
|
13006
13026
|
});
|
|
13007
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
|
+
|
|
13008
13260
|
// src/effect-runtime.ts
|
|
13009
13261
|
function computeEffectKey(effect) {
|
|
13010
13262
|
const attachedClass = effect.className?.trim();
|
|
@@ -13069,6 +13321,45 @@ function resolveInvocationMode(context) {
|
|
|
13069
13321
|
}
|
|
13070
13322
|
return "execute";
|
|
13071
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
|
+
}
|
|
13072
13363
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13073
13364
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13074
13365
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13121,7 +13412,7 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13121
13412
|
if (mode === "artifactOptions") {
|
|
13122
13413
|
if (!effect.artifactOptionsHandler) {
|
|
13123
13414
|
throw new Error(
|
|
13124
|
-
`Artifact
|
|
13415
|
+
`Artifact field options are not supported for ${request.effectKey}`
|
|
13125
13416
|
);
|
|
13126
13417
|
}
|
|
13127
13418
|
return {
|
|
@@ -13140,22 +13431,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13140
13431
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13141
13432
|
}
|
|
13142
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
|
+
}
|
|
13143
13457
|
if (effect.reverseHandler) {
|
|
13144
13458
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13145
13459
|
}
|
|
13146
|
-
const reverseEffect = resolveReverseEffect(
|
|
13147
|
-
effectMap,
|
|
13148
|
-
effect,
|
|
13149
|
-
request,
|
|
13150
|
-
behaviors
|
|
13151
|
-
);
|
|
13152
|
-
if (reverseEffect) {
|
|
13153
|
-
return {
|
|
13154
|
-
effect: reverseEffect,
|
|
13155
|
-
mode,
|
|
13156
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13157
|
-
};
|
|
13158
|
-
}
|
|
13159
13460
|
throw new Error(
|
|
13160
13461
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13161
13462
|
);
|
|
@@ -13256,18 +13557,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13256
13557
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13257
13558
|
}
|
|
13258
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
|
+
);
|
|
13259
13680
|
const context = {
|
|
13260
13681
|
...request.context || {},
|
|
13682
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13683
|
+
commit: commitContext,
|
|
13261
13684
|
behaviors: normalizeEffectBehaviors(
|
|
13262
13685
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13263
13686
|
),
|
|
13264
13687
|
invocation: {
|
|
13265
13688
|
mode: resolved.mode,
|
|
13266
|
-
|
|
13267
|
-
|
|
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,
|
|
13268
13692
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13269
13693
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13270
|
-
...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 } : {}
|
|
13271
13696
|
}
|
|
13272
13697
|
};
|
|
13273
13698
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13291,6 +13716,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13291
13716
|
handlerFailed = true;
|
|
13292
13717
|
handlerError = error;
|
|
13293
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
|
+
}
|
|
13294
13729
|
let feedbackError;
|
|
13295
13730
|
let feedbackFailed = false;
|
|
13296
13731
|
if (feedbackContext) {
|
|
@@ -13304,9 +13739,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13304
13739
|
if (handlerFailed) {
|
|
13305
13740
|
throw handlerError;
|
|
13306
13741
|
}
|
|
13742
|
+
if (commitFailed) {
|
|
13743
|
+
throw commitError;
|
|
13744
|
+
}
|
|
13307
13745
|
if (feedbackFailed) {
|
|
13308
13746
|
throw feedbackError;
|
|
13309
13747
|
}
|
|
13748
|
+
if (commitRequired && !commitStarted) {
|
|
13749
|
+
throw new Error(
|
|
13750
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13751
|
+
);
|
|
13752
|
+
}
|
|
13310
13753
|
return handlerResult;
|
|
13311
13754
|
}
|
|
13312
13755
|
|
|
@@ -13375,12 +13818,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13375
13818
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13376
13819
|
);
|
|
13377
13820
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13378
|
-
const
|
|
13379
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path2)) {
|
|
13380
|
-
return null;
|
|
13381
|
-
}
|
|
13382
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13383
|
-
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;
|
|
13384
13822
|
return {
|
|
13385
13823
|
path: path2,
|
|
13386
13824
|
className,
|
|
@@ -13390,30 +13828,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13390
13828
|
fields
|
|
13391
13829
|
};
|
|
13392
13830
|
}
|
|
13393
|
-
function isPlaceholderRecordLabel(label, id, path2) {
|
|
13394
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13395
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path2);
|
|
13396
|
-
}
|
|
13397
|
-
function displayLabelFromFields(fields) {
|
|
13398
|
-
const preferredFieldNames = [
|
|
13399
|
-
"name",
|
|
13400
|
-
"title",
|
|
13401
|
-
"label",
|
|
13402
|
-
"display_name",
|
|
13403
|
-
"file_name",
|
|
13404
|
-
"number",
|
|
13405
|
-
"code"
|
|
13406
|
-
];
|
|
13407
|
-
for (const preferred of preferredFieldNames) {
|
|
13408
|
-
const match = fields.find(
|
|
13409
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13410
|
-
);
|
|
13411
|
-
if (typeof match?.value === "string") {
|
|
13412
|
-
return match.value.trim();
|
|
13413
|
-
}
|
|
13414
|
-
}
|
|
13415
|
-
return null;
|
|
13416
|
-
}
|
|
13417
13831
|
function normalizeRecordSearchText(value) {
|
|
13418
13832
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13419
13833
|
}
|
|
@@ -13623,18 +14037,24 @@ function normalizeEnumInput(enumSpec) {
|
|
|
13623
14037
|
(value) => typeof value === "string" && value.length > 0
|
|
13624
14038
|
);
|
|
13625
14039
|
if (values.length === 0) return null;
|
|
13626
|
-
|
|
14040
|
+
const labels = Array.isArray(config.labels) && config.labels.length === values.length ? config.labels : values;
|
|
14041
|
+
return {
|
|
14042
|
+
values,
|
|
14043
|
+
labels,
|
|
14044
|
+
...config.message ? { message: config.message } : {}
|
|
14045
|
+
};
|
|
13627
14046
|
}
|
|
13628
14047
|
function buildEnumFieldMutations(fieldPath, enumSpec) {
|
|
13629
14048
|
const normalized = normalizeEnumInput(enumSpec);
|
|
13630
14049
|
if (!normalized) return [];
|
|
13631
14050
|
const messageArg = normalized.message ? `, message: ${JSON.stringify(normalized.message)}` : "";
|
|
14051
|
+
const labelsArg = normalized.labels ? `, labels: ${JSON.stringify(normalized.labels)}` : "";
|
|
13632
14052
|
return [
|
|
13633
14053
|
{
|
|
13634
14054
|
label: `set enum on ${fieldPath}`,
|
|
13635
14055
|
query: `mutation { at(path: ${JSON.stringify(fieldPath)}) { set_enum(values: ${JSON.stringify(
|
|
13636
14056
|
normalized.values
|
|
13637
|
-
)}${messageArg}) { values } } }`
|
|
14057
|
+
)}${labelsArg}${messageArg}) { values labels } } }`
|
|
13638
14058
|
}
|
|
13639
14059
|
];
|
|
13640
14060
|
}
|
|
@@ -13644,7 +14064,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13644
14064
|
fieldRows: [
|
|
13645
14065
|
{
|
|
13646
14066
|
key: "enum",
|
|
13647
|
-
description: 'Allowed values.
|
|
14067
|
+
description: 'Allowed values. Use `{ "values": [...], "labels": [...] }` for authored display labels; otherwise each unchanged value is its display fallback.'
|
|
13648
14068
|
}
|
|
13649
14069
|
]
|
|
13650
14070
|
},
|
|
@@ -13654,6 +14074,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13654
14074
|
type EnumMetamodel {
|
|
13655
14075
|
model: Model!
|
|
13656
14076
|
values: [String!]!
|
|
14077
|
+
labels: [String!]!
|
|
13657
14078
|
message: String
|
|
13658
14079
|
}
|
|
13659
14080
|
|
|
@@ -13662,7 +14083,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13662
14083
|
}
|
|
13663
14084
|
|
|
13664
14085
|
extend type ModelMutation {
|
|
13665
|
-
set_enum(values: [String!]!, message: String): EnumMetamodel
|
|
14086
|
+
set_enum(values: [String!]!, labels: [String!], message: String): EnumMetamodel
|
|
13666
14087
|
}
|
|
13667
14088
|
`
|
|
13668
14089
|
],
|
|
@@ -13671,15 +14092,19 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13671
14092
|
EnumMetamodel: {
|
|
13672
14093
|
model: (value) => value.model,
|
|
13673
14094
|
values: (value) => value.values,
|
|
14095
|
+
labels: (value) => value.labels || [],
|
|
13674
14096
|
message: (value) => value.message || null
|
|
13675
14097
|
},
|
|
13676
14098
|
Model: {
|
|
13677
14099
|
enum_rule: async (ant) => await run(ant.enum_rule())
|
|
13678
14100
|
},
|
|
13679
14101
|
ModelMutation: {
|
|
13680
|
-
set_enum: async (ant, { values, message }) => {
|
|
13681
|
-
const
|
|
13682
|
-
|
|
14102
|
+
set_enum: async (ant, { values, labels, message }) => {
|
|
14103
|
+
const resolvedLabels = Array.isArray(labels) && labels.length === values.length ? labels : values;
|
|
14104
|
+
const model = await run(
|
|
14105
|
+
ant.set_enum(values, resolvedLabels, message)
|
|
14106
|
+
);
|
|
14107
|
+
return { model, values, labels: resolvedLabels, message };
|
|
13683
14108
|
}
|
|
13684
14109
|
}
|
|
13685
14110
|
};
|
|
@@ -13692,7 +14117,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13692
14117
|
},
|
|
13693
14118
|
summary: {
|
|
13694
14119
|
selections: {
|
|
13695
|
-
propertyFields: [`enum_rule { values message }`]
|
|
14120
|
+
propertyFields: [`enum_rule { values labels message }`]
|
|
13696
14121
|
},
|
|
13697
14122
|
readPropertySummary(rawProperty) {
|
|
13698
14123
|
const values = Array.isArray(rawProperty.enum_rule?.values) ? rawProperty.enum_rule.values.filter(
|
|
@@ -13700,8 +14125,15 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13700
14125
|
) : [];
|
|
13701
14126
|
if (values.length === 0) return { enumRule: null };
|
|
13702
14127
|
const message = typeof rawProperty.enum_rule?.message === "string" ? rawProperty.enum_rule.message : null;
|
|
14128
|
+
const labels = Array.isArray(rawProperty.enum_rule?.labels) ? rawProperty.enum_rule.labels.filter(
|
|
14129
|
+
(label) => typeof label === "string" && label.length > 0
|
|
14130
|
+
) : [];
|
|
13703
14131
|
return {
|
|
13704
|
-
enumRule:
|
|
14132
|
+
enumRule: {
|
|
14133
|
+
values,
|
|
14134
|
+
...labels.length === values.length ? { labels } : {},
|
|
14135
|
+
...message ? { message } : {}
|
|
14136
|
+
}
|
|
13705
14137
|
};
|
|
13706
14138
|
}
|
|
13707
14139
|
},
|
|
@@ -14358,7 +14790,8 @@ function normalizeStateMachines(values) {
|
|
|
14358
14790
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14359
14791
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14360
14792
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14361
|
-
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)
|
|
14362
14795
|
})).filter(
|
|
14363
14796
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14364
14797
|
);
|
|
@@ -14451,6 +14884,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14451
14884
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14452
14885
|
);
|
|
14453
14886
|
}
|
|
14887
|
+
if (transition.outcomes) {
|
|
14888
|
+
args.push(
|
|
14889
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14890
|
+
);
|
|
14891
|
+
}
|
|
14454
14892
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14455
14893
|
}
|
|
14456
14894
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14729,7 +15167,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14729
15167
|
name: String!
|
|
14730
15168
|
state_machine: StateMachine!
|
|
14731
15169
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
14732
|
-
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!
|
|
14733
15171
|
activate_transition(name: String!): StateMachineMutation!
|
|
14734
15172
|
}
|
|
14735
15173
|
|
|
@@ -14746,6 +15184,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14746
15184
|
type StateMachineSnapshotMutation {
|
|
14747
15185
|
snapshot: StateMachineSnapshot!
|
|
14748
15186
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15187
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14749
15188
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14750
15189
|
}
|
|
14751
15190
|
|
|
@@ -14792,6 +15231,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14792
15231
|
permission_json: String
|
|
14793
15232
|
risk: String
|
|
14794
15233
|
expected_outcome_json: String
|
|
15234
|
+
outcomes_json: String
|
|
14795
15235
|
}
|
|
14796
15236
|
|
|
14797
15237
|
type StateMachinePath {
|
|
@@ -14802,6 +15242,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14802
15242
|
type StateMachineTransitionEvent {
|
|
14803
15243
|
sequence: Int!
|
|
14804
15244
|
occurred_at: Float!
|
|
15245
|
+
commit_id: String
|
|
15246
|
+
outcome: String
|
|
15247
|
+
source_version: String
|
|
15248
|
+
projected_from_mismatch: String
|
|
14805
15249
|
transition: StateMachineTransition!
|
|
14806
15250
|
from: StateMachineState!
|
|
14807
15251
|
to: StateMachineState!
|
|
@@ -14867,7 +15311,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14867
15311
|
requirements_json,
|
|
14868
15312
|
permission_json,
|
|
14869
15313
|
risk,
|
|
14870
|
-
expected_outcome_json
|
|
15314
|
+
expected_outcome_json,
|
|
15315
|
+
outcomes_json
|
|
14871
15316
|
}) => {
|
|
14872
15317
|
await run(
|
|
14873
15318
|
value.target.add_state_machine_transition(
|
|
@@ -14883,7 +15328,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14883
15328
|
requirementsJson: requirements_json,
|
|
14884
15329
|
permissionJson: permission_json,
|
|
14885
15330
|
risk,
|
|
14886
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15331
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15332
|
+
outcomesJson: outcomes_json
|
|
14887
15333
|
}
|
|
14888
15334
|
)
|
|
14889
15335
|
);
|
|
@@ -14904,6 +15350,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14904
15350
|
);
|
|
14905
15351
|
return value;
|
|
14906
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
|
+
},
|
|
14907
15366
|
observe_state: async (value, { state, force, source }) => {
|
|
14908
15367
|
await run(
|
|
14909
15368
|
value.target.observe_state_machine_state(
|
|
@@ -14933,7 +15392,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14933
15392
|
requirements_json: (value) => value.requirements_json || null,
|
|
14934
15393
|
permission_json: (value) => value.permission_json || null,
|
|
14935
15394
|
risk: (value) => value.risk || null,
|
|
14936
|
-
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
|
|
14937
15397
|
},
|
|
14938
15398
|
StateMachinePath: {
|
|
14939
15399
|
states: (value) => value.states,
|
|
@@ -14942,6 +15402,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14942
15402
|
StateMachineTransitionEvent: {
|
|
14943
15403
|
sequence: (value) => value.sequence,
|
|
14944
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,
|
|
14945
15409
|
transition: (value) => value.transition,
|
|
14946
15410
|
from: (value) => value.from,
|
|
14947
15411
|
to: (value) => value.to
|
|
@@ -15015,6 +15479,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15015
15479
|
permission_json
|
|
15016
15480
|
risk
|
|
15017
15481
|
expected_outcome_json
|
|
15482
|
+
outcomes_json
|
|
15018
15483
|
}
|
|
15019
15484
|
}`
|
|
15020
15485
|
]
|
|
@@ -15644,6 +16109,133 @@ var Environment = class _Environment {
|
|
|
15644
16109
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15645
16110
|
};
|
|
15646
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
|
+
}
|
|
15647
16239
|
/**
|
|
15648
16240
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15649
16241
|
* own the customer application's state machine.
|
|
@@ -17050,6 +17642,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17050
17642
|
}
|
|
17051
17643
|
};
|
|
17052
17644
|
}
|
|
17645
|
+
get mutations() {
|
|
17646
|
+
return {
|
|
17647
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17648
|
+
"/mutations",
|
|
17649
|
+
options
|
|
17650
|
+
)
|
|
17651
|
+
};
|
|
17652
|
+
}
|
|
17053
17653
|
get artifacts() {
|
|
17054
17654
|
return {
|
|
17055
17655
|
list: (options = {}) => {
|
|
@@ -18549,6 +19149,7 @@ var Granular = class _Granular {
|
|
|
18549
19149
|
const serialized = {
|
|
18550
19150
|
effectKey: computeEffectKey2(effect),
|
|
18551
19151
|
name: effect.name,
|
|
19152
|
+
...effect.label ? { label: effect.label } : {},
|
|
18552
19153
|
description: effect.description,
|
|
18553
19154
|
inputSchema: effect.inputSchema,
|
|
18554
19155
|
stability: effect.stability || "stable",
|
|
@@ -18572,6 +19173,9 @@ var Granular = class _Granular {
|
|
|
18572
19173
|
if (effect.metamodels !== void 0) {
|
|
18573
19174
|
serialized.metamodels = effect.metamodels;
|
|
18574
19175
|
}
|
|
19176
|
+
if (effect.commit !== void 0) {
|
|
19177
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19178
|
+
}
|
|
18575
19179
|
return serialized;
|
|
18576
19180
|
}
|
|
18577
19181
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18792,16 +19396,60 @@ var Granular = class _Granular {
|
|
|
18792
19396
|
};
|
|
18793
19397
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18794
19398
|
const request = params;
|
|
18795
|
-
|
|
19399
|
+
let commitReceipt;
|
|
19400
|
+
const result = await invokeRegisteredEffect(
|
|
18796
19401
|
this.getSandboxEffectMap(sandboxId),
|
|
18797
19402
|
request,
|
|
18798
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
|
+
},
|
|
18799
19442
|
feedback: {
|
|
18800
19443
|
invocationId: request.callId,
|
|
18801
19444
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18802
19445
|
}
|
|
18803
19446
|
}
|
|
18804
19447
|
);
|
|
19448
|
+
return {
|
|
19449
|
+
__granularEffectInvocationResult: true,
|
|
19450
|
+
result,
|
|
19451
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19452
|
+
};
|
|
18805
19453
|
});
|
|
18806
19454
|
wsClient.on("open", () => {
|
|
18807
19455
|
void this.synchronizeEffectHost(host).catch((error) => {
|