@modainteract/moda-interact-shared 1.5.0 → 2.1.0
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/{chunk-XZXQT77J.js → chunk-GI755E7S.js} +6 -2
- package/dist/chunk-GI755E7S.js.map +1 -0
- package/dist/commerce/model/node.d.ts +1 -1
- package/dist/commerce/model/node.js +12 -3
- package/dist/commerce/model/node.js.map +1 -1
- package/dist/commerce/runner/index.d.ts +2 -2
- package/dist/commerce/runner/index.js +23 -7
- package/dist/commerce/runner/index.js.map +1 -1
- package/dist/incidents/index.d.ts +124 -0
- package/dist/incidents/index.js +264 -0
- package/dist/incidents/index.js.map +1 -0
- package/dist/internationalization.d.ts +2 -2
- package/dist/shopify/index.d.ts +23 -23
- package/dist/{types-UsnWbpTN.d.ts → types-Di8jfodf.d.ts} +3 -1
- package/package.json +7 -2
- package/dist/chunk-XZXQT77J.js.map +0 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// src/incidents/index.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
// src/incidents/reporter.ts
|
|
6
|
+
function createOperationalIncidentReporter(options) {
|
|
7
|
+
return {
|
|
8
|
+
async report(raw) {
|
|
9
|
+
let occurrence;
|
|
10
|
+
try {
|
|
11
|
+
occurrence = parseOperationalIncidentOccurrenceV1(raw);
|
|
12
|
+
} catch {
|
|
13
|
+
options.logger.error("operational_incident.invalid_occurrence", {});
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const incidentKey = createOperationalIncidentKey({
|
|
17
|
+
sourceService: occurrence.sourceService,
|
|
18
|
+
environment: occurrence.environment,
|
|
19
|
+
category: occurrence.category,
|
|
20
|
+
reasonCode: occurrence.reasonCode,
|
|
21
|
+
resourceKey: occurrence.resourceKey,
|
|
22
|
+
shopId: occurrence.shopId
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
return await options.persistence.transactionForKey(incidentKey, async (tx) => {
|
|
26
|
+
const current = await tx.loadOrCreate({ incidentKey, occurrence });
|
|
27
|
+
const recorded = await tx.insertOccurrenceIfNew({ incidentKey, occurrence });
|
|
28
|
+
if (!recorded) {
|
|
29
|
+
return {
|
|
30
|
+
incidentKey,
|
|
31
|
+
recorded: false,
|
|
32
|
+
occurrenceCount: current.occurrenceCount,
|
|
33
|
+
status: current.status
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const updated = await tx.recordOccurrence({
|
|
37
|
+
incidentKey,
|
|
38
|
+
occurrence,
|
|
39
|
+
reopen: current.status === "RESOLVED",
|
|
40
|
+
newlyCreated: current.newlyCreated
|
|
41
|
+
});
|
|
42
|
+
if (options.notifySupport && occurrence.shopId !== null && tx.notifySupportOnce) {
|
|
43
|
+
await tx.notifySupportOnce({ incidentKey, occurrence });
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
incidentKey,
|
|
47
|
+
recorded: true,
|
|
48
|
+
occurrenceCount: updated.occurrenceCount,
|
|
49
|
+
status: "OPEN"
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
} catch {
|
|
53
|
+
options.logger.error("operational_incident.report_failed", {
|
|
54
|
+
incidentKey,
|
|
55
|
+
sourceService: occurrence.sourceService,
|
|
56
|
+
environment: occurrence.environment,
|
|
57
|
+
occurrenceId: occurrence.occurrenceId
|
|
58
|
+
});
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/incidents/postgres.ts
|
|
66
|
+
import { randomUUID } from "crypto";
|
|
67
|
+
async function rows(tx, statement, ...params) {
|
|
68
|
+
return tx.$queryRawUnsafe(statement, ...params);
|
|
69
|
+
}
|
|
70
|
+
function snapshot(row) {
|
|
71
|
+
return {
|
|
72
|
+
incidentKey: row.incidentKey,
|
|
73
|
+
status: row.status,
|
|
74
|
+
occurrenceCount: row.occurrenceCount,
|
|
75
|
+
lastOccurredAt: row.lastOccurredAt
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
var PgIncidentTransaction = class {
|
|
79
|
+
constructor(tx, supportWriter) {
|
|
80
|
+
this.tx = tx;
|
|
81
|
+
this.supportWriter = supportWriter;
|
|
82
|
+
}
|
|
83
|
+
tx;
|
|
84
|
+
supportWriter;
|
|
85
|
+
incidentId = null;
|
|
86
|
+
newIncident = false;
|
|
87
|
+
async loadOrCreate({ incidentKey, occurrence }) {
|
|
88
|
+
const inserted = await rows(
|
|
89
|
+
this.tx,
|
|
90
|
+
`
|
|
91
|
+
INSERT INTO support."OperationalIncident" (
|
|
92
|
+
"id", "incidentKey", "sourceService", "environment", "category", "reasonCode",
|
|
93
|
+
"resourceKey", "shopId", "severity", "status", "occurrenceCount",
|
|
94
|
+
"firstOccurredAt", "lastOccurredAt", "lastCorrelationId")
|
|
95
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'OPEN',1,$10::timestamptz,$10::timestamptz,$11)
|
|
96
|
+
ON CONFLICT ("incidentKey") DO NOTHING RETURNING *`,
|
|
97
|
+
randomUUID(),
|
|
98
|
+
incidentKey,
|
|
99
|
+
occurrence.sourceService,
|
|
100
|
+
occurrence.environment,
|
|
101
|
+
occurrence.category,
|
|
102
|
+
occurrence.reasonCode,
|
|
103
|
+
occurrence.resourceKey,
|
|
104
|
+
occurrence.shopId,
|
|
105
|
+
occurrence.severity,
|
|
106
|
+
occurrence.occurredAt,
|
|
107
|
+
occurrence.correlationId
|
|
108
|
+
);
|
|
109
|
+
this.newIncident = inserted.length > 0;
|
|
110
|
+
const found = await rows(
|
|
111
|
+
this.tx,
|
|
112
|
+
`SELECT * FROM support."OperationalIncident" WHERE "incidentKey" = $1 FOR UPDATE`,
|
|
113
|
+
incidentKey
|
|
114
|
+
);
|
|
115
|
+
if (found.length !== 1) throw new Error("incident row not found after upsert");
|
|
116
|
+
this.incidentId = found[0].id;
|
|
117
|
+
return { ...snapshot(found[0]), newlyCreated: this.newIncident };
|
|
118
|
+
}
|
|
119
|
+
async insertOccurrenceIfNew({ incidentKey, occurrence }) {
|
|
120
|
+
if (!this.incidentId) throw new Error("incident not loaded");
|
|
121
|
+
const created = await rows(
|
|
122
|
+
this.tx,
|
|
123
|
+
`
|
|
124
|
+
INSERT INTO support."OperationalIncidentEvent"
|
|
125
|
+
("id", "incidentId", "eventType", "correlationId", "occurredAt")
|
|
126
|
+
VALUES ($1::uuid, $2, 'OCCURRED', $3, $4::timestamptz)
|
|
127
|
+
ON CONFLICT ("id") DO NOTHING RETURNING "id"`,
|
|
128
|
+
occurrence.occurrenceId,
|
|
129
|
+
this.incidentId,
|
|
130
|
+
occurrence.correlationId,
|
|
131
|
+
occurrence.occurredAt
|
|
132
|
+
);
|
|
133
|
+
if (!created.length) {
|
|
134
|
+
if (this.newIncident) throw new Error("duplicate occurrence cannot create new incident");
|
|
135
|
+
const existing = await rows(this.tx, `
|
|
136
|
+
SELECT i."incidentKey" FROM support."OperationalIncidentEvent" e
|
|
137
|
+
JOIN support."OperationalIncident" i ON i."id" = e."incidentId"
|
|
138
|
+
WHERE e."id" = $1::uuid`, occurrence.occurrenceId);
|
|
139
|
+
if (existing.length !== 1 || existing[0].incidentKey !== incidentKey) {
|
|
140
|
+
throw new Error("occurrence already belongs to another incident");
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
async recordOccurrence({ incidentKey, occurrence, reopen, newlyCreated }) {
|
|
147
|
+
if (!this.incidentId) throw new Error("incident not loaded");
|
|
148
|
+
if (reopen) {
|
|
149
|
+
await rows(
|
|
150
|
+
this.tx,
|
|
151
|
+
`INSERT INTO support."OperationalIncidentEvent"
|
|
152
|
+
("id", "incidentId", "eventType", "correlationId", "occurredAt")
|
|
153
|
+
VALUES ($1::uuid,$2,'REOPENED',$3,$4::timestamptz)`,
|
|
154
|
+
randomUUID(),
|
|
155
|
+
this.incidentId,
|
|
156
|
+
occurrence.correlationId,
|
|
157
|
+
occurrence.occurredAt
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
const updated = await rows(
|
|
161
|
+
this.tx,
|
|
162
|
+
`
|
|
163
|
+
UPDATE support."OperationalIncident" SET
|
|
164
|
+
"occurrenceCount" = "occurrenceCount" + $2::integer,
|
|
165
|
+
"lastOccurredAt" = GREATEST("lastOccurredAt", $3::timestamptz),
|
|
166
|
+
"lastCorrelationId" = $4,
|
|
167
|
+
"severity" = $5,
|
|
168
|
+
"status" = 'OPEN', "resolvedAt" = NULL,
|
|
169
|
+
"resolvedByPlatformAdminId" = NULL, "resolutionNote" = NULL,
|
|
170
|
+
"revision" = "revision" + 1
|
|
171
|
+
WHERE "incidentKey" = $1 RETURNING *`,
|
|
172
|
+
incidentKey,
|
|
173
|
+
newlyCreated ? 0 : 1,
|
|
174
|
+
occurrence.occurredAt,
|
|
175
|
+
occurrence.correlationId,
|
|
176
|
+
occurrence.severity
|
|
177
|
+
);
|
|
178
|
+
if (updated.length !== 1) throw new Error("incident update failed");
|
|
179
|
+
return snapshot(updated[0]);
|
|
180
|
+
}
|
|
181
|
+
async notifySupportOnce(input) {
|
|
182
|
+
if (this.supportWriter && this.incidentId) await this.supportWriter({
|
|
183
|
+
tx: this.tx,
|
|
184
|
+
incidentId: this.incidentId,
|
|
185
|
+
incidentKey: input.incidentKey,
|
|
186
|
+
occurrence: input.occurrence
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
function retryable(error) {
|
|
191
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
192
|
+
return code === "P2034" || code === "40001" || code === "40P01";
|
|
193
|
+
}
|
|
194
|
+
function createPrismaOperationalIncidentPersistence(options) {
|
|
195
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
196
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 10) {
|
|
197
|
+
throw new Error("maxAttempts must be between 1 and 10");
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
async transactionForKey(key, fn) {
|
|
201
|
+
for (let attempt = 1; ; attempt++) {
|
|
202
|
+
try {
|
|
203
|
+
return await options.prisma.$transaction(async (tx) => {
|
|
204
|
+
await rows(tx, `SELECT pg_advisory_xact_lock(hashtextextended($1::text, 0))::text AS acquired`, key);
|
|
205
|
+
return fn(new PgIncidentTransaction(tx, options.supportWriter));
|
|
206
|
+
}, { isolationLevel: "Serializable", timeout: 15e3 });
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (attempt >= maxAttempts || !retryable(error)) throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/incidents/index.ts
|
|
216
|
+
var OPERATIONAL_INCIDENT_CONTRACT_VERSION = "operational-incident.v1";
|
|
217
|
+
var identifier = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
|
|
218
|
+
var smallIdentifier = z.string().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
|
|
219
|
+
var opaqueId = z.string().uuid();
|
|
220
|
+
var OperationalIncidentOccurrenceV1Schema = z.strictObject({
|
|
221
|
+
contractVersion: z.literal(OPERATIONAL_INCIDENT_CONTRACT_VERSION),
|
|
222
|
+
sourceService: smallIdentifier,
|
|
223
|
+
environment: z.enum(["local", "development", "test", "staging", "production"]),
|
|
224
|
+
category: smallIdentifier,
|
|
225
|
+
reasonCode: identifier,
|
|
226
|
+
resourceKey: identifier,
|
|
227
|
+
shopId: z.string().min(1).max(128).nullable(),
|
|
228
|
+
severity: z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]),
|
|
229
|
+
occurrenceId: opaqueId,
|
|
230
|
+
correlationId: identifier,
|
|
231
|
+
occurredAt: z.iso.datetime({ offset: true })
|
|
232
|
+
// Deliberately no arbitrary metadata bag, message, headers or exception object.
|
|
233
|
+
});
|
|
234
|
+
function parseOperationalIncidentOccurrenceV1(input) {
|
|
235
|
+
return OperationalIncidentOccurrenceV1Schema.parse(input);
|
|
236
|
+
}
|
|
237
|
+
function createOperationalIncidentKey(input) {
|
|
238
|
+
const parsed = OperationalIncidentOccurrenceV1Schema.pick({
|
|
239
|
+
sourceService: true,
|
|
240
|
+
environment: true,
|
|
241
|
+
category: true,
|
|
242
|
+
reasonCode: true,
|
|
243
|
+
resourceKey: true,
|
|
244
|
+
shopId: true
|
|
245
|
+
}).parse(input);
|
|
246
|
+
return createHash("sha256").update(JSON.stringify([
|
|
247
|
+
"operational-incident-key.v1",
|
|
248
|
+
parsed.sourceService,
|
|
249
|
+
parsed.environment,
|
|
250
|
+
parsed.category,
|
|
251
|
+
parsed.reasonCode,
|
|
252
|
+
parsed.shopId,
|
|
253
|
+
parsed.resourceKey
|
|
254
|
+
])).digest("hex");
|
|
255
|
+
}
|
|
256
|
+
export {
|
|
257
|
+
OPERATIONAL_INCIDENT_CONTRACT_VERSION,
|
|
258
|
+
OperationalIncidentOccurrenceV1Schema,
|
|
259
|
+
createOperationalIncidentKey,
|
|
260
|
+
createOperationalIncidentReporter,
|
|
261
|
+
createPrismaOperationalIncidentPersistence,
|
|
262
|
+
parseOperationalIncidentOccurrenceV1
|
|
263
|
+
};
|
|
264
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/incidents/index.ts","../../src/incidents/reporter.ts","../../src/incidents/postgres.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n// ARCH-033 v1: safe, internal-only occurrence envelope. Producer classifies actionability.\nexport const OPERATIONAL_INCIDENT_CONTRACT_VERSION = \"operational-incident.v1\" as const;\nconst identifier = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);\nconst smallIdentifier = z.string().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);\nconst opaqueId = z.string().uuid();\nexport const OperationalIncidentOccurrenceV1Schema = z.strictObject({\n contractVersion: z.literal(OPERATIONAL_INCIDENT_CONTRACT_VERSION),\n sourceService: smallIdentifier,\n environment: z.enum([\"local\", \"development\", \"test\", \"staging\", \"production\"]),\n category: smallIdentifier,\n reasonCode: identifier,\n resourceKey: identifier,\n shopId: z.string().min(1).max(128).nullable(),\n severity: z.enum([\"LOW\", \"MEDIUM\", \"HIGH\", \"CRITICAL\"]),\n occurrenceId: opaqueId,\n correlationId: identifier,\n occurredAt: z.iso.datetime({ offset: true }),\n // Deliberately no arbitrary metadata bag, message, headers or exception object.\n});\nexport type OperationalIncidentOccurrenceV1 = z.infer<typeof OperationalIncidentOccurrenceV1Schema>;\nexport function parseOperationalIncidentOccurrenceV1(input: unknown): OperationalIncidentOccurrenceV1 {\n return OperationalIncidentOccurrenceV1Schema.parse(input);\n}\nexport function createOperationalIncidentKey(input: Pick<OperationalIncidentOccurrenceV1,\n \"sourceService\" | \"environment\" | \"category\" | \"reasonCode\" | \"resourceKey\" | \"shopId\">): string {\n const parsed = OperationalIncidentOccurrenceV1Schema.pick({\n sourceService: true, environment: true, category: true, reasonCode: true, resourceKey: true, shopId: true,\n }).parse(input);\n return createHash(\"sha256\").update(JSON.stringify([\n \"operational-incident-key.v1\", parsed.sourceService, parsed.environment, parsed.category,\n parsed.reasonCode, parsed.shopId, parsed.resourceKey,\n ])).digest(\"hex\");\n}\n\nexport { createOperationalIncidentReporter } from \"./reporter.js\";\nexport type {\n IncidentPersistence, IncidentTransaction, IncidentSnapshot, IncidentReportResult,\n IncidentReporterLogger,\n} from \"./reporter.js\";\n\nexport { createPrismaOperationalIncidentPersistence } from \"./postgres.js\";\nexport type { OperationalIncidentPrismaClient, OperationalIncidentSqlClient, OperationalIncidentSupportWriter } from \"./postgres.js\";\n","import { createOperationalIncidentKey, parseOperationalIncidentOccurrenceV1 } from \"./index.js\";\nimport type { OperationalIncidentOccurrenceV1 } from \"./index.js\";\n\n/** Durable outcome of one occurrence; duplicate deliveries have no side effects. */\nexport type IncidentReportResult = {\n incidentKey: string;\n recorded: boolean;\n occurrenceCount: number;\n status: \"OPEN\" | \"RESOLVED\";\n};\n\nexport type IncidentSnapshot = {\n incidentKey: string;\n status: \"OPEN\" | \"RESOLVED\";\n occurrenceCount: number;\n lastOccurredAt: Date;\n};\n\n/**\n * Transactional persistence contract, implemented by the consuming service.\n * transactionForKey MUST serialize operations for the same key across processes,\n * INCLUDING first creation; the callback must be retried on a conflicting insert.\n * insertOccurrenceIfNew MUST enforce a GLOBAL unique occurrenceId (not only per\n * incident); an occurrenceId already belonging to another key MUST throw.\n * On creation loadOrCreate MUST initialise occurrenceCount=1; recordOccurrence\n * MUST NOT increment that first occurrence again. If a duplicate is encountered\n * when newlyCreated=true, the transaction MUST fail and roll back creation.\n * Every operation in this port, including optional support notification, MUST\n * participate in the SAME database transaction and roll back together.\n */\nexport interface IncidentTransaction {\n loadOrCreate(input: { incidentKey: string; occurrence: OperationalIncidentOccurrenceV1 }): Promise<IncidentSnapshot & { newlyCreated: boolean }>;\n insertOccurrenceIfNew(input: { incidentKey: string; occurrence: OperationalIncidentOccurrenceV1 }): Promise<boolean>;\n recordOccurrence(input: {\n incidentKey: string;\n occurrence: OperationalIncidentOccurrenceV1;\n reopen: boolean;\n newlyCreated: boolean;\n }): Promise<IncidentSnapshot>;\n /** Optional, safe internal support projection; at most one per incident key. */\n notifySupportOnce?(input: { incidentKey: string; occurrence: OperationalIncidentOccurrenceV1 }): Promise<void>;\n}\nexport interface IncidentPersistence {\n transactionForKey<T>(key: string, fn: (tx: IncidentTransaction) => Promise<T>): Promise<T>;\n}\nexport interface IncidentReporterLogger {\n error(event: string, fields: Record<string, unknown>): void;\n}\n\n/** Reporter never turns an operational-notification failure into the producer's failure. */\nexport function createOperationalIncidentReporter(options: {\n persistence: IncidentPersistence;\n logger: IncidentReporterLogger;\n notifySupport?: boolean;\n}) {\n return {\n async report(raw: unknown): Promise<IncidentReportResult | null> {\n let occurrence: OperationalIncidentOccurrenceV1;\n try {\n occurrence = parseOperationalIncidentOccurrenceV1(raw);\n } catch {\n // Fail closed, and do not log potentially sensitive invalid payloads.\n options.logger.error(\"operational_incident.invalid_occurrence\", {});\n return null;\n }\n const incidentKey = createOperationalIncidentKey({\n sourceService: occurrence.sourceService, environment: occurrence.environment,\n category: occurrence.category, reasonCode: occurrence.reasonCode,\n resourceKey: occurrence.resourceKey, shopId: occurrence.shopId,\n });\n try {\n return await options.persistence.transactionForKey(incidentKey, async (tx) => {\n const current = await tx.loadOrCreate({ incidentKey, occurrence });\n const recorded = await tx.insertOccurrenceIfNew({ incidentKey, occurrence });\n if (!recorded) {\n return { incidentKey, recorded: false, occurrenceCount: current.occurrenceCount,\n status: current.status };\n }\n const updated = await tx.recordOccurrence({ incidentKey, occurrence,\n reopen: current.status === \"RESOLVED\", newlyCreated: current.newlyCreated });\n if (options.notifySupport && occurrence.shopId !== null && tx.notifySupportOnce) {\n await tx.notifySupportOnce({ incidentKey, occurrence });\n }\n return { incidentKey, recorded: true, occurrenceCount: updated.occurrenceCount,\n status: \"OPEN\" as const };\n });\n } catch {\n options.logger.error(\"operational_incident.report_failed\", {\n incidentKey, sourceService: occurrence.sourceService,\n environment: occurrence.environment, occurrenceId: occurrence.occurrenceId,\n });\n return null;\n }\n },\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { OperationalIncidentOccurrenceV1 } from \"./index.js\";\nimport type { IncidentPersistence, IncidentSnapshot, IncidentTransaction } from \"./reporter.js\";\n\n/** A deliberately small interface; the application's Prisma transaction client supplies it. */\nexport interface OperationalIncidentSqlClient {\n $queryRawUnsafe<T = unknown>(query: string, ...parameters: unknown[]): Promise<T>;\n}\nexport interface OperationalIncidentPrismaClient {\n $transaction<T>(fn: (tx: OperationalIncidentSqlClient) => Promise<T>, options?: {\n maxWait?: number; timeout?: number; isolationLevel?: \"Serializable\";\n }): Promise<T>;\n}\n\n/** Optional merchant-support projection. This callback MUST use the supplied transaction. */\nexport type OperationalIncidentSupportWriter = (input: {\n tx: OperationalIncidentSqlClient;\n incidentId: string;\n incidentKey: string;\n occurrence: OperationalIncidentOccurrenceV1;\n}) => Promise<void>;\n\ninterface IncidentRow {\n id: string;\n incidentKey: string;\n status: \"OPEN\" | \"RESOLVED\";\n occurrenceCount: number;\n lastOccurredAt: Date;\n}\n\nasync function rows<T>(tx: OperationalIncidentSqlClient, statement: string, ...params: unknown[]): Promise<T[]> {\n return tx.$queryRawUnsafe<T[]>(statement, ...params);\n}\n\nfunction snapshot(row: IncidentRow): IncidentSnapshot {\n return { incidentKey: row.incidentKey, status: row.status,\n occurrenceCount: row.occurrenceCount, lastOccurredAt: row.lastOccurredAt };\n}\n\nclass PgIncidentTransaction implements IncidentTransaction {\n private incidentId: string | null = null;\n private newIncident = false;\n constructor(private readonly tx: OperationalIncidentSqlClient,\n private readonly supportWriter?: OperationalIncidentSupportWriter) {}\n\n async loadOrCreate({ incidentKey, occurrence }: {\n incidentKey: string; occurrence: OperationalIncidentOccurrenceV1;\n }): Promise<IncidentSnapshot & { newlyCreated: boolean }> {\n const inserted = await rows<IncidentRow>(this.tx, `\n INSERT INTO support.\"OperationalIncident\" (\n \"id\", \"incidentKey\", \"sourceService\", \"environment\", \"category\", \"reasonCode\",\n \"resourceKey\", \"shopId\", \"severity\", \"status\", \"occurrenceCount\",\n \"firstOccurredAt\", \"lastOccurredAt\", \"lastCorrelationId\")\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'OPEN',1,$10::timestamptz,$10::timestamptz,$11)\n ON CONFLICT (\"incidentKey\") DO NOTHING RETURNING *`,\n randomUUID(), incidentKey, occurrence.sourceService, occurrence.environment,\n occurrence.category, occurrence.reasonCode, occurrence.resourceKey,\n occurrence.shopId, occurrence.severity, occurrence.occurredAt, occurrence.correlationId);\n this.newIncident = inserted.length > 0;\n const found = await rows<IncidentRow>(this.tx,\n `SELECT * FROM support.\"OperationalIncident\" WHERE \"incidentKey\" = $1 FOR UPDATE`, incidentKey);\n if (found.length !== 1) throw new Error(\"incident row not found after upsert\");\n this.incidentId = found[0]!.id;\n return { ...snapshot(found[0]!), newlyCreated: this.newIncident };\n }\n\n async insertOccurrenceIfNew({ incidentKey, occurrence }: {\n incidentKey: string; occurrence: OperationalIncidentOccurrenceV1;\n }): Promise<boolean> {\n if (!this.incidentId) throw new Error(\"incident not loaded\");\n const created = await rows<{ id: string }>(this.tx, `\n INSERT INTO support.\"OperationalIncidentEvent\"\n (\"id\", \"incidentId\", \"eventType\", \"correlationId\", \"occurredAt\")\n VALUES ($1::uuid, $2, 'OCCURRED', $3, $4::timestamptz)\n ON CONFLICT (\"id\") DO NOTHING RETURNING \"id\"`,\n occurrence.occurrenceId, this.incidentId, occurrence.correlationId, occurrence.occurredAt);\n if (!created.length) {\n if (this.newIncident) throw new Error(\"duplicate occurrence cannot create new incident\");\n const existing = await rows<{ incidentKey: string }>(this.tx, `\n SELECT i.\"incidentKey\" FROM support.\"OperationalIncidentEvent\" e\n JOIN support.\"OperationalIncident\" i ON i.\"id\" = e.\"incidentId\"\n WHERE e.\"id\" = $1::uuid`, occurrence.occurrenceId);\n if (existing.length !== 1 || existing[0]!.incidentKey !== incidentKey) {\n throw new Error(\"occurrence already belongs to another incident\");\n }\n return false;\n }\n return true;\n }\n\n async recordOccurrence({ incidentKey, occurrence, reopen, newlyCreated }: {\n incidentKey: string; occurrence: OperationalIncidentOccurrenceV1;\n reopen: boolean; newlyCreated: boolean;\n }): Promise<IncidentSnapshot> {\n if (!this.incidentId) throw new Error(\"incident not loaded\");\n if (reopen) {\n await rows(this.tx, `INSERT INTO support.\"OperationalIncidentEvent\"\n (\"id\", \"incidentId\", \"eventType\", \"correlationId\", \"occurredAt\")\n VALUES ($1::uuid,$2,'REOPENED',$3,$4::timestamptz)`,\n randomUUID(), this.incidentId, occurrence.correlationId, occurrence.occurredAt);\n }\n const updated = await rows<IncidentRow>(this.tx, `\n UPDATE support.\"OperationalIncident\" SET\n \"occurrenceCount\" = \"occurrenceCount\" + $2::integer,\n \"lastOccurredAt\" = GREATEST(\"lastOccurredAt\", $3::timestamptz),\n \"lastCorrelationId\" = $4,\n \"severity\" = $5,\n \"status\" = 'OPEN', \"resolvedAt\" = NULL,\n \"resolvedByPlatformAdminId\" = NULL, \"resolutionNote\" = NULL,\n \"revision\" = \"revision\" + 1\n WHERE \"incidentKey\" = $1 RETURNING *`, incidentKey,\n newlyCreated ? 0 : 1, occurrence.occurredAt, occurrence.correlationId, occurrence.severity);\n if (updated.length !== 1) throw new Error(\"incident update failed\");\n return snapshot(updated[0]!);\n }\n\n async notifySupportOnce(input: { incidentKey: string; occurrence: OperationalIncidentOccurrenceV1 }): Promise<void> {\n if (this.supportWriter && this.incidentId) await this.supportWriter({\n tx: this.tx, incidentId: this.incidentId,\n incidentKey: input.incidentKey, occurrence: input.occurrence,\n });\n }\n}\n\nfunction retryable(error: unknown): boolean {\n const code = typeof error === \"object\" && error !== null && \"code\" in error\n ? (error as { code?: unknown }).code : undefined;\n return code === \"P2034\" || code === \"40001\" || code === \"40P01\";\n}\n\n/**\n * PostgreSQL/Prisma adapter. No global Prisma client or credentials are created.\n * All writes and an optional support projection occur within a single transaction.\n * Uses a per-key transaction advisory lock and a row lock for cross-process safety.\n */\nexport function createPrismaOperationalIncidentPersistence(options: {\n prisma: OperationalIncidentPrismaClient;\n supportWriter?: OperationalIncidentSupportWriter;\n maxAttempts?: number;\n}): IncidentPersistence {\n const maxAttempts = options.maxAttempts ?? 3;\n if (!Number.isInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 10) {\n throw new Error(\"maxAttempts must be between 1 and 10\");\n }\n return {\n async transactionForKey<T>(key: string, fn: (tx: IncidentTransaction) => Promise<T>): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await options.prisma.$transaction(async (tx) => {\n await rows(tx, `SELECT pg_advisory_xact_lock(hashtextextended($1::text, 0))::text AS acquired`, key);\n return fn(new PgIncidentTransaction(tx, options.supportWriter));\n }, { isolationLevel: \"Serializable\", timeout: 15000 });\n } catch (error) {\n if (attempt >= maxAttempts || !retryable(error)) throw error;\n }\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,9 +2,9 @@ import { z } from 'zod';
|
|
|
2
2
|
import { PrimitiveType } from 'intl-messageformat';
|
|
3
3
|
|
|
4
4
|
declare const LanguageSourceSchema: z.ZodEnum<{
|
|
5
|
+
shopify: "shopify";
|
|
5
6
|
"customer-explicit": "customer-explicit";
|
|
6
7
|
detected: "detected";
|
|
7
|
-
shopify: "shopify";
|
|
8
8
|
"merchant-default": "merchant-default";
|
|
9
9
|
"platform-default": "platform-default";
|
|
10
10
|
}>;
|
|
@@ -45,9 +45,9 @@ declare const TimeZoneIdSchema: z.ZodPipe<z.ZodString, z.ZodTransform<string, st
|
|
|
45
45
|
declare const InternationalContextSchema: z.ZodObject<{
|
|
46
46
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
47
47
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
48
|
+
shopify: "shopify";
|
|
48
49
|
"customer-explicit": "customer-explicit";
|
|
49
50
|
detected: "detected";
|
|
50
|
-
shopify: "shopify";
|
|
51
51
|
"merchant-default": "merchant-default";
|
|
52
52
|
"platform-default": "platform-default";
|
|
53
53
|
}>>;
|
package/dist/shopify/index.d.ts
CHANGED
|
@@ -388,9 +388,9 @@ declare const ShopifyCheckoutCreatedEventV2Schema: z.ZodObject<{
|
|
|
388
388
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
389
389
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
390
390
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
391
|
+
shopify: "shopify";
|
|
391
392
|
"customer-explicit": "customer-explicit";
|
|
392
393
|
detected: "detected";
|
|
393
|
-
shopify: "shopify";
|
|
394
394
|
"merchant-default": "merchant-default";
|
|
395
395
|
"platform-default": "platform-default";
|
|
396
396
|
}>>;
|
|
@@ -425,9 +425,9 @@ declare const ShopifyCartActivityEventV2Schema: z.ZodObject<{
|
|
|
425
425
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
426
426
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
427
427
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
428
|
+
shopify: "shopify";
|
|
428
429
|
"customer-explicit": "customer-explicit";
|
|
429
430
|
detected: "detected";
|
|
430
|
-
shopify: "shopify";
|
|
431
431
|
"merchant-default": "merchant-default";
|
|
432
432
|
"platform-default": "platform-default";
|
|
433
433
|
}>>;
|
|
@@ -460,9 +460,9 @@ declare const ShopifyCheckoutUpdatedEventV2Schema: z.ZodObject<{
|
|
|
460
460
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
461
461
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
462
462
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
463
|
+
shopify: "shopify";
|
|
463
464
|
"customer-explicit": "customer-explicit";
|
|
464
465
|
detected: "detected";
|
|
465
|
-
shopify: "shopify";
|
|
466
466
|
"merchant-default": "merchant-default";
|
|
467
467
|
"platform-default": "platform-default";
|
|
468
468
|
}>>;
|
|
@@ -494,9 +494,9 @@ declare const ShopifyOrderCompletedEventV2Schema: z.ZodObject<{
|
|
|
494
494
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
495
495
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
496
496
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
497
|
+
shopify: "shopify";
|
|
497
498
|
"customer-explicit": "customer-explicit";
|
|
498
499
|
detected: "detected";
|
|
499
|
-
shopify: "shopify";
|
|
500
500
|
"merchant-default": "merchant-default";
|
|
501
501
|
"platform-default": "platform-default";
|
|
502
502
|
}>>;
|
|
@@ -531,9 +531,9 @@ declare const ShopifyRecoveryEventV2Schema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
531
531
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
532
532
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
533
533
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
534
|
+
shopify: "shopify";
|
|
534
535
|
"customer-explicit": "customer-explicit";
|
|
535
536
|
detected: "detected";
|
|
536
|
-
shopify: "shopify";
|
|
537
537
|
"merchant-default": "merchant-default";
|
|
538
538
|
"platform-default": "platform-default";
|
|
539
539
|
}>>;
|
|
@@ -564,9 +564,9 @@ declare const ShopifyRecoveryEventV2Schema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
564
564
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
565
565
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
566
566
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
567
|
+
shopify: "shopify";
|
|
567
568
|
"customer-explicit": "customer-explicit";
|
|
568
569
|
detected: "detected";
|
|
569
|
-
shopify: "shopify";
|
|
570
570
|
"merchant-default": "merchant-default";
|
|
571
571
|
"platform-default": "platform-default";
|
|
572
572
|
}>>;
|
|
@@ -599,9 +599,9 @@ declare const ShopifyRecoveryEventV2Schema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
599
599
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
600
600
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
601
601
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
602
|
+
shopify: "shopify";
|
|
602
603
|
"customer-explicit": "customer-explicit";
|
|
603
604
|
detected: "detected";
|
|
604
|
-
shopify: "shopify";
|
|
605
605
|
"merchant-default": "merchant-default";
|
|
606
606
|
"platform-default": "platform-default";
|
|
607
607
|
}>>;
|
|
@@ -631,9 +631,9 @@ declare const ShopifyRecoveryEventV2Schema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
631
631
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
632
632
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
633
633
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
634
|
+
shopify: "shopify";
|
|
634
635
|
"customer-explicit": "customer-explicit";
|
|
635
636
|
detected: "detected";
|
|
636
|
-
shopify: "shopify";
|
|
637
637
|
"merchant-default": "merchant-default";
|
|
638
638
|
"platform-default": "platform-default";
|
|
639
639
|
}>>;
|
|
@@ -676,7 +676,7 @@ declare function safeParseShopifyRecoveryEventV2(input: unknown): z.ZodSafeParse
|
|
|
676
676
|
};
|
|
677
677
|
internationalContext?: {
|
|
678
678
|
languageTag: string | null;
|
|
679
|
-
languageSource: "
|
|
679
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
680
680
|
countryCode: string | null;
|
|
681
681
|
currencyCode: string | null;
|
|
682
682
|
timeZone: string | null;
|
|
@@ -703,7 +703,7 @@ declare function safeParseShopifyRecoveryEventV2(input: unknown): z.ZodSafeParse
|
|
|
703
703
|
};
|
|
704
704
|
internationalContext?: {
|
|
705
705
|
languageTag: string | null;
|
|
706
|
-
languageSource: "
|
|
706
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
707
707
|
countryCode: string | null;
|
|
708
708
|
currencyCode: string | null;
|
|
709
709
|
timeZone: string | null;
|
|
@@ -729,7 +729,7 @@ declare function safeParseShopifyRecoveryEventV2(input: unknown): z.ZodSafeParse
|
|
|
729
729
|
};
|
|
730
730
|
internationalContext?: {
|
|
731
731
|
languageTag: string | null;
|
|
732
|
-
languageSource: "
|
|
732
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
733
733
|
countryCode: string | null;
|
|
734
734
|
currencyCode: string | null;
|
|
735
735
|
timeZone: string | null;
|
|
@@ -758,7 +758,7 @@ declare function safeParseShopifyRecoveryEventV2(input: unknown): z.ZodSafeParse
|
|
|
758
758
|
};
|
|
759
759
|
internationalContext?: {
|
|
760
760
|
languageTag: string | null;
|
|
761
|
-
languageSource: "
|
|
761
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
762
762
|
countryCode: string | null;
|
|
763
763
|
currencyCode: string | null;
|
|
764
764
|
timeZone: string | null;
|
|
@@ -802,9 +802,9 @@ declare const ShopifyCheckoutUpdatedEventV3Schema: z.ZodObject<{
|
|
|
802
802
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
803
803
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
804
804
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
805
|
+
shopify: "shopify";
|
|
805
806
|
"customer-explicit": "customer-explicit";
|
|
806
807
|
detected: "detected";
|
|
807
|
-
shopify: "shopify";
|
|
808
808
|
"merchant-default": "merchant-default";
|
|
809
809
|
"platform-default": "platform-default";
|
|
810
810
|
}>>;
|
|
@@ -840,9 +840,9 @@ declare const ShopifyRecoveryEventSchema: z.ZodUnion<readonly [z.ZodDiscriminate
|
|
|
840
840
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
841
841
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
842
842
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
843
|
+
shopify: "shopify";
|
|
843
844
|
"customer-explicit": "customer-explicit";
|
|
844
845
|
detected: "detected";
|
|
845
|
-
shopify: "shopify";
|
|
846
846
|
"merchant-default": "merchant-default";
|
|
847
847
|
"platform-default": "platform-default";
|
|
848
848
|
}>>;
|
|
@@ -873,9 +873,9 @@ declare const ShopifyRecoveryEventSchema: z.ZodUnion<readonly [z.ZodDiscriminate
|
|
|
873
873
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
874
874
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
875
875
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
876
|
+
shopify: "shopify";
|
|
876
877
|
"customer-explicit": "customer-explicit";
|
|
877
878
|
detected: "detected";
|
|
878
|
-
shopify: "shopify";
|
|
879
879
|
"merchant-default": "merchant-default";
|
|
880
880
|
"platform-default": "platform-default";
|
|
881
881
|
}>>;
|
|
@@ -908,9 +908,9 @@ declare const ShopifyRecoveryEventSchema: z.ZodUnion<readonly [z.ZodDiscriminate
|
|
|
908
908
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
909
909
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
910
910
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
911
|
+
shopify: "shopify";
|
|
911
912
|
"customer-explicit": "customer-explicit";
|
|
912
913
|
detected: "detected";
|
|
913
|
-
shopify: "shopify";
|
|
914
914
|
"merchant-default": "merchant-default";
|
|
915
915
|
"platform-default": "platform-default";
|
|
916
916
|
}>>;
|
|
@@ -940,9 +940,9 @@ declare const ShopifyRecoveryEventSchema: z.ZodUnion<readonly [z.ZodDiscriminate
|
|
|
940
940
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
941
941
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
942
942
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
943
|
+
shopify: "shopify";
|
|
943
944
|
"customer-explicit": "customer-explicit";
|
|
944
945
|
detected: "detected";
|
|
945
|
-
shopify: "shopify";
|
|
946
946
|
"merchant-default": "merchant-default";
|
|
947
947
|
"platform-default": "platform-default";
|
|
948
948
|
}>>;
|
|
@@ -975,9 +975,9 @@ declare const ShopifyRecoveryEventSchema: z.ZodUnion<readonly [z.ZodDiscriminate
|
|
|
975
975
|
internationalContext: z.ZodOptional<z.ZodObject<{
|
|
976
976
|
languageTag: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
977
977
|
languageSource: z.ZodNullable<z.ZodEnum<{
|
|
978
|
+
shopify: "shopify";
|
|
978
979
|
"customer-explicit": "customer-explicit";
|
|
979
980
|
detected: "detected";
|
|
980
|
-
shopify: "shopify";
|
|
981
981
|
"merchant-default": "merchant-default";
|
|
982
982
|
"platform-default": "platform-default";
|
|
983
983
|
}>>;
|
|
@@ -1019,7 +1019,7 @@ declare function safeParseShopifyRecoveryEvent(input: unknown): z.ZodSafeParseRe
|
|
|
1019
1019
|
};
|
|
1020
1020
|
internationalContext?: {
|
|
1021
1021
|
languageTag: string | null;
|
|
1022
|
-
languageSource: "
|
|
1022
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
1023
1023
|
countryCode: string | null;
|
|
1024
1024
|
currencyCode: string | null;
|
|
1025
1025
|
timeZone: string | null;
|
|
@@ -1046,7 +1046,7 @@ declare function safeParseShopifyRecoveryEvent(input: unknown): z.ZodSafeParseRe
|
|
|
1046
1046
|
};
|
|
1047
1047
|
internationalContext?: {
|
|
1048
1048
|
languageTag: string | null;
|
|
1049
|
-
languageSource: "
|
|
1049
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
1050
1050
|
countryCode: string | null;
|
|
1051
1051
|
currencyCode: string | null;
|
|
1052
1052
|
timeZone: string | null;
|
|
@@ -1072,7 +1072,7 @@ declare function safeParseShopifyRecoveryEvent(input: unknown): z.ZodSafeParseRe
|
|
|
1072
1072
|
};
|
|
1073
1073
|
internationalContext?: {
|
|
1074
1074
|
languageTag: string | null;
|
|
1075
|
-
languageSource: "
|
|
1075
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
1076
1076
|
countryCode: string | null;
|
|
1077
1077
|
currencyCode: string | null;
|
|
1078
1078
|
timeZone: string | null;
|
|
@@ -1101,7 +1101,7 @@ declare function safeParseShopifyRecoveryEvent(input: unknown): z.ZodSafeParseRe
|
|
|
1101
1101
|
};
|
|
1102
1102
|
internationalContext?: {
|
|
1103
1103
|
languageTag: string | null;
|
|
1104
|
-
languageSource: "
|
|
1104
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
1105
1105
|
countryCode: string | null;
|
|
1106
1106
|
currencyCode: string | null;
|
|
1107
1107
|
timeZone: string | null;
|
|
@@ -1130,7 +1130,7 @@ declare function safeParseShopifyRecoveryEvent(input: unknown): z.ZodSafeParseRe
|
|
|
1130
1130
|
};
|
|
1131
1131
|
internationalContext?: {
|
|
1132
1132
|
languageTag: string | null;
|
|
1133
|
-
languageSource: "
|
|
1133
|
+
languageSource: "shopify" | "customer-explicit" | "detected" | "merchant-default" | "platform-default" | null;
|
|
1134
1134
|
countryCode: string | null;
|
|
1135
1135
|
currencyCode: string | null;
|
|
1136
1136
|
timeZone: string | null;
|
|
@@ -4,7 +4,7 @@ import { S as StructuredLogger } from './types-B32wDMLO.js';
|
|
|
4
4
|
/** Finite, non-customer-facing failure taxonomy for Commerce hosts and logs. */
|
|
5
5
|
declare const RUNNER_DIAGNOSTIC_STAGES: readonly ["preflight.validate", "preflight.contract", "runtime.cancellation", "runtime.deadline", "runtime.operation", "tools.resolve", "model.invoke", "model.validate", "model.route", "tool.authorize", "tool.validate", "tool.execute", "tool.result", "evidence.validate", "final.validate", "graph.execute"];
|
|
6
6
|
type RunnerDiagnosticStage = typeof RUNNER_DIAGNOSTIC_STAGES[number];
|
|
7
|
-
declare const RUNNER_DIAGNOSTIC_REASONS: readonly ["CANCELLED_BY_CALLER", "TURN_DEADLINE_EXCEEDED", "OPERATION_TIMEOUT", "UPSTREAM_OPERATION_FAILED", "UNEXPECTED_EXCEPTION", "INPUT_TOO_LARGE", "TURN_INVALID", "GRANT_INVALID", "MANIFEST_INVALID", "GRANT_MISMATCH", "RUNNER_VERSION_INCOMPATIBLE", "RESPONSE_CONTRACT_INVALID", "LANGUAGE_INVALID", "BUDGET_INVALID", "CONTEXT_INVALID", "TOOL_REGISTRATION_INVALID", "MODEL_STEP_BUDGET_EXHAUSTED", "MODEL_INVOCATION_FAILED", "MODEL_STEP_NOT_OBJECT", "MODEL_STEP_CALLS_MISSING", "MODEL_STEP_CALLS_NOT_ARRAY", "MODEL_OUTPUT_TOKENS_MISSING", "MODEL_OUTPUT_TOKENS_NOT_SAFE_INTEGER", "MODEL_OUTPUT_TOKENS_NEGATIVE", "MODEL_OUTPUT_TOKENS_OVER_BUDGET", "MODEL_TOOL_CALL_LIMIT_EXCEEDED", "MODEL_TOOL_CALL_NULL", "MODEL_TOOL_CALL_NOT_OBJECT", "MODEL_TOOL_CALL_NAME_MISSING", "MODEL_TOOL_CALL_NAME_NOT_STRING", "MODEL_TOOL_CALL_NAME_EMPTY", "MODEL_TOOL_CALL_NAME_TOO_LONG", "MODEL_TOOL_CALL_ARGUMENTS_MISSING", "MODEL_TOOL_CALL_ARGUMENTS_NULL", "MODEL_TOOL_CALL_ARGUMENTS_NOT_OBJECT", "MODEL_STEP_TOO_LARGE", "MODEL_STEP_NOT_SERIALIZABLE", "MODEL_STEP_VALIDATION_FAILED", "MODEL_CALL_ROUTE_INVALID", "MODEL_RESULT_MISSING", "REQUEST_INVALID", "MODEL_REQUEST_HISTORY_INVALID", "MODEL_REQUEST_TOOL_RESULT_INVALID", "MODEL_CONFIGURATION_INVALID", "MODEL_CREDENTIAL_INVALID", "MODEL_ADAPTER_INITIALIZATION_FAILED", "MODEL_REQUEST_CANCELLED", "TOOL_BIND_FAILED", "PROVIDER_AUTH_FAILED", "PROVIDER_RATE_LIMITED", "PROVIDER_HTTP_ERROR", "PROVIDER_REQUEST_FAILED", "PROVIDER_RESPONSE_INVALID", "PROVIDER_RESPONSE_USAGE_INVALID", "PROVIDER_RESPONSE_TOOL_CALL_INVALID", "TOOL_DESCRIPTOR_MISMATCH", "TOOL_AUTHORIZATION_FAILED", "TOOL_INPUT_INVALID", "TOOL_CALL_BUDGET_EXHAUSTED", "TOOL_EXECUTION_FAILED", "TOOL_RESULT_TOO_LARGE", "TOOL_RESULT_INVALID", "TOOL_RESULT_MISSING", "TOOL_STALE_TURN", "EVIDENCE_INVALID", "FINAL_SCHEMA_INVALID", "FINAL_LANGUAGE_MISMATCH", "FINAL_REFERRAL_REQUIRED", "FINAL_EVIDENCE_INVALID", "FINAL_EVIDENCE_EXPIRED", "EVIDENCE_RESERVATION_EXCEEDED"];
|
|
7
|
+
declare const RUNNER_DIAGNOSTIC_REASONS: readonly ["CANCELLED_BY_CALLER", "TURN_DEADLINE_EXCEEDED", "OPERATION_TIMEOUT", "UPSTREAM_OPERATION_FAILED", "UNEXPECTED_EXCEPTION", "INPUT_TOO_LARGE", "TURN_INVALID", "GRANT_INVALID", "MANIFEST_INVALID", "GRANT_MISMATCH", "RUNNER_VERSION_INCOMPATIBLE", "RESPONSE_CONTRACT_INVALID", "LANGUAGE_INVALID", "BUDGET_INVALID", "CONTEXT_INVALID", "TOOL_REGISTRATION_INVALID", "MODEL_STEP_BUDGET_EXHAUSTED", "MODEL_INVOCATION_FAILED", "MODEL_STEP_NOT_OBJECT", "MODEL_STEP_CALLS_MISSING", "MODEL_STEP_CALLS_NOT_ARRAY", "MODEL_OUTPUT_TOKENS_MISSING", "MODEL_OUTPUT_TOKENS_NOT_SAFE_INTEGER", "MODEL_OUTPUT_TOKENS_NEGATIVE", "MODEL_OUTPUT_TOKENS_OVER_BUDGET", "MODEL_TOOL_CALL_LIMIT_EXCEEDED", "MODEL_TOOL_CALL_NULL", "MODEL_TOOL_CALL_NOT_OBJECT", "MODEL_TOOL_CALL_NAME_MISSING", "MODEL_TOOL_CALL_NAME_NOT_STRING", "MODEL_TOOL_CALL_NAME_EMPTY", "MODEL_TOOL_CALL_NAME_TOO_LONG", "MODEL_TOOL_CALL_ARGUMENTS_MISSING", "MODEL_TOOL_CALL_ARGUMENTS_NULL", "MODEL_TOOL_CALL_ARGUMENTS_NOT_OBJECT", "MODEL_TEXT_NOT_STRING", "MODEL_TEXT_TOO_LONG", "MODEL_STEP_TOO_LARGE", "MODEL_STEP_NOT_SERIALIZABLE", "MODEL_STEP_VALIDATION_FAILED", "MODEL_CALL_ROUTE_INVALID", "MODEL_RESULT_MISSING", "REQUEST_INVALID", "MODEL_REQUEST_HISTORY_INVALID", "MODEL_REQUEST_TOOL_RESULT_INVALID", "MODEL_CONFIGURATION_INVALID", "MODEL_CREDENTIAL_INVALID", "MODEL_ADAPTER_INITIALIZATION_FAILED", "MODEL_REQUEST_CANCELLED", "TOOL_BIND_FAILED", "PROVIDER_AUTH_FAILED", "PROVIDER_RATE_LIMITED", "PROVIDER_HTTP_ERROR", "PROVIDER_REQUEST_FAILED", "PROVIDER_RESPONSE_INVALID", "PROVIDER_RESPONSE_USAGE_INVALID", "PROVIDER_RESPONSE_TOOL_CALL_INVALID", "TOOL_DESCRIPTOR_MISMATCH", "TOOL_AUTHORIZATION_FAILED", "TOOL_INPUT_INVALID", "TOOL_CALL_BUDGET_EXHAUSTED", "TOOL_EXECUTION_FAILED", "TOOL_RESULT_TOO_LARGE", "TOOL_RESULT_INVALID", "TOOL_RESULT_MISSING", "TOOL_STALE_TURN", "EVIDENCE_INVALID", "FINAL_SCHEMA_INVALID", "FINAL_LANGUAGE_MISMATCH", "FINAL_REFERRAL_REQUIRED", "FINAL_EVIDENCE_INVALID", "FINAL_EVIDENCE_EXPIRED", "EVIDENCE_RESERVATION_EXCEEDED"];
|
|
8
8
|
type RunnerDiagnosticReason = typeof RUNNER_DIAGNOSTIC_REASONS[number];
|
|
9
9
|
/** Recognised transport failures, never arbitrary OS/provider error codes. */
|
|
10
10
|
declare const RUNNER_TRANSPORT_CODES: readonly ["ENOTFOUND", "EAI_AGAIN", "ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "ENETUNREACH", "EHOSTUNREACH", "EPIPE", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_SOCKET", "CERT_HAS_EXPIRED", "UNABLE_TO_VERIFY_LEAF_SIGNATURE", "DEPTH_ZERO_SELF_SIGNED_CERT", "ERR_TLS_CERT_ALTNAME_INVALID"];
|
|
@@ -38,9 +38,11 @@ type ModelCall = {
|
|
|
38
38
|
name: string;
|
|
39
39
|
arguments: unknown;
|
|
40
40
|
};
|
|
41
|
+
/** `text` is optional so existing structured-tool invokers remain compatible. */
|
|
41
42
|
type ModelStep = {
|
|
42
43
|
calls: ModelCall[];
|
|
43
44
|
outputTokens: number;
|
|
45
|
+
text?: string;
|
|
44
46
|
};
|
|
45
47
|
type ModelRequest = {
|
|
46
48
|
instructions: readonly string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modainteract/moda-interact-shared",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -95,6 +95,10 @@
|
|
|
95
95
|
"./merchant-knowledge/node": {
|
|
96
96
|
"types": "./dist/merchant-knowledge/node.d.ts",
|
|
97
97
|
"import": "./dist/merchant-knowledge/node.js"
|
|
98
|
+
},
|
|
99
|
+
"./incidents": {
|
|
100
|
+
"types": "./dist/incidents/index.d.ts",
|
|
101
|
+
"import": "./dist/incidents/index.js"
|
|
98
102
|
}
|
|
99
103
|
},
|
|
100
104
|
"files": [
|
|
@@ -114,7 +118,8 @@
|
|
|
114
118
|
"prepack": "npm run build",
|
|
115
119
|
"validate:commerce-entrypoints": "node scripts/validate-commerce-entrypoints.mjs",
|
|
116
120
|
"validate:arch023-shared-entrypoints": "npm run build && node scripts/validate-arch023-shared-entrypoints.mjs",
|
|
117
|
-
"validate:arch024-model-entrypoints": "npm run build && node scripts/validate-arch024-model-entrypoints.mjs"
|
|
121
|
+
"validate:arch024-model-entrypoints": "npm run build && node scripts/validate-arch024-model-entrypoints.mjs",
|
|
122
|
+
"test:arch033-incidents:postgres": "node scripts/test-arch033-incidents-postgres.mjs"
|
|
118
123
|
},
|
|
119
124
|
"dependencies": {
|
|
120
125
|
"@opentelemetry/api": "^1.9.0",
|