@granular-software/sdk 0.4.64 → 0.4.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-evals.d.mts +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +679 -53
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +679 -53
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +679 -53
- package/dist/{client-IQeOoPnO.d.mts → client-S-z8bri1.d.mts} +47 -1
- package/dist/{client-BziqnDTl.d.ts → client-z6shQivt.d.ts} +47 -1
- package/dist/index.d.mts +72 -5
- package/dist/index.d.ts +72 -5
- package/dist/index.js +1024 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1018 -72
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-CDLmk-KW.d.mts → spend-CESUvrZC.d.mts} +227 -1
- package/dist/{spend-CDLmk-KW.d.ts → spend-CESUvrZC.d.ts} +227 -1
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -14167,6 +14167,12 @@ var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
|
14167
14167
|
summary: external_exports.string().optional()
|
|
14168
14168
|
}).strict()
|
|
14169
14169
|
]);
|
|
14170
|
+
var StateTransitionOutcomeSchema = external_exports.object({
|
|
14171
|
+
label: external_exports.string().min(1).optional(),
|
|
14172
|
+
to: external_exports.string().min(1),
|
|
14173
|
+
primary: external_exports.boolean().optional(),
|
|
14174
|
+
disposition: external_exports.enum(["continue", "error"])
|
|
14175
|
+
}).strict();
|
|
14170
14176
|
var StateMachineTransitionSchema = external_exports.object({
|
|
14171
14177
|
name: external_exports.string().min(1),
|
|
14172
14178
|
from: external_exports.string().min(1),
|
|
@@ -14178,7 +14184,8 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
14178
14184
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
14179
14185
|
permission: StateTransitionPermissionSchema.optional(),
|
|
14180
14186
|
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
14181
|
-
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
14187
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional(),
|
|
14188
|
+
outcomes: external_exports.record(external_exports.string().min(1), StateTransitionOutcomeSchema).optional()
|
|
14182
14189
|
}).strict();
|
|
14183
14190
|
external_exports.object({
|
|
14184
14191
|
name: external_exports.string().min(1),
|
|
@@ -15529,7 +15536,8 @@ function normalizeStateMachines(values) {
|
|
|
15529
15536
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
15530
15537
|
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
15531
15538
|
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
15532
|
-
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
15539
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json),
|
|
15540
|
+
outcomes: parseJsonRecord(transition?.outcomes) || parseJsonRecord(transition?.outcomes_json)
|
|
15533
15541
|
})).filter(
|
|
15534
15542
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
15535
15543
|
);
|
|
@@ -15622,6 +15630,11 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
15622
15630
|
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
15623
15631
|
);
|
|
15624
15632
|
}
|
|
15633
|
+
if (transition.outcomes) {
|
|
15634
|
+
args.push(
|
|
15635
|
+
`outcomes_json: ${JSON.stringify(JSON.stringify(transition.outcomes))}`
|
|
15636
|
+
);
|
|
15637
|
+
}
|
|
15625
15638
|
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
15626
15639
|
}
|
|
15627
15640
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
@@ -15900,7 +15913,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15900
15913
|
name: String!
|
|
15901
15914
|
state_machine: StateMachine!
|
|
15902
15915
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
15903
|
-
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!
|
|
15916
|
+
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!
|
|
15904
15917
|
activate_transition(name: String!): StateMachineMutation!
|
|
15905
15918
|
}
|
|
15906
15919
|
|
|
@@ -15917,6 +15930,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15917
15930
|
type StateMachineSnapshotMutation {
|
|
15918
15931
|
snapshot: StateMachineSnapshot!
|
|
15919
15932
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15933
|
+
commit_transition(name: String!, outcome: String!, to: String!, commit_id: String!, source_version: String): StateMachineSnapshotMutation!
|
|
15920
15934
|
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
15921
15935
|
}
|
|
15922
15936
|
|
|
@@ -15963,6 +15977,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15963
15977
|
permission_json: String
|
|
15964
15978
|
risk: String
|
|
15965
15979
|
expected_outcome_json: String
|
|
15980
|
+
outcomes_json: String
|
|
15966
15981
|
}
|
|
15967
15982
|
|
|
15968
15983
|
type StateMachinePath {
|
|
@@ -15973,6 +15988,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15973
15988
|
type StateMachineTransitionEvent {
|
|
15974
15989
|
sequence: Int!
|
|
15975
15990
|
occurred_at: Float!
|
|
15991
|
+
commit_id: String
|
|
15992
|
+
outcome: String
|
|
15993
|
+
source_version: String
|
|
15994
|
+
projected_from_mismatch: String
|
|
15976
15995
|
transition: StateMachineTransition!
|
|
15977
15996
|
from: StateMachineState!
|
|
15978
15997
|
to: StateMachineState!
|
|
@@ -16038,7 +16057,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
16038
16057
|
requirements_json,
|
|
16039
16058
|
permission_json,
|
|
16040
16059
|
risk,
|
|
16041
|
-
expected_outcome_json
|
|
16060
|
+
expected_outcome_json,
|
|
16061
|
+
outcomes_json
|
|
16042
16062
|
}) => {
|
|
16043
16063
|
await run(
|
|
16044
16064
|
value.target.add_state_machine_transition(
|
|
@@ -16054,7 +16074,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
16054
16074
|
requirementsJson: requirements_json,
|
|
16055
16075
|
permissionJson: permission_json,
|
|
16056
16076
|
risk,
|
|
16057
|
-
expectedOutcomeJson: expected_outcome_json
|
|
16077
|
+
expectedOutcomeJson: expected_outcome_json,
|
|
16078
|
+
outcomesJson: outcomes_json
|
|
16058
16079
|
}
|
|
16059
16080
|
)
|
|
16060
16081
|
);
|
|
@@ -16075,6 +16096,19 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
16075
16096
|
);
|
|
16076
16097
|
return value;
|
|
16077
16098
|
},
|
|
16099
|
+
commit_transition: async (value, { name, outcome, to, commit_id, source_version }) => {
|
|
16100
|
+
await run(
|
|
16101
|
+
value.target.commit_state_machine_transition(
|
|
16102
|
+
value.name,
|
|
16103
|
+
name,
|
|
16104
|
+
outcome,
|
|
16105
|
+
to,
|
|
16106
|
+
commit_id,
|
|
16107
|
+
source_version
|
|
16108
|
+
)
|
|
16109
|
+
);
|
|
16110
|
+
return value;
|
|
16111
|
+
},
|
|
16078
16112
|
observe_state: async (value, { state, force, source }) => {
|
|
16079
16113
|
await run(
|
|
16080
16114
|
value.target.observe_state_machine_state(
|
|
@@ -16104,7 +16138,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
16104
16138
|
requirements_json: (value) => value.requirements_json || null,
|
|
16105
16139
|
permission_json: (value) => value.permission_json || null,
|
|
16106
16140
|
risk: (value) => value.risk || null,
|
|
16107
|
-
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
16141
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null,
|
|
16142
|
+
outcomes_json: (value) => value.outcomes_json || null
|
|
16108
16143
|
},
|
|
16109
16144
|
StateMachinePath: {
|
|
16110
16145
|
states: (value) => value.states,
|
|
@@ -16113,6 +16148,10 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
16113
16148
|
StateMachineTransitionEvent: {
|
|
16114
16149
|
sequence: (value) => value.sequence,
|
|
16115
16150
|
occurred_at: (value) => value.occurred_at,
|
|
16151
|
+
commit_id: (value) => value.commit_id || null,
|
|
16152
|
+
outcome: (value) => value.outcome || null,
|
|
16153
|
+
source_version: (value) => value.source_version || null,
|
|
16154
|
+
projected_from_mismatch: (value) => value.projected_from_mismatch || null,
|
|
16116
16155
|
transition: (value) => value.transition,
|
|
16117
16156
|
from: (value) => value.from,
|
|
16118
16157
|
to: (value) => value.to
|
|
@@ -16186,6 +16225,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
16186
16225
|
permission_json
|
|
16187
16226
|
risk
|
|
16188
16227
|
expected_outcome_json
|
|
16228
|
+
outcomes_json
|
|
16189
16229
|
}
|
|
16190
16230
|
}`
|
|
16191
16231
|
]
|
|
@@ -22095,6 +22135,11 @@ var Session = class {
|
|
|
22095
22135
|
}
|
|
22096
22136
|
}
|
|
22097
22137
|
buildDirectedInvocationEffectContext(params, feedbackContext) {
|
|
22138
|
+
const commitUnavailable = async () => {
|
|
22139
|
+
throw new Error(
|
|
22140
|
+
"This directed browser tool invocation has no product commit transport"
|
|
22141
|
+
);
|
|
22142
|
+
};
|
|
22098
22143
|
return {
|
|
22099
22144
|
effectClientId: this.clientId,
|
|
22100
22145
|
sandboxId: params.sandboxId || "",
|
|
@@ -22107,6 +22152,10 @@ var Session = class {
|
|
|
22107
22152
|
userId: "",
|
|
22108
22153
|
subjectId: ""
|
|
22109
22154
|
},
|
|
22155
|
+
commit: {
|
|
22156
|
+
effect: commitUnavailable,
|
|
22157
|
+
transition: commitUnavailable
|
|
22158
|
+
},
|
|
22110
22159
|
...feedbackContext ? {
|
|
22111
22160
|
feedback: feedbackContext.feedback,
|
|
22112
22161
|
transientFeedback: feedbackContext.transientFeedback
|
|
@@ -23636,6 +23685,238 @@ function buildSessionTranscript(input) {
|
|
|
23636
23685
|
});
|
|
23637
23686
|
}
|
|
23638
23687
|
|
|
23688
|
+
// src/commit.ts
|
|
23689
|
+
var MAX_PROJECTION_CHANGES = 1e3;
|
|
23690
|
+
var MAX_SAFE_SUMMARY_KEYS = 32;
|
|
23691
|
+
var MAX_SAFE_TEXT_LENGTH = 2e3;
|
|
23692
|
+
var definedTransitionMetadata = /* @__PURE__ */ new WeakMap();
|
|
23693
|
+
function getDefinedTransitionMetadata(transition) {
|
|
23694
|
+
return definedTransitionMetadata.get(transition);
|
|
23695
|
+
}
|
|
23696
|
+
function requireNonEmptyString(value, path7) {
|
|
23697
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
23698
|
+
throw new Error(`${path7} must be a non-empty string`);
|
|
23699
|
+
}
|
|
23700
|
+
return value.trim();
|
|
23701
|
+
}
|
|
23702
|
+
function validateObjectReference(value, path7) {
|
|
23703
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
23704
|
+
throw new Error(`${path7} must be an object reference`);
|
|
23705
|
+
}
|
|
23706
|
+
const reference = value;
|
|
23707
|
+
requireNonEmptyString(reference.className, `${path7}.className`);
|
|
23708
|
+
requireNonEmptyString(reference.id, `${path7}.id`);
|
|
23709
|
+
if (reference.path !== void 0) {
|
|
23710
|
+
requireNonEmptyString(reference.path, `${path7}.path`);
|
|
23711
|
+
}
|
|
23712
|
+
}
|
|
23713
|
+
function validateScalarRecord(value, path7) {
|
|
23714
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
23715
|
+
throw new Error(`${path7} must be an object of scalar values`);
|
|
23716
|
+
}
|
|
23717
|
+
for (const [key, item] of Object.entries(value)) {
|
|
23718
|
+
if (item !== null && typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean") {
|
|
23719
|
+
throw new Error(
|
|
23720
|
+
`${path7}.${key} must be a string, finite number, boolean, or null`
|
|
23721
|
+
);
|
|
23722
|
+
}
|
|
23723
|
+
if (typeof item === "number" && !Number.isFinite(item)) {
|
|
23724
|
+
throw new Error(`${path7}.${key} must be finite`);
|
|
23725
|
+
}
|
|
23726
|
+
}
|
|
23727
|
+
}
|
|
23728
|
+
function validateProjectedRecord(value, path7) {
|
|
23729
|
+
validateObjectReference(value, path7);
|
|
23730
|
+
const record = value;
|
|
23731
|
+
if (record.label !== void 0) {
|
|
23732
|
+
if (typeof record.label !== "string") {
|
|
23733
|
+
throw new Error(`${path7}.label must be a string`);
|
|
23734
|
+
}
|
|
23735
|
+
if (record.label.length > MAX_SAFE_TEXT_LENGTH) {
|
|
23736
|
+
throw new Error(
|
|
23737
|
+
`${path7}.label exceeds ${MAX_SAFE_TEXT_LENGTH} characters`
|
|
23738
|
+
);
|
|
23739
|
+
}
|
|
23740
|
+
}
|
|
23741
|
+
validateScalarRecord(record.fields, `${path7}.fields`);
|
|
23742
|
+
}
|
|
23743
|
+
function validateBoundedJson(value, path7, depth = 0) {
|
|
23744
|
+
if (depth > 12) throw new Error(`${path7} is nested too deeply`);
|
|
23745
|
+
if (value === null || typeof value === "boolean") return;
|
|
23746
|
+
if (typeof value === "number") {
|
|
23747
|
+
if (!Number.isFinite(value)) throw new Error(`${path7} must be finite`);
|
|
23748
|
+
return;
|
|
23749
|
+
}
|
|
23750
|
+
if (typeof value === "string") {
|
|
23751
|
+
if (value.length > MAX_SAFE_TEXT_LENGTH) {
|
|
23752
|
+
throw new Error(`${path7} exceeds ${MAX_SAFE_TEXT_LENGTH} characters`);
|
|
23753
|
+
}
|
|
23754
|
+
return;
|
|
23755
|
+
}
|
|
23756
|
+
if (Array.isArray(value)) {
|
|
23757
|
+
if (value.length > MAX_PROJECTION_CHANGES) {
|
|
23758
|
+
throw new Error(`${path7} contains too many values`);
|
|
23759
|
+
}
|
|
23760
|
+
value.forEach(
|
|
23761
|
+
(item, index) => validateBoundedJson(item, `${path7}[${index}]`, depth + 1)
|
|
23762
|
+
);
|
|
23763
|
+
return;
|
|
23764
|
+
}
|
|
23765
|
+
if (!value || typeof value !== "object") {
|
|
23766
|
+
throw new Error(`${path7} contains an unsupported value`);
|
|
23767
|
+
}
|
|
23768
|
+
const entries = Object.entries(value);
|
|
23769
|
+
if (entries.length > 256) throw new Error(`${path7} contains too many keys`);
|
|
23770
|
+
for (const [key, item] of entries) {
|
|
23771
|
+
if (/token|secret|password|authorization|cookie/i.test(key)) {
|
|
23772
|
+
throw new Error(`${path7}.${key} is not allowed in a commit projection`);
|
|
23773
|
+
}
|
|
23774
|
+
validateBoundedJson(item, `${path7}.${key}`, depth + 1);
|
|
23775
|
+
}
|
|
23776
|
+
}
|
|
23777
|
+
function validateProjectionResult(declaration, projection) {
|
|
23778
|
+
if (!projection || typeof projection !== "object") {
|
|
23779
|
+
throw new Error("Projection mapper must return an object");
|
|
23780
|
+
}
|
|
23781
|
+
requireNonEmptyString(
|
|
23782
|
+
projection.source?.reference,
|
|
23783
|
+
"projection.source.reference"
|
|
23784
|
+
);
|
|
23785
|
+
if (projection.source.version !== void 0) {
|
|
23786
|
+
requireNonEmptyString(
|
|
23787
|
+
projection.source.version,
|
|
23788
|
+
"projection.source.version"
|
|
23789
|
+
);
|
|
23790
|
+
}
|
|
23791
|
+
if (!Array.isArray(projection.changes)) {
|
|
23792
|
+
throw new Error("projection.changes must be an array");
|
|
23793
|
+
}
|
|
23794
|
+
if (projection.changes.length > MAX_PROJECTION_CHANGES) {
|
|
23795
|
+
throw new Error(
|
|
23796
|
+
`projection.changes exceeds the ${MAX_PROJECTION_CHANGES} change limit`
|
|
23797
|
+
);
|
|
23798
|
+
}
|
|
23799
|
+
if (projection.primaryTarget) {
|
|
23800
|
+
validateObjectReference(
|
|
23801
|
+
projection.primaryTarget,
|
|
23802
|
+
"projection.primaryTarget"
|
|
23803
|
+
);
|
|
23804
|
+
}
|
|
23805
|
+
if (projection.safeSummary) {
|
|
23806
|
+
const entries = Object.entries(projection.safeSummary);
|
|
23807
|
+
if (entries.length > MAX_SAFE_SUMMARY_KEYS) {
|
|
23808
|
+
throw new Error(
|
|
23809
|
+
`projection.safeSummary exceeds the ${MAX_SAFE_SUMMARY_KEYS} key limit`
|
|
23810
|
+
);
|
|
23811
|
+
}
|
|
23812
|
+
validateBoundedJson(projection.safeSummary, "projection.safeSummary");
|
|
23813
|
+
validateScalarRecord(projection.safeSummary, "projection.safeSummary");
|
|
23814
|
+
}
|
|
23815
|
+
projection.changes.forEach((change, index) => {
|
|
23816
|
+
const changePath = `projection.changes[${index}]`;
|
|
23817
|
+
validateBoundedJson(change, changePath);
|
|
23818
|
+
if (!change || typeof change !== "object" || Array.isArray(change)) {
|
|
23819
|
+
throw new Error(`${changePath} must be an object`);
|
|
23820
|
+
}
|
|
23821
|
+
const rawChange = change;
|
|
23822
|
+
const kind = requireNonEmptyString(rawChange.kind, `${changePath}.kind`);
|
|
23823
|
+
if (kind === "object") {
|
|
23824
|
+
const operation = requireNonEmptyString(
|
|
23825
|
+
rawChange.operation,
|
|
23826
|
+
`${changePath}.operation`
|
|
23827
|
+
);
|
|
23828
|
+
if (operation !== "created" && operation !== "updated" && operation !== "deleted") {
|
|
23829
|
+
throw new Error(
|
|
23830
|
+
`${changePath}.operation must be created, updated, or deleted`
|
|
23831
|
+
);
|
|
23832
|
+
}
|
|
23833
|
+
if (operation === "deleted") {
|
|
23834
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
23835
|
+
} else {
|
|
23836
|
+
validateProjectedRecord(rawChange.record, `${changePath}.record`);
|
|
23837
|
+
}
|
|
23838
|
+
} else if (kind === "relationship") {
|
|
23839
|
+
const operation = requireNonEmptyString(
|
|
23840
|
+
rawChange.operation,
|
|
23841
|
+
`${changePath}.operation`
|
|
23842
|
+
);
|
|
23843
|
+
if (operation !== "connected" && operation !== "disconnected") {
|
|
23844
|
+
throw new Error(
|
|
23845
|
+
`${changePath}.operation must be connected or disconnected`
|
|
23846
|
+
);
|
|
23847
|
+
}
|
|
23848
|
+
requireNonEmptyString(
|
|
23849
|
+
rawChange.relationship,
|
|
23850
|
+
`${changePath}.relationship`
|
|
23851
|
+
);
|
|
23852
|
+
validateObjectReference(rawChange.from, `${changePath}.from`);
|
|
23853
|
+
validateObjectReference(rawChange.to, `${changePath}.to`);
|
|
23854
|
+
} else if (kind === "state_observation") {
|
|
23855
|
+
if (rawChange.operation !== void 0) {
|
|
23856
|
+
throw new Error(
|
|
23857
|
+
`${changePath}.operation is not valid for an observation`
|
|
23858
|
+
);
|
|
23859
|
+
}
|
|
23860
|
+
validateObjectReference(rawChange.target, `${changePath}.target`);
|
|
23861
|
+
requireNonEmptyString(rawChange.machine, `${changePath}.machine`);
|
|
23862
|
+
requireNonEmptyString(rawChange.state, `${changePath}.state`);
|
|
23863
|
+
} else {
|
|
23864
|
+
throw new Error(`${changePath}.kind is unsupported: ${kind}`);
|
|
23865
|
+
}
|
|
23866
|
+
});
|
|
23867
|
+
const objectOperations = /* @__PURE__ */ new Map();
|
|
23868
|
+
for (const change of projection.changes) {
|
|
23869
|
+
if (change.kind !== "object") continue;
|
|
23870
|
+
const reference = change.operation === "deleted" ? change.target : change.record;
|
|
23871
|
+
const key = `${reference.className}\0${reference.id}`;
|
|
23872
|
+
const operations = objectOperations.get(key) || {
|
|
23873
|
+
deleted: false,
|
|
23874
|
+
upserted: false
|
|
23875
|
+
};
|
|
23876
|
+
if (change.operation === "deleted") operations.deleted = true;
|
|
23877
|
+
else operations.upserted = true;
|
|
23878
|
+
if (operations.deleted && operations.upserted) {
|
|
23879
|
+
throw new Error(
|
|
23880
|
+
`projection.changes cannot both delete and upsert ${reference.className}/${reference.id}; return only its canonical final state`
|
|
23881
|
+
);
|
|
23882
|
+
}
|
|
23883
|
+
objectOperations.set(key, operations);
|
|
23884
|
+
}
|
|
23885
|
+
const outcome = projection.outcome;
|
|
23886
|
+
if (declaration.kind === "transition") {
|
|
23887
|
+
if (!outcome) {
|
|
23888
|
+
throw new Error("A transition projection must return an outcome");
|
|
23889
|
+
}
|
|
23890
|
+
requireNonEmptyString(outcome.key, "projection.outcome.key");
|
|
23891
|
+
if (outcome.error) {
|
|
23892
|
+
validateBoundedJson(outcome.error, "projection.outcome.error");
|
|
23893
|
+
}
|
|
23894
|
+
} else if (projection.outcome !== void 0) {
|
|
23895
|
+
throw new Error("An effect projection cannot declare a transition outcome");
|
|
23896
|
+
}
|
|
23897
|
+
}
|
|
23898
|
+
function canonicalizeCommitValue(value) {
|
|
23899
|
+
const normalize = (current) => {
|
|
23900
|
+
if (current === null || typeof current === "boolean" || typeof current === "string") {
|
|
23901
|
+
return typeof current === "string" ? current.normalize("NFC") : current;
|
|
23902
|
+
}
|
|
23903
|
+
if (typeof current === "number") {
|
|
23904
|
+
if (!Number.isFinite(current)) {
|
|
23905
|
+
throw new Error("Cannot canonicalize a non-finite number");
|
|
23906
|
+
}
|
|
23907
|
+
return Object.is(current, -0) ? 0 : current;
|
|
23908
|
+
}
|
|
23909
|
+
if (Array.isArray(current)) return current.map(normalize);
|
|
23910
|
+
if (current && typeof current === "object") {
|
|
23911
|
+
return Object.fromEntries(
|
|
23912
|
+
Object.entries(current).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key.normalize("NFC"), normalize(item)])
|
|
23913
|
+
);
|
|
23914
|
+
}
|
|
23915
|
+
throw new Error(`Cannot canonicalize ${typeof current}`);
|
|
23916
|
+
};
|
|
23917
|
+
return JSON.stringify(normalize(value));
|
|
23918
|
+
}
|
|
23919
|
+
|
|
23639
23920
|
// src/effect-runtime.ts
|
|
23640
23921
|
function computeEffectKey(effect) {
|
|
23641
23922
|
const attachedClass = effect.className?.trim();
|
|
@@ -23700,6 +23981,45 @@ function resolveInvocationMode(context) {
|
|
|
23700
23981
|
}
|
|
23701
23982
|
return "execute";
|
|
23702
23983
|
}
|
|
23984
|
+
async function sha256Hex(value) {
|
|
23985
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
23986
|
+
"SHA-256",
|
|
23987
|
+
new TextEncoder().encode(value)
|
|
23988
|
+
);
|
|
23989
|
+
return Array.from(
|
|
23990
|
+
new Uint8Array(digest),
|
|
23991
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
23992
|
+
).join("");
|
|
23993
|
+
}
|
|
23994
|
+
async function resolveInvocationIdempotencyKey(request) {
|
|
23995
|
+
const supplied = request.context?.idempotencyKey?.trim();
|
|
23996
|
+
if (supplied) return supplied;
|
|
23997
|
+
const invocationId = request.context?.invocationId?.trim();
|
|
23998
|
+
if (!invocationId) {
|
|
23999
|
+
throw new Error(
|
|
24000
|
+
`Committed effect ${request.effectKey} requires an invocation id`
|
|
24001
|
+
);
|
|
24002
|
+
}
|
|
24003
|
+
const digest = await sha256Hex(
|
|
24004
|
+
canonicalizeCommitValue({
|
|
24005
|
+
sandboxId: request.context?.sandboxId || "",
|
|
24006
|
+
environmentId: request.context?.environmentId || "",
|
|
24007
|
+
effectKey: request.effectKey,
|
|
24008
|
+
invocationId,
|
|
24009
|
+
input: request.input
|
|
24010
|
+
})
|
|
24011
|
+
);
|
|
24012
|
+
return `gci_${digest}`;
|
|
24013
|
+
}
|
|
24014
|
+
function createUnavailableCommitContext(message) {
|
|
24015
|
+
const unavailable = async () => {
|
|
24016
|
+
throw new Error(message);
|
|
24017
|
+
};
|
|
24018
|
+
return {
|
|
24019
|
+
effect: unavailable,
|
|
24020
|
+
transition: unavailable
|
|
24021
|
+
};
|
|
24022
|
+
}
|
|
23703
24023
|
function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
23704
24024
|
const explicitReverseHandler = request.context?.invocation?.reverseHandler?.trim();
|
|
23705
24025
|
const configuredReverseHandler = behaviors.reverse?.handler?.trim();
|
|
@@ -23771,22 +24091,32 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
23771
24091
|
throw new Error(`Dry run is not supported for ${request.effectKey}`);
|
|
23772
24092
|
}
|
|
23773
24093
|
if (mode === "reverse") {
|
|
24094
|
+
const sourceEffectKey = request.context?.invocation?.sourceEffectKey?.trim();
|
|
24095
|
+
if (sourceEffectKey && sourceEffectKey !== request.effectKey) {
|
|
24096
|
+
return { effect, mode, handler: effect.handler };
|
|
24097
|
+
}
|
|
24098
|
+
const namedReverseHandler = request.context?.invocation?.reverseHandler?.trim() || behaviors.reverse?.handler?.trim();
|
|
24099
|
+
if (namedReverseHandler) {
|
|
24100
|
+
const reverseEffect = resolveReverseEffect(
|
|
24101
|
+
effectMap,
|
|
24102
|
+
effect,
|
|
24103
|
+
request,
|
|
24104
|
+
behaviors
|
|
24105
|
+
);
|
|
24106
|
+
if (reverseEffect) {
|
|
24107
|
+
return {
|
|
24108
|
+
effect: reverseEffect,
|
|
24109
|
+
mode,
|
|
24110
|
+
handler: reverseEffect.handler
|
|
24111
|
+
};
|
|
24112
|
+
}
|
|
24113
|
+
throw new Error(
|
|
24114
|
+
`Reverse effect ${namedReverseHandler} is not registered for ${request.effectKey}`
|
|
24115
|
+
);
|
|
24116
|
+
}
|
|
23774
24117
|
if (effect.reverseHandler) {
|
|
23775
24118
|
return { effect, mode, handler: effect.reverseHandler };
|
|
23776
24119
|
}
|
|
23777
|
-
const reverseEffect = resolveReverseEffect(
|
|
23778
|
-
effectMap,
|
|
23779
|
-
effect,
|
|
23780
|
-
request,
|
|
23781
|
-
behaviors
|
|
23782
|
-
);
|
|
23783
|
-
if (reverseEffect) {
|
|
23784
|
-
return {
|
|
23785
|
-
effect: reverseEffect,
|
|
23786
|
-
mode,
|
|
23787
|
-
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
23788
|
-
};
|
|
23789
|
-
}
|
|
23790
24120
|
throw new Error(
|
|
23791
24121
|
`Reverse execution is not supported for ${request.effectKey}`
|
|
23792
24122
|
);
|
|
@@ -23887,18 +24217,142 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
23887
24217
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
23888
24218
|
}
|
|
23889
24219
|
const resolved = resolveHandlerForMode(effectMap, effect, request);
|
|
24220
|
+
const isPreResolvedNamedReverse = resolved.mode === "reverse" && Boolean(request.context?.invocation?.sourceEffectKey?.trim()) && request.context?.invocation?.sourceEffectKey?.trim() !== request.effectKey;
|
|
24221
|
+
const resolvedEffectKey = computeEffectKey(resolved.effect);
|
|
24222
|
+
if (resolved.mode === "reverse" && resolved.effect !== effect && (effect.commit || resolved.effect.commit)) {
|
|
24223
|
+
throw new Error(
|
|
24224
|
+
`Committed named reverse effect ${resolvedEffectKey} must be reserved and dispatched using its own effect key; customer code was not invoked`
|
|
24225
|
+
);
|
|
24226
|
+
}
|
|
24227
|
+
const declaration = resolved.effect.commit;
|
|
24228
|
+
const commitTransport = options.commit;
|
|
24229
|
+
const commitRequired = Boolean(declaration) && (resolved.mode === "execute" || resolved.mode === "reverse");
|
|
24230
|
+
if (commitRequired && !commitTransport) {
|
|
24231
|
+
throw new Error(
|
|
24232
|
+
`Committed effect ${request.effectKey} has no durable commit transport; customer code was not invoked`
|
|
24233
|
+
);
|
|
24234
|
+
}
|
|
24235
|
+
if (commitRequired && declaration?.kind === "transition" && !request.context?.invocation?.transition) {
|
|
24236
|
+
throw new Error(
|
|
24237
|
+
`Transition effect ${request.effectKey} has no resolved transition context; customer code was not invoked`
|
|
24238
|
+
);
|
|
24239
|
+
}
|
|
24240
|
+
const idempotencyKey = commitRequired ? await resolveInvocationIdempotencyKey(request) : request.context?.idempotencyKey;
|
|
24241
|
+
let commitStarted = false;
|
|
24242
|
+
let commitPromise = null;
|
|
24243
|
+
const beginCommit = (requestedKind, productResult) => {
|
|
24244
|
+
if (!declaration || !commitRequired || !commitTransport) {
|
|
24245
|
+
return Promise.reject(
|
|
24246
|
+
new Error(
|
|
24247
|
+
`Effect ${request.effectKey} does not declare an active ${requestedKind} commit`
|
|
24248
|
+
)
|
|
24249
|
+
);
|
|
24250
|
+
}
|
|
24251
|
+
if (declaration.kind !== requestedKind) {
|
|
24252
|
+
return Promise.reject(
|
|
24253
|
+
new Error(
|
|
24254
|
+
`Effect ${request.effectKey} declares ${declaration.kind} commit, not ${requestedKind}`
|
|
24255
|
+
)
|
|
24256
|
+
);
|
|
24257
|
+
}
|
|
24258
|
+
if (commitStarted) {
|
|
24259
|
+
return Promise.reject(
|
|
24260
|
+
new Error(
|
|
24261
|
+
`Effect invocation ${request.context?.invocationId || request.effectKey} already emitted its commit`
|
|
24262
|
+
)
|
|
24263
|
+
);
|
|
24264
|
+
}
|
|
24265
|
+
commitStarted = true;
|
|
24266
|
+
commitPromise = (async () => {
|
|
24267
|
+
let projection;
|
|
24268
|
+
try {
|
|
24269
|
+
projection = declaration.project(productResult);
|
|
24270
|
+
validateProjectionResult(declaration, projection);
|
|
24271
|
+
} catch (error2) {
|
|
24272
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
24273
|
+
if (commitTransport.mappingFailed) {
|
|
24274
|
+
await commitTransport.mappingFailed({
|
|
24275
|
+
effectKey: request.effectKey,
|
|
24276
|
+
effectName: request.effectName,
|
|
24277
|
+
invocationId: request.context?.invocationId || "",
|
|
24278
|
+
idempotencyKey: idempotencyKey || "",
|
|
24279
|
+
environmentId: request.context?.environmentId || "",
|
|
24280
|
+
message
|
|
24281
|
+
});
|
|
24282
|
+
}
|
|
24283
|
+
throw new Error(
|
|
24284
|
+
`Product mutation may have succeeded, but its commit projection is invalid: ${message}`
|
|
24285
|
+
);
|
|
24286
|
+
}
|
|
24287
|
+
if (requestedKind === "transition") {
|
|
24288
|
+
const transition = request.context?.invocation?.transition;
|
|
24289
|
+
const outcome = projection.outcome;
|
|
24290
|
+
if (!transition || !outcome?.key) {
|
|
24291
|
+
throw new Error(
|
|
24292
|
+
`Transition effect ${request.effectKey} did not resolve an authored outcome`
|
|
24293
|
+
);
|
|
24294
|
+
}
|
|
24295
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
24296
|
+
transition.outcomes,
|
|
24297
|
+
outcome.key
|
|
24298
|
+
)) {
|
|
24299
|
+
throw new Error(
|
|
24300
|
+
`Transition ${transition.machine}.${transition.transition} does not declare outcome ${outcome.key}`
|
|
24301
|
+
);
|
|
24302
|
+
}
|
|
24303
|
+
}
|
|
24304
|
+
const invocationId = request.context?.invocationId || "";
|
|
24305
|
+
const environmentId = request.context?.environmentId || "";
|
|
24306
|
+
const sandboxId = request.context?.sandboxId || "";
|
|
24307
|
+
if (!invocationId || !environmentId || !sandboxId || !idempotencyKey) {
|
|
24308
|
+
throw new Error(
|
|
24309
|
+
`Committed effect ${request.effectKey} is missing its trusted invocation scope`
|
|
24310
|
+
);
|
|
24311
|
+
}
|
|
24312
|
+
const commitRequest = {
|
|
24313
|
+
kind: requestedKind,
|
|
24314
|
+
effectKey: request.effectKey,
|
|
24315
|
+
effectName: request.effectName,
|
|
24316
|
+
operationLabel: resolved.effect.label || resolved.effect.name,
|
|
24317
|
+
invocationId,
|
|
24318
|
+
idempotencyKey,
|
|
24319
|
+
sandboxId,
|
|
24320
|
+
environmentId,
|
|
24321
|
+
...request.context?.sessionId ? { sessionId: request.context.sessionId } : {},
|
|
24322
|
+
...request.context?.jobId ? { jobId: request.context.jobId } : {},
|
|
24323
|
+
...request.context?.buildId ? { buildId: request.context.buildId } : {},
|
|
24324
|
+
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
24325
|
+
projection,
|
|
24326
|
+
...requestedKind === "transition" && request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
24327
|
+
};
|
|
24328
|
+
const receipt = await commitTransport.persist(commitRequest);
|
|
24329
|
+
await options.commitAcknowledged?.(receipt);
|
|
24330
|
+
return receipt;
|
|
24331
|
+
})();
|
|
24332
|
+
return commitPromise;
|
|
24333
|
+
};
|
|
24334
|
+
const commitContext = commitRequired ? {
|
|
24335
|
+
effect: (productResult) => beginCommit("effect", productResult),
|
|
24336
|
+
transition: (productResult) => beginCommit("transition", productResult)
|
|
24337
|
+
} : createUnavailableCommitContext(
|
|
24338
|
+
`Effect ${request.effectKey} is not executing a declared product commit`
|
|
24339
|
+
);
|
|
23890
24340
|
const context = {
|
|
23891
24341
|
...request.context || {},
|
|
24342
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
24343
|
+
commit: commitContext,
|
|
23892
24344
|
behaviors: normalizeEffectBehaviors(
|
|
23893
24345
|
request.context?.behaviors || effect.metamodels || void 0
|
|
23894
24346
|
),
|
|
23895
24347
|
invocation: {
|
|
23896
24348
|
mode: resolved.mode,
|
|
23897
|
-
|
|
23898
|
-
|
|
24349
|
+
...commitRequired && declaration ? { commitKind: declaration.kind } : {},
|
|
24350
|
+
sourceEffectKey: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectKey || request.effectKey : request.effectKey,
|
|
24351
|
+
sourceEffectName: isPreResolvedNamedReverse ? request.context?.invocation?.sourceEffectName || request.effectName : request.effectName,
|
|
23899
24352
|
...request.context?.invocation?.reverseHandler ? { reverseHandler: request.context.invocation.reverseHandler } : {},
|
|
23900
24353
|
...request.context?.invocation?.artifactId ? { artifactId: request.context.invocation.artifactId } : {},
|
|
23901
|
-
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {}
|
|
24354
|
+
...request.context?.invocation?.artifactOptions ? { artifactOptions: request.context.invocation.artifactOptions } : {},
|
|
24355
|
+
...request.context?.invocation?.transition ? { transition: request.context.invocation.transition } : {}
|
|
23902
24356
|
}
|
|
23903
24357
|
};
|
|
23904
24358
|
const feedbackContext = options.feedback ? createInvocationFeedbackContext(options.feedback) : null;
|
|
@@ -23922,6 +24376,16 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
23922
24376
|
handlerFailed = true;
|
|
23923
24377
|
handlerError = error2;
|
|
23924
24378
|
}
|
|
24379
|
+
let commitError;
|
|
24380
|
+
let commitFailed = false;
|
|
24381
|
+
if (commitPromise) {
|
|
24382
|
+
try {
|
|
24383
|
+
await commitPromise;
|
|
24384
|
+
} catch (error2) {
|
|
24385
|
+
commitFailed = true;
|
|
24386
|
+
commitError = error2;
|
|
24387
|
+
}
|
|
24388
|
+
}
|
|
23925
24389
|
let feedbackError;
|
|
23926
24390
|
let feedbackFailed = false;
|
|
23927
24391
|
if (feedbackContext) {
|
|
@@ -23935,9 +24399,17 @@ async function invokeRegisteredEffect(effectMap, request, options = {}) {
|
|
|
23935
24399
|
if (handlerFailed) {
|
|
23936
24400
|
throw handlerError;
|
|
23937
24401
|
}
|
|
24402
|
+
if (commitFailed) {
|
|
24403
|
+
throw commitError;
|
|
24404
|
+
}
|
|
23938
24405
|
if (feedbackFailed) {
|
|
23939
24406
|
throw feedbackError;
|
|
23940
24407
|
}
|
|
24408
|
+
if (commitRequired && !commitStarted) {
|
|
24409
|
+
throw new Error(
|
|
24410
|
+
`Mutating effect ${request.effectKey} returned without acknowledging its product mutation`
|
|
24411
|
+
);
|
|
24412
|
+
}
|
|
23941
24413
|
return handlerResult;
|
|
23942
24414
|
}
|
|
23943
24415
|
|
|
@@ -24006,12 +24478,7 @@ function toRecordSearchResult(className, node) {
|
|
|
24006
24478
|
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
24007
24479
|
);
|
|
24008
24480
|
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
24009
|
-
const
|
|
24010
|
-
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path7)) {
|
|
24011
|
-
return null;
|
|
24012
|
-
}
|
|
24013
|
-
const fallbackLabel = displayLabelFromFields(fields);
|
|
24014
|
-
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path7) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
24481
|
+
const label = typeof node.label === "string" && node.label.trim() ? node.label.trim() : path7 || id;
|
|
24015
24482
|
return {
|
|
24016
24483
|
path: path7,
|
|
24017
24484
|
className,
|
|
@@ -24021,30 +24488,6 @@ function toRecordSearchResult(className, node) {
|
|
|
24021
24488
|
fields
|
|
24022
24489
|
};
|
|
24023
24490
|
}
|
|
24024
|
-
function isPlaceholderRecordLabel(label, id, path7) {
|
|
24025
|
-
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
24026
|
-
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path7);
|
|
24027
|
-
}
|
|
24028
|
-
function displayLabelFromFields(fields) {
|
|
24029
|
-
const preferredFieldNames = [
|
|
24030
|
-
"name",
|
|
24031
|
-
"title",
|
|
24032
|
-
"label",
|
|
24033
|
-
"display_name",
|
|
24034
|
-
"file_name",
|
|
24035
|
-
"number",
|
|
24036
|
-
"code"
|
|
24037
|
-
];
|
|
24038
|
-
for (const preferred of preferredFieldNames) {
|
|
24039
|
-
const match = fields.find(
|
|
24040
|
-
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
24041
|
-
);
|
|
24042
|
-
if (typeof match?.value === "string") {
|
|
24043
|
-
return match.value.trim();
|
|
24044
|
-
}
|
|
24045
|
-
}
|
|
24046
|
-
return null;
|
|
24047
|
-
}
|
|
24048
24491
|
function normalizeRecordSearchText(value) {
|
|
24049
24492
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
24050
24493
|
}
|
|
@@ -24674,6 +25117,133 @@ var Environment = class _Environment {
|
|
|
24674
25117
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
24675
25118
|
};
|
|
24676
25119
|
}
|
|
25120
|
+
/**
|
|
25121
|
+
* Acknowledge a declared product mutation that already happened outside a
|
|
25122
|
+
* Granular-run effect (for example, in a webhook consumer). These methods
|
|
25123
|
+
* run the declaration's pure projection mapper; they never call its handler.
|
|
25124
|
+
*/
|
|
25125
|
+
get commit() {
|
|
25126
|
+
return {
|
|
25127
|
+
effect: async (effect, productResult, options = {}) => this.persistExternalEffect(effect, productResult, options),
|
|
25128
|
+
transition: async (transition, productResult, options = {}) => this.persistExternalTransition(transition, productResult, options),
|
|
25129
|
+
get: async (commitId) => this.controlPlaneRequest(
|
|
25130
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
25131
|
+
commitId
|
|
25132
|
+
)}`
|
|
25133
|
+
),
|
|
25134
|
+
retry: async (commitId) => this.controlPlaneRequest(
|
|
25135
|
+
`/control/environments/${this.environmentId}/commits/${encodeURIComponent(
|
|
25136
|
+
commitId
|
|
25137
|
+
)}/retry`,
|
|
25138
|
+
{ method: "POST" }
|
|
25139
|
+
)
|
|
25140
|
+
};
|
|
25141
|
+
}
|
|
25142
|
+
/** Inspect or retry the agent synchronization of a product snapshot. */
|
|
25143
|
+
get observation() {
|
|
25144
|
+
return {
|
|
25145
|
+
get: async (observationId) => this.controlPlaneRequest(
|
|
25146
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
25147
|
+
observationId
|
|
25148
|
+
)}`
|
|
25149
|
+
),
|
|
25150
|
+
retry: async (observationId) => this.controlPlaneRequest(
|
|
25151
|
+
`/control/environments/${this.environmentId}/observations/${encodeURIComponent(
|
|
25152
|
+
observationId
|
|
25153
|
+
)}/retry`,
|
|
25154
|
+
{ method: "POST" }
|
|
25155
|
+
)
|
|
25156
|
+
};
|
|
25157
|
+
}
|
|
25158
|
+
async persistExternalEffect(effect, productResult, options) {
|
|
25159
|
+
const projection = effect.commit.project(productResult);
|
|
25160
|
+
validateProjectionResult(effect.commit, projection);
|
|
25161
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
25162
|
+
return this.controlPlaneRequest(
|
|
25163
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
25164
|
+
{
|
|
25165
|
+
method: "POST",
|
|
25166
|
+
body: JSON.stringify({
|
|
25167
|
+
kind: "effect",
|
|
25168
|
+
effectKey: computeEffectKey2(effect),
|
|
25169
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
25170
|
+
projection
|
|
25171
|
+
})
|
|
25172
|
+
}
|
|
25173
|
+
);
|
|
25174
|
+
}
|
|
25175
|
+
async persistExternalTransition(transition, productResult, options) {
|
|
25176
|
+
const metadata = getDefinedTransitionMetadata(transition);
|
|
25177
|
+
if (!metadata) {
|
|
25178
|
+
throw new Error(
|
|
25179
|
+
"environment.commit.transition requires a transition returned by defineStateMachine"
|
|
25180
|
+
);
|
|
25181
|
+
}
|
|
25182
|
+
const projection = transition.effect.commit.project(productResult);
|
|
25183
|
+
validateProjectionResult(transition.effect.commit, projection);
|
|
25184
|
+
this.requireExternalIdentity(projection.source.version, options);
|
|
25185
|
+
if (!Object.prototype.hasOwnProperty.call(
|
|
25186
|
+
transition.outcomes,
|
|
25187
|
+
projection.outcome.key
|
|
25188
|
+
)) {
|
|
25189
|
+
throw new Error(
|
|
25190
|
+
`Transition ${metadata.machine}.${metadata.transition} does not declare outcome ${projection.outcome.key}`
|
|
25191
|
+
);
|
|
25192
|
+
}
|
|
25193
|
+
if (!projection.primaryTarget) {
|
|
25194
|
+
throw new Error(
|
|
25195
|
+
"An external transition projection requires primaryTarget to identify the transitioned product record"
|
|
25196
|
+
);
|
|
25197
|
+
}
|
|
25198
|
+
return this.controlPlaneRequest(
|
|
25199
|
+
`/control/environments/${this.environmentId}/external-commits`,
|
|
25200
|
+
{
|
|
25201
|
+
method: "POST",
|
|
25202
|
+
body: JSON.stringify({
|
|
25203
|
+
kind: "transition",
|
|
25204
|
+
effectKey: computeEffectKey2(transition.effect),
|
|
25205
|
+
...options.sourceEventId?.trim() ? { sourceEventId: options.sourceEventId.trim() } : {},
|
|
25206
|
+
projection,
|
|
25207
|
+
transition: {
|
|
25208
|
+
className: projection.primaryTarget.className,
|
|
25209
|
+
objectId: projection.primaryTarget.id,
|
|
25210
|
+
...projection.primaryTarget.path ? { objectPath: projection.primaryTarget.path } : {},
|
|
25211
|
+
machine: metadata.machine,
|
|
25212
|
+
transition: metadata.transition,
|
|
25213
|
+
from: transition.from
|
|
25214
|
+
}
|
|
25215
|
+
})
|
|
25216
|
+
}
|
|
25217
|
+
);
|
|
25218
|
+
}
|
|
25219
|
+
requireExternalIdentity(sourceVersion, options) {
|
|
25220
|
+
if (!options.sourceEventId?.trim() && !sourceVersion?.trim()) {
|
|
25221
|
+
throw new Error(
|
|
25222
|
+
"An external commit requires sourceEventId or a source version from its projection mapper"
|
|
25223
|
+
);
|
|
25224
|
+
}
|
|
25225
|
+
}
|
|
25226
|
+
/**
|
|
25227
|
+
* Synchronize a versioned product snapshot without claiming an effect or
|
|
25228
|
+
* lifecycle transition. This records no transition history.
|
|
25229
|
+
*/
|
|
25230
|
+
async observe(mapper, productResult) {
|
|
25231
|
+
const declaration = { kind: "effect"};
|
|
25232
|
+
const projection = mapper(productResult);
|
|
25233
|
+
validateProjectionResult(declaration, projection);
|
|
25234
|
+
if (!projection.source.version?.trim()) {
|
|
25235
|
+
throw new Error(
|
|
25236
|
+
"environment.observe requires a monotonic source version or serialized adapter sequence"
|
|
25237
|
+
);
|
|
25238
|
+
}
|
|
25239
|
+
return this.controlPlaneRequest(
|
|
25240
|
+
`/control/environments/${this.environmentId}/observations`,
|
|
25241
|
+
{
|
|
25242
|
+
method: "POST",
|
|
25243
|
+
body: JSON.stringify({ projection })
|
|
25244
|
+
}
|
|
25245
|
+
);
|
|
25246
|
+
}
|
|
24677
25247
|
/**
|
|
24678
25248
|
* Mirror product-owned workflow state into Granular without making Granular
|
|
24679
25249
|
* own the customer application's state machine.
|
|
@@ -26080,6 +26650,14 @@ var EnvironmentSession = class extends Session {
|
|
|
26080
26650
|
}
|
|
26081
26651
|
};
|
|
26082
26652
|
}
|
|
26653
|
+
get mutations() {
|
|
26654
|
+
return {
|
|
26655
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
26656
|
+
"/mutations",
|
|
26657
|
+
options
|
|
26658
|
+
)
|
|
26659
|
+
};
|
|
26660
|
+
}
|
|
26083
26661
|
get artifacts() {
|
|
26084
26662
|
return {
|
|
26085
26663
|
list: (options = {}) => {
|
|
@@ -27579,6 +28157,7 @@ var Granular = class _Granular {
|
|
|
27579
28157
|
const serialized = {
|
|
27580
28158
|
effectKey: computeEffectKey2(effect),
|
|
27581
28159
|
name: effect.name,
|
|
28160
|
+
...effect.label ? { label: effect.label } : {},
|
|
27582
28161
|
description: effect.description,
|
|
27583
28162
|
inputSchema: effect.inputSchema,
|
|
27584
28163
|
stability: effect.stability || "stable",
|
|
@@ -27602,6 +28181,9 @@ var Granular = class _Granular {
|
|
|
27602
28181
|
if (effect.metamodels !== void 0) {
|
|
27603
28182
|
serialized.metamodels = effect.metamodels;
|
|
27604
28183
|
}
|
|
28184
|
+
if (effect.commit !== void 0) {
|
|
28185
|
+
serialized.commit = { kind: effect.commit.kind };
|
|
28186
|
+
}
|
|
27605
28187
|
return serialized;
|
|
27606
28188
|
}
|
|
27607
28189
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -27822,16 +28404,60 @@ var Granular = class _Granular {
|
|
|
27822
28404
|
};
|
|
27823
28405
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
27824
28406
|
const request = params;
|
|
27825
|
-
|
|
28407
|
+
let commitReceipt;
|
|
28408
|
+
const result = await invokeRegisteredEffect(
|
|
27826
28409
|
this.getSandboxEffectMap(sandboxId),
|
|
27827
28410
|
request,
|
|
27828
28411
|
{
|
|
28412
|
+
commitAcknowledged: (receipt) => {
|
|
28413
|
+
commitReceipt = receipt;
|
|
28414
|
+
},
|
|
28415
|
+
commit: {
|
|
28416
|
+
persist: (commitRequest) => this.request(
|
|
28417
|
+
`/control/environments/${encodeURIComponent(
|
|
28418
|
+
commitRequest.environmentId
|
|
28419
|
+
)}/commits`,
|
|
28420
|
+
{
|
|
28421
|
+
method: "POST",
|
|
28422
|
+
body: JSON.stringify({
|
|
28423
|
+
kind: commitRequest.kind,
|
|
28424
|
+
invocationId: commitRequest.invocationId,
|
|
28425
|
+
idempotencyKey: commitRequest.idempotencyKey,
|
|
28426
|
+
effectKey: commitRequest.effectKey,
|
|
28427
|
+
projection: commitRequest.projection
|
|
28428
|
+
})
|
|
28429
|
+
}
|
|
28430
|
+
),
|
|
28431
|
+
mappingFailed: (failure) => this.request(
|
|
28432
|
+
`/control/environments/${encodeURIComponent(
|
|
28433
|
+
failure.environmentId
|
|
28434
|
+
)}/commit-invocations/${encodeURIComponent(
|
|
28435
|
+
failure.invocationId
|
|
28436
|
+
)}`,
|
|
28437
|
+
{
|
|
28438
|
+
method: "PATCH",
|
|
28439
|
+
body: JSON.stringify({
|
|
28440
|
+
status: "mapping_failed",
|
|
28441
|
+
error: {
|
|
28442
|
+
code: "commit_projection_mapping_failed",
|
|
28443
|
+
message: failure.message,
|
|
28444
|
+
retryable: false
|
|
28445
|
+
}
|
|
28446
|
+
})
|
|
28447
|
+
}
|
|
28448
|
+
)
|
|
28449
|
+
},
|
|
27829
28450
|
feedback: {
|
|
27830
28451
|
invocationId: request.callId,
|
|
27831
28452
|
publish: (method, publishParams) => wsClient.call(method, publishParams)
|
|
27832
28453
|
}
|
|
27833
28454
|
}
|
|
27834
28455
|
);
|
|
28456
|
+
return {
|
|
28457
|
+
__granularEffectInvocationResult: true,
|
|
28458
|
+
result,
|
|
28459
|
+
...commitReceipt ? { commit: commitReceipt } : {}
|
|
28460
|
+
};
|
|
27835
28461
|
});
|
|
27836
28462
|
wsClient.on("open", () => {
|
|
27837
28463
|
void this.synchronizeEffectHost(host).catch((error2) => {
|