@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.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
|
|
@@ -12172,8 +12181,12 @@ external_exports.union([
|
|
|
12172
12181
|
external_exports.array(external_exports.string()),
|
|
12173
12182
|
external_exports.object({
|
|
12174
12183
|
values: external_exports.array(external_exports.string()),
|
|
12184
|
+
labels: external_exports.array(external_exports.string().min(1)).optional(),
|
|
12175
12185
|
message: external_exports.string().optional()
|
|
12176
|
-
}).strict()
|
|
12186
|
+
}).strict().refine(
|
|
12187
|
+
(rule) => !rule.labels || rule.labels.length === rule.values.length,
|
|
12188
|
+
{ message: "Enum labels must match enum values one-for-one" }
|
|
12189
|
+
)
|
|
12177
12190
|
]);
|
|
12178
12191
|
external_exports.union([
|
|
12179
12192
|
external_exports.boolean(),
|
|
@@ -12201,7 +12214,7 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
12201
12214
|
external_exports.string(),
|
|
12202
12215
|
external_exports.object({
|
|
12203
12216
|
name: external_exports.string().min(1),
|
|
12204
|
-
label: external_exports.string().optional(),
|
|
12217
|
+
label: external_exports.string().min(1).optional(),
|
|
12205
12218
|
description: external_exports.string().optional(),
|
|
12206
12219
|
isFinal: external_exports.boolean().optional()
|
|
12207
12220
|
}).strict()
|
|
@@ -12287,6 +12300,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
12287
12300
|
summary: external_exports.string().optional()
|
|
12288
12301
|
}).strict()
|
|
12289
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();
|
|
12290
12309
|
var StateMachineTransitionSchema = external_exports.object({
|
|
12291
12310
|
name: external_exports.string().min(1),
|
|
12292
12311
|
from: external_exports.string().min(1),
|
|
@@ -12298,7 +12317,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12298
12317
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
12299
12318
|
permission: StateTransitionPermissionSchema.optional(),
|
|
12300
12319
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
12301
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
12320
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
12321
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
12302
12322
|
}).strict();
|
|
12303
12323
|
external_exports.object({
|
|
12304
12324
|
name: external_exports.string().min(1),
|
|
@@ -12979,6 +12999,238 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
12979
12999
|
}
|
|
12980
13000
|
});
|
|
12981
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
|
+
|
|
12982
13234
|
// src/effect-runtime.ts
|
|
12983
13235
|
function computeEffectKey(effect) {
|
|
12984
13236
|
const attachedClass = effect.className?.trim();
|
|
@@ -13043,6 +13295,45 @@ function resolveInvocationMode(context) {
|
|
|
13043
13295
|
}
|
|
13044
13296
|
return "execute";
|
|
13045
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
|
+
}
|
|
13046
13337
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
13047
13338
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
13048
13339
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -13095,7 +13386,7 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13095
13386
|
if (mode === "artifactOptions") {
|
|
13096
13387
|
if (!effect.artifactOptionsHandler) {
|
|
13097
13388
|
throw new Error(
|
|
13098
|
-
`Artifact
|
|
13389
|
+
`Artifact field options are not supported for ${request.effectKey}`
|
|
13099
13390
|
);
|
|
13100
13391
|
}
|
|
13101
13392
|
return {
|
|
@@ -13114,22 +13405,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
13114
13405
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
13115
13406
|
}
|
|
13116
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
|
+
}
|
|
13117
13431
|
if (effect.reverseHandler) {
|
|
13118
13432
|
return { effect, mode, handler: effect.reverseHandler };
|
|
13119
13433
|
}
|
|
13120
|
-
const reverseEffect = resolveReverseEffect(
|
|
13121
|
-
effectMap,
|
|
13122
|
-
effect,
|
|
13123
|
-
request,
|
|
13124
|
-
behaviors
|
|
13125
|
-
);
|
|
13126
|
-
if (reverseEffect) {
|
|
13127
|
-
return {
|
|
13128
|
-
effect: reverseEffect,
|
|
13129
|
-
mode,
|
|
13130
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
13131
|
-
};
|
|
13132
|
-
}
|
|
13133
13434
|
throw new Error(
|
|
13134
13435
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
13135
13436
|
);
|
|
@@ -13230,18 +13531,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13230
13531
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
13231
13532
|
}
|
|
13232
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
|
+
);
|
|
13233
13654
|
const context = {
|
|
13234
13655
|
...request.context || {},
|
|
13656
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
13657
|
+
commit: commitContext,
|
|
13235
13658
|
behaviors: normalizeEffectBehaviors(
|
|
13236
13659
|
request.context?.behaviors || effect.metamodels || void 0
|
|
13237
13660
|
),
|
|
13238
13661
|
invocation: {
|
|
13239
13662
|
mode: resolved.mode,
|
|
13240
|
-
|
|
13241
|
-
|
|
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,
|
|
13242
13666
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
13243
13667
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
13244
|
-
...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 } : {}
|
|
13245
13670
|
}
|
|
13246
13671
|
};
|
|
13247
13672
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -13265,6 +13690,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13265
13690
|
handlerFailed = true;
|
|
13266
13691
|
handlerError = error;
|
|
13267
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
|
+
}
|
|
13268
13703
|
let feedbackError;
|
|
13269
13704
|
let feedbackFailed = false;
|
|
13270
13705
|
if (feedbackContext) {
|
|
@@ -13278,9 +13713,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
13278
13713
|
if (handlerFailed) {
|
|
13279
13714
|
throw handlerError;
|
|
13280
13715
|
}
|
|
13716
|
+
if (commitFailed) {
|
|
13717
|
+
throw commitError;
|
|
13718
|
+
}
|
|
13281
13719
|
if (feedbackFailed) {
|
|
13282
13720
|
throw feedbackError;
|
|
13283
13721
|
}
|
|
13722
|
+
if (commitRequired && !commitStarted) {
|
|
13723
|
+
throw new Error(
|
|
13724
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
13725
|
+
);
|
|
13726
|
+
}
|
|
13284
13727
|
return handlerResult;
|
|
13285
13728
|
}
|
|
13286
13729
|
|
|
@@ -13349,12 +13792,7 @@ function toRecordSearchResult(className, node) {
|
|
|
13349
13792
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
13350
13793
|
);
|
|
13351
13794
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
13352
|
-
const
|
|
13353
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path2)) {
|
|
13354
|
-
return null;
|
|
13355
|
-
}
|
|
13356
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
13357
|
-
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;
|
|
13358
13796
|
return {
|
|
13359
13797
|
path: path2,
|
|
13360
13798
|
className,
|
|
@@ -13364,30 +13802,6 @@ function toRecordSearchResult(className, node) {
|
|
|
13364
13802
|
fields
|
|
13365
13803
|
};
|
|
13366
13804
|
}
|
|
13367
|
-
function isPlaceholderRecordLabel(label, id, path2) {
|
|
13368
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
13369
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path2);
|
|
13370
|
-
}
|
|
13371
|
-
function displayLabelFromFields(fields) {
|
|
13372
|
-
const preferredFieldNames = [
|
|
13373
|
-
"name",
|
|
13374
|
-
"title",
|
|
13375
|
-
"label",
|
|
13376
|
-
"display_name",
|
|
13377
|
-
"file_name",
|
|
13378
|
-
"number",
|
|
13379
|
-
"code"
|
|
13380
|
-
];
|
|
13381
|
-
for (const preferred of preferredFieldNames) {
|
|
13382
|
-
const match = fields.find(
|
|
13383
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
13384
|
-
);
|
|
13385
|
-
if (typeof match?.value === "string") {
|
|
13386
|
-
return match.value.trim();
|
|
13387
|
-
}
|
|
13388
|
-
}
|
|
13389
|
-
return null;
|
|
13390
|
-
}
|
|
13391
13805
|
function normalizeRecordSearchText(value) {
|
|
13392
13806
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
13393
13807
|
}
|
|
@@ -13597,18 +14011,24 @@ function normalizeEnumInput(enumSpec) {
|
|
|
13597
14011
|
(value) => typeof value === "string" && value.length > 0
|
|
13598
14012
|
);
|
|
13599
14013
|
if (values.length === 0) return null;
|
|
13600
|
-
|
|
14014
|
+
const labels = Array.isArray(config.labels) && config.labels.length === values.length ? config.labels : values;
|
|
14015
|
+
return {
|
|
14016
|
+
values,
|
|
14017
|
+
labels,
|
|
14018
|
+
...config.message ? { message: config.message } : {}
|
|
14019
|
+
};
|
|
13601
14020
|
}
|
|
13602
14021
|
function buildEnumFieldMutations(fieldPath, enumSpec) {
|
|
13603
14022
|
const normalized = normalizeEnumInput(enumSpec);
|
|
13604
14023
|
if (!normalized) return [];
|
|
13605
14024
|
const messageArg = normalized.message ? `, message: ${JSON.stringify(normalized.message)}` : "";
|
|
14025
|
+
const labelsArg = normalized.labels ? `, labels: ${JSON.stringify(normalized.labels)}` : "";
|
|
13606
14026
|
return [
|
|
13607
14027
|
{
|
|
13608
14028
|
label: `set enum on ${fieldPath}`,
|
|
13609
14029
|
query: `mutation { at(path: ${JSON.stringify(fieldPath)}) { set_enum(values: ${JSON.stringify(
|
|
13610
14030
|
normalized.values
|
|
13611
|
-
)}${messageArg}) { values } } }`
|
|
14031
|
+
)}${labelsArg}${messageArg}) { values labels } } }`
|
|
13612
14032
|
}
|
|
13613
14033
|
];
|
|
13614
14034
|
}
|
|
@@ -13618,7 +14038,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13618
14038
|
fieldRows: [
|
|
13619
14039
|
{
|
|
13620
14040
|
key: "enum",
|
|
13621
|
-
description: 'Allowed values.
|
|
14041
|
+
description: 'Allowed values. Use `{ "values": [...], "labels": [...] }` for authored display labels; otherwise each unchanged value is its display fallback.'
|
|
13622
14042
|
}
|
|
13623
14043
|
]
|
|
13624
14044
|
},
|
|
@@ -13628,6 +14048,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13628
14048
|
type EnumMetamodel {
|
|
13629
14049
|
model: Model!
|
|
13630
14050
|
values: [String!]!
|
|
14051
|
+
labels: [String!]!
|
|
13631
14052
|
message: String
|
|
13632
14053
|
}
|
|
13633
14054
|
|
|
@@ -13636,7 +14057,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13636
14057
|
}
|
|
13637
14058
|
|
|
13638
14059
|
extend type ModelMutation {
|
|
13639
|
-
set_enum(values: [String!]!, message: String): EnumMetamodel
|
|
14060
|
+
set_enum(values: [String!]!, labels: [String!], message: String): EnumMetamodel
|
|
13640
14061
|
}
|
|
13641
14062
|
`
|
|
13642
14063
|
],
|
|
@@ -13645,15 +14066,19 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13645
14066
|
EnumMetamodel: {
|
|
13646
14067
|
model: (value) => value.model,
|
|
13647
14068
|
values: (value) => value.values,
|
|
14069
|
+
labels: (value) => value.labels || [],
|
|
13648
14070
|
message: (value) => value.message || null
|
|
13649
14071
|
},
|
|
13650
14072
|
Model: {
|
|
13651
14073
|
enum_rule: async (ant) => await run(ant.enum_rule())
|
|
13652
14074
|
},
|
|
13653
14075
|
ModelMutation: {
|
|
13654
|
-
set_enum: async (ant, { values, message }) => {
|
|
13655
|
-
const
|
|
13656
|
-
|
|
14076
|
+
set_enum: async (ant, { values, labels, message }) => {
|
|
14077
|
+
const resolvedLabels = Array.isArray(labels) && labels.length === values.length ? labels : values;
|
|
14078
|
+
const model = await run(
|
|
14079
|
+
ant.set_enum(values, resolvedLabels, message)
|
|
14080
|
+
);
|
|
14081
|
+
return { model, values, labels: resolvedLabels, message };
|
|
13657
14082
|
}
|
|
13658
14083
|
}
|
|
13659
14084
|
};
|
|
@@ -13666,7 +14091,7 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13666
14091
|
},
|
|
13667
14092
|
summary: {
|
|
13668
14093
|
selections: {
|
|
13669
|
-
propertyFields: [`enum_rule { values message }`]
|
|
14094
|
+
propertyFields: [`enum_rule { values labels message }`]
|
|
13670
14095
|
},
|
|
13671
14096
|
readPropertySummary(rawProperty) {
|
|
13672
14097
|
const values = Array.isArray(rawProperty.enum_rule?.values) ? rawProperty.enum_rule.values.filter(
|
|
@@ -13674,8 +14099,15 @@ var enumMetamodelPackage = defineMetamodelPackage({
|
|
|
13674
14099
|
) : [];
|
|
13675
14100
|
if (values.length === 0) return { enumRule: null };
|
|
13676
14101
|
const message = typeof rawProperty.enum_rule?.message === "string" ? rawProperty.enum_rule.message : null;
|
|
14102
|
+
const labels = Array.isArray(rawProperty.enum_rule?.labels) ? rawProperty.enum_rule.labels.filter(
|
|
14103
|
+
(label) => typeof label === "string" && label.length > 0
|
|
14104
|
+
) : [];
|
|
13677
14105
|
return {
|
|
13678
|
-
enumRule:
|
|
14106
|
+
enumRule: {
|
|
14107
|
+
values,
|
|
14108
|
+
...labels.length === values.length ? { labels } : {},
|
|
14109
|
+
...message ? { message } : {}
|
|
14110
|
+
}
|
|
13679
14111
|
};
|
|
13680
14112
|
}
|
|
13681
14113
|
},
|
|
@@ -14332,7 +14764,8 @@ function normalizeStateMachines(values) {
|
|
|
14332
14764
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
14333
14765
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
14334
14766
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
14335
|
-
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)
|
|
14336
14769
|
})).filter(
|
|
14337
14770
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
14338
14771
|
);
|
|
@@ -14425,6 +14858,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14425
14858
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
14426
14859
|
);
|
|
14427
14860
|
}
|
|
14861
|
+
if (transition.outcomes) {
|
|
14862
|
+
args.push(
|
|
14863
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
14864
|
+
);
|
|
14865
|
+
}
|
|
14428
14866
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
14429
14867
|
}
|
|
14430
14868
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -14703,7 +15141,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14703
15141
|
name: String!
|
|
14704
15142
|
state_machine: StateMachine!
|
|
14705
15143
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
14706
|
-
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!
|
|
14707
15145
|
activate_transition(name: String!): StateMachineMutation!
|
|
14708
15146
|
}
|
|
14709
15147
|
|
|
@@ -14720,6 +15158,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14720
15158
|
type StateMachineSnapshotMutation {
|
|
14721
15159
|
snapshot: StateMachineSnapshot!
|
|
14722
15160
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15161
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
14723
15162
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
14724
15163
|
}
|
|
14725
15164
|
|
|
@@ -14766,6 +15205,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14766
15205
|
permission_json: String
|
|
14767
15206
|
risk: String
|
|
14768
15207
|
expected_outcome_json: String
|
|
15208
|
+
outcomes_json: String
|
|
14769
15209
|
}
|
|
14770
15210
|
|
|
14771
15211
|
type StateMachinePath {
|
|
@@ -14776,6 +15216,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14776
15216
|
type StateMachineTransitionEvent {
|
|
14777
15217
|
sequence: Int!
|
|
14778
15218
|
occurred_at: Float!
|
|
15219
|
+
commit_id: String
|
|
15220
|
+
outcome: String
|
|
15221
|
+
source_version: String
|
|
15222
|
+
projected_from_mismatch: String
|
|
14779
15223
|
transition: StateMachineTransition!
|
|
14780
15224
|
from: StateMachineState!
|
|
14781
15225
|
to: StateMachineState!
|
|
@@ -14841,7 +15285,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14841
15285
|
requirements_json,
|
|
14842
15286
|
permission_json,
|
|
14843
15287
|
risk,
|
|
14844
|
-
expected_outcome_json
|
|
15288
|
+
expected_outcome_json,
|
|
15289
|
+
outcomes_json
|
|
14845
15290
|
}) => {
|
|
14846
15291
|
await run(
|
|
14847
15292
|
value.target.add_state_machine_transition(
|
|
@@ -14857,7 +15302,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14857
15302
|
requirementsJson: requirements_json,
|
|
14858
15303
|
permissionJson: permission_json,
|
|
14859
15304
|
risk,
|
|
14860
|
-
expectedOutcomeJson: expected_outcome_json
|
|
15305
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
15306
|
+
outcomesJson: outcomes_json
|
|
14861
15307
|
}
|
|
14862
15308
|
)
|
|
14863
15309
|
);
|
|
@@ -14878,6 +15324,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14878
15324
|
);
|
|
14879
15325
|
return value;
|
|
14880
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
|
+
},
|
|
14881
15340
|
observe_state: async (value, { state, force, source }) => {
|
|
14882
15341
|
await run(
|
|
14883
15342
|
value.target.observe_state_machine_state(
|
|
@@ -14907,7 +15366,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14907
15366
|
requirements_json: (value) => value.requirements_json || null,
|
|
14908
15367
|
permission_json: (value) => value.permission_json || null,
|
|
14909
15368
|
risk: (value) => value.risk || null,
|
|
14910
|
-
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
|
|
14911
15371
|
},
|
|
14912
15372
|
StateMachinePath: {
|
|
14913
15373
|
states: (value) => value.states,
|
|
@@ -14916,6 +15376,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14916
15376
|
StateMachineTransitionEvent: {
|
|
14917
15377
|
sequence: (value) => value.sequence,
|
|
14918
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,
|
|
14919
15383
|
transition: (value) => value.transition,
|
|
14920
15384
|
from: (value) => value.from,
|
|
14921
15385
|
to: (value) => value.to
|
|
@@ -14989,6 +15453,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
14989
15453
|
permission_json
|
|
14990
15454
|
risk
|
|
14991
15455
|
expected_outcome_json
|
|
15456
|
+
outcomes_json
|
|
14992
15457
|
}
|
|
14993
15458
|
}`
|
|
14994
15459
|
]
|
|
@@ -15618,6 +16083,133 @@ var Environment = class _Environment {
|
|
|
15618
16083
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
15619
16084
|
};
|
|
15620
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
|
+
}
|
|
15621
16213
|
/**
|
|
15622
16214
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
15623
16215
|
* own the customer application's state machine.
|
|
@@ -17024,6 +17616,14 @@ var EnvironmentSession = class extends Session {
|
|
|
17024
17616
|
}
|
|
17025
17617
|
};
|
|
17026
17618
|
}
|
|
17619
|
+
get mutations() {
|
|
17620
|
+
return {
|
|
17621
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
17622
|
+
"/mutations",
|
|
17623
|
+
options
|
|
17624
|
+
)
|
|
17625
|
+
};
|
|
17626
|
+
}
|
|
17027
17627
|
get artifacts() {
|
|
17028
17628
|
return {
|
|
17029
17629
|
list: (options = {}) => {
|
|
@@ -18523,6 +19123,7 @@ var Granular = class _Granular {
|
|
|
18523
19123
|
const serialized = {
|
|
18524
19124
|
effectKey: computeEffectKey2(effect),
|
|
18525
19125
|
name: effect.name,
|
|
19126
|
+
...effect.label ? { label: effect.label } : {},
|
|
18526
19127
|
description: effect.description,
|
|
18527
19128
|
inputSchema: effect.inputSchema,
|
|
18528
19129
|
stability: effect.stability || "stable",
|
|
@@ -18546,6 +19147,9 @@ var Granular = class _Granular {
|
|
|
18546
19147
|
if (effect.metamodels !== void 0) {
|
|
18547
19148
|
serialized.metamodels = effect.metamodels;
|
|
18548
19149
|
}
|
|
19150
|
+
if (effect.commit !== void 0) {
|
|
19151
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
19152
|
+
}
|
|
18549
19153
|
return serialized;
|
|
18550
19154
|
}
|
|
18551
19155
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -18766,16 +19370,60 @@ var Granular = class _Granular {
|
|
|
18766
19370
|
};
|
|
18767
19371
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
18768
19372
|
const request = params;
|
|
18769
|
-
|
|
19373
|
+
let commitReceipt;
|
|
19374
|
+
const result = await invokeRegisteredEffect(
|
|
18770
19375
|
this.getSandboxEffectMap(sandboxId),
|
|
18771
19376
|
request,
|
|
18772
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
|
+
},
|
|
18773
19416
|
feedback: {
|
|
18774
19417
|
invocationId: request.callId,
|
|
18775
19418
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
18776
19419
|
}
|
|
18777
19420
|
}
|
|
18778
19421
|
);
|
|
19422
|
+
return {
|
|
19423
|
+
__granularEffectInvocationResult: true,
|
|
19424
|
+
result,
|
|
19425
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
19426
|
+
};
|
|
18779
19427
|
});
|
|
18780
19428
|
wsClient.on("open", () => {
|
|
18781
19429
|
void this.synchronizeEffectHost(host).catch((error) => {
|