@01.software/cli 0.16.0 → 0.17.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/index.js CHANGED
@@ -13,18 +13,52 @@ import {
13
13
 
14
14
  // src/lib/credentials.ts
15
15
  import {
16
+ chmodSync,
17
+ closeSync,
18
+ constants as fsConstants,
16
19
  existsSync,
20
+ fchmodSync,
21
+ fstatSync,
22
+ fsyncSync,
23
+ lstatSync,
17
24
  mkdirSync,
25
+ openSync,
18
26
  readFileSync,
19
- writeFileSync,
27
+ renameSync,
20
28
  unlinkSync,
29
+ writeFileSync,
21
30
  appendFileSync
22
31
  } from "fs";
23
- import { join } from "path";
32
+ import { spawnSync } from "child_process";
33
+ import { randomBytes } from "crypto";
34
+ import { dirname, join } from "path";
24
35
  import { homedir } from "os";
25
36
  var DIR_NAME = ".01software";
26
37
  var FILE_NAME = "credentials.json";
27
38
  var TENANTS_FILE = "tenants.json";
39
+ var LOCAL_CREDENTIAL_PATH = `${DIR_NAME}/${FILE_NAME}`;
40
+ var LOCAL_GITIGNORE_ENTRY = `${DIR_NAME}/`;
41
+ var PRIVATE_DIRECTORY_MODE = 448;
42
+ var PRIVATE_FILE_MODE = 384;
43
+ var CURRENT_USER_ID = process.platform !== "win32" && typeof process.getuid === "function" ? process.getuid() : null;
44
+ var HAS_POSIX_FILE_PERMISSIONS = CURRENT_USER_ID !== null;
45
+ var SANITIZED_GIT_ENVIRONMENT_VARIABLES = /* @__PURE__ */ new Set([
46
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
47
+ "GIT_CEILING_DIRECTORIES",
48
+ "GIT_COMMON_DIR",
49
+ "GIT_CONFIG_COUNT",
50
+ "GIT_CONFIG_PARAMETERS",
51
+ "GIT_DIR",
52
+ "GIT_DISCOVERY_ACROSS_FILESYSTEM",
53
+ "GIT_INDEX_FILE",
54
+ "GIT_ICASE_PATHSPECS",
55
+ "GIT_GLOB_PATHSPECS",
56
+ "GIT_LITERAL_PATHSPECS",
57
+ "GIT_NOGLOB_PATHSPECS",
58
+ "GIT_OBJECT_DIRECTORY",
59
+ "GIT_PREFIX",
60
+ "GIT_WORK_TREE"
61
+ ]);
28
62
  function getCredentialsPath() {
29
63
  return join(homedir(), DIR_NAME, FILE_NAME);
30
64
  }
@@ -32,19 +66,12 @@ function loadCredentials() {
32
66
  return loadCredentialsFrom(getCredentialsPath());
33
67
  }
34
68
  function saveCredentials(creds) {
35
- const dir = join(homedir(), DIR_NAME);
36
- if (!existsSync(dir)) {
37
- mkdirSync(dir, { recursive: true, mode: 448 });
38
- }
39
69
  const filePath = getCredentialsPath();
40
70
  const data = {
41
71
  ...creds,
42
72
  storedAt: (/* @__PURE__ */ new Date()).toISOString()
43
73
  };
44
- writeFileSync(filePath, JSON.stringify(data, null, 2), {
45
- encoding: "utf-8",
46
- mode: 384
47
- });
74
+ writeCredentialFile(filePath, JSON.stringify(data, null, 2));
48
75
  }
49
76
  function deleteCredentials() {
50
77
  const filePath = getCredentialsPath();
@@ -58,21 +85,19 @@ function getLocalCredentialsPath() {
58
85
  function loadLocalCredentials() {
59
86
  return loadCredentialsFrom(getLocalCredentialsPath());
60
87
  }
61
- function saveLocalCredentials(creds) {
62
- const dir = join(process.cwd(), DIR_NAME);
63
- if (!existsSync(dir)) {
64
- mkdirSync(dir, { recursive: true, mode: 448 });
65
- }
66
- const filePath = getLocalCredentialsPath();
88
+ function saveLocalCredentials(creds, workingDirectory = process.cwd()) {
89
+ const temporaryFileName = createTemporaryCredentialFileName();
90
+ ensureGitignore(workingDirectory, `${DIR_NAME}/${temporaryFileName}`);
91
+ const filePath = join(workingDirectory, DIR_NAME, FILE_NAME);
67
92
  const data = {
68
93
  ...creds,
69
94
  storedAt: (/* @__PURE__ */ new Date()).toISOString()
70
95
  };
71
- writeFileSync(filePath, JSON.stringify(data, null, 2), {
72
- encoding: "utf-8",
73
- mode: 384
74
- });
75
- ensureGitignore();
96
+ writeCredentialFile(
97
+ filePath,
98
+ JSON.stringify(data, null, 2),
99
+ temporaryFileName
100
+ );
76
101
  }
77
102
  function saveTenantList(tenants) {
78
103
  const dir = join(homedir(), DIR_NAME);
@@ -100,6 +125,161 @@ function loadTenantList() {
100
125
  return null;
101
126
  }
102
127
  }
128
+ function writeCredentialFile(filePath, content, temporaryFileName = createTemporaryCredentialFileName()) {
129
+ const directoryPath = dirname(filePath);
130
+ const directoryDescriptor = openPrivateCredentialDirectory(directoryPath);
131
+ const initialTarget = inspectCredentialTarget(filePath);
132
+ const tempPath = join(directoryPath, temporaryFileName);
133
+ let tempDescriptor;
134
+ try {
135
+ tempDescriptor = openSync(tempPath, "wx", PRIVATE_FILE_MODE);
136
+ const tempStats = fstatSync(tempDescriptor);
137
+ assertRegularOwnedCredentialFile(tempStats, "temporary credential file");
138
+ if (HAS_POSIX_FILE_PERMISSIONS) {
139
+ fchmodSync(tempDescriptor, PRIVATE_FILE_MODE);
140
+ assertMode(
141
+ fstatSync(tempDescriptor),
142
+ PRIVATE_FILE_MODE,
143
+ "temporary credential file"
144
+ );
145
+ }
146
+ writeFileSync(tempDescriptor, content, { encoding: "utf-8" });
147
+ fsyncSync(tempDescriptor);
148
+ closeSync(tempDescriptor);
149
+ tempDescriptor = void 0;
150
+ const currentTarget = inspectCredentialTarget(filePath);
151
+ if (!sameFileIdentity(initialTarget, currentTarget)) {
152
+ throw credentialFileProtectionError(
153
+ "credential target changed while being checked"
154
+ );
155
+ }
156
+ renameSync(tempPath, filePath);
157
+ const finalStats = lstatCredentialPath(filePath);
158
+ if (!finalStats) {
159
+ throw credentialFileProtectionError(
160
+ "credential target disappeared after atomic replacement"
161
+ );
162
+ }
163
+ assertRegularOwnedCredentialFile(finalStats, "credential target");
164
+ if (HAS_POSIX_FILE_PERMISSIONS) {
165
+ assertMode(finalStats, PRIVATE_FILE_MODE, "credential target");
166
+ if (directoryDescriptor !== void 0) {
167
+ fsyncSync(directoryDescriptor);
168
+ }
169
+ }
170
+ } catch (error) {
171
+ if (tempDescriptor !== void 0) {
172
+ try {
173
+ closeSync(tempDescriptor);
174
+ } catch {
175
+ }
176
+ }
177
+ try {
178
+ unlinkSync(tempPath);
179
+ } catch {
180
+ }
181
+ throw error;
182
+ } finally {
183
+ if (directoryDescriptor !== void 0) {
184
+ closeSync(directoryDescriptor);
185
+ }
186
+ }
187
+ }
188
+ function openPrivateCredentialDirectory(directoryPath) {
189
+ let directoryStats = lstatCredentialPath(directoryPath);
190
+ if (!directoryStats) {
191
+ try {
192
+ mkdirSync(directoryPath, { mode: PRIVATE_DIRECTORY_MODE });
193
+ if (HAS_POSIX_FILE_PERMISSIONS) {
194
+ chmodSync(directoryPath, PRIVATE_DIRECTORY_MODE);
195
+ }
196
+ } catch (error) {
197
+ if (error.code !== "EEXIST") throw error;
198
+ }
199
+ directoryStats = lstatCredentialPath(directoryPath);
200
+ }
201
+ if (!directoryStats || directoryStats.isSymbolicLink() || !directoryStats.isDirectory()) {
202
+ throw credentialFileProtectionError(
203
+ "credential directory must be a regular directory, not a symlink"
204
+ );
205
+ }
206
+ assertCurrentUserOwns(directoryStats, "credential directory");
207
+ if (!HAS_POSIX_FILE_PERMISSIONS) return void 0;
208
+ const descriptor = openSync(
209
+ directoryPath,
210
+ fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW
211
+ );
212
+ try {
213
+ const openedStats = fstatSync(descriptor);
214
+ if (!openedStats.isDirectory() || openedStats.dev !== directoryStats.dev || openedStats.ino !== directoryStats.ino) {
215
+ throw credentialFileProtectionError(
216
+ "credential directory changed while being checked"
217
+ );
218
+ }
219
+ assertCurrentUserOwns(openedStats, "credential directory");
220
+ fchmodSync(descriptor, PRIVATE_DIRECTORY_MODE);
221
+ assertMode(
222
+ fstatSync(descriptor),
223
+ PRIVATE_DIRECTORY_MODE,
224
+ "credential directory"
225
+ );
226
+ return descriptor;
227
+ } catch (error) {
228
+ closeSync(descriptor);
229
+ throw error;
230
+ }
231
+ }
232
+ function inspectCredentialTarget(filePath) {
233
+ const stats = lstatCredentialPath(filePath);
234
+ if (!stats) return null;
235
+ assertRegularOwnedCredentialFile(stats, "credential target");
236
+ return { dev: stats.dev, ino: stats.ino };
237
+ }
238
+ function assertRegularOwnedCredentialFile(stats, description) {
239
+ if (stats.isSymbolicLink()) {
240
+ throw credentialFileProtectionError(`${description} must not be a symlink`);
241
+ }
242
+ if (!stats.isFile()) {
243
+ throw credentialFileProtectionError(`${description} must be a regular file`);
244
+ }
245
+ if (HAS_POSIX_FILE_PERMISSIONS && stats.nlink > 1) {
246
+ throw credentialFileProtectionError(
247
+ `${description} must not have multiple hard links`
248
+ );
249
+ }
250
+ assertCurrentUserOwns(stats, description);
251
+ }
252
+ function assertCurrentUserOwns(stats, description) {
253
+ if (CURRENT_USER_ID !== null && stats.uid !== CURRENT_USER_ID) {
254
+ throw credentialFileProtectionError(
255
+ `${description} must be owned by the current user`
256
+ );
257
+ }
258
+ }
259
+ function assertMode(stats, expectedMode, description) {
260
+ if ((stats.mode & 511) !== expectedMode) {
261
+ throw credentialFileProtectionError(
262
+ `${description} permissions could not be secured`
263
+ );
264
+ }
265
+ }
266
+ function sameFileIdentity(initial, current) {
267
+ if (!initial || !current) return initial === current;
268
+ return initial.dev === current.dev && initial.ino === current.ino;
269
+ }
270
+ function lstatCredentialPath(filePath) {
271
+ try {
272
+ return lstatSync(filePath);
273
+ } catch (error) {
274
+ if (error.code === "ENOENT") return null;
275
+ throw credentialFileProtectionError(
276
+ "credential filesystem target could not be inspected"
277
+ );
278
+ }
279
+ }
280
+ function credentialFileProtectionError(message) {
281
+ return new Error(`Cannot save credentials: ${message}`);
282
+ }
103
283
  function loadCredentialsFrom(filePath) {
104
284
  if (!existsSync(filePath)) return null;
105
285
  try {
@@ -111,25 +291,160 @@ function loadCredentialsFrom(filePath) {
111
291
  return null;
112
292
  }
113
293
  }
114
- function ensureGitignore() {
115
- const gitignorePath = join(process.cwd(), ".gitignore");
116
- const entry = ".01software/";
117
- if (existsSync(gitignorePath)) {
118
- const content = readFileSync(gitignorePath, "utf-8");
119
- if (content.includes(entry)) return;
120
- appendFileSync(gitignorePath, `
121
- ${entry}
122
- `, "utf-8");
123
- } else {
124
- writeFileSync(gitignorePath, `${entry}
125
- `, "utf-8");
294
+ function ensureGitignore(cwd, temporaryCredentialPath) {
295
+ const gitignorePath = join(cwd, ".gitignore");
296
+ assertSafeGitignorePath(gitignorePath);
297
+ const gitState = detectGitWorkTree(cwd);
298
+ if (gitState === "inside") {
299
+ assertCredentialPathIsUntracked(cwd);
300
+ if (areCredentialPathsIgnored(cwd, temporaryCredentialPath)) return;
301
+ ensureTerminalGitignoreEntry(gitignorePath, true);
302
+ if (!areCredentialPathsIgnored(cwd, temporaryCredentialPath)) {
303
+ throw credentialProtectionError(
304
+ "Git still reports a credential path as unignored"
305
+ );
306
+ }
307
+ return;
308
+ }
309
+ ensureTerminalGitignoreEntry(gitignorePath, false);
310
+ }
311
+ function assertSafeGitignorePath(gitignorePath) {
312
+ const stats = lstatIfPresent(gitignorePath);
313
+ if (stats && (!stats.isFile() || stats.isSymbolicLink())) {
314
+ throw credentialProtectionError(".gitignore must be a regular file");
315
+ }
316
+ }
317
+ function ensureTerminalGitignoreEntry(gitignorePath, forceAppend) {
318
+ const initialStats = lstatIfPresent(gitignorePath);
319
+ if (!initialStats) {
320
+ try {
321
+ writeFileSync(gitignorePath, `${LOCAL_GITIGNORE_ENTRY}
322
+ `, {
323
+ encoding: "utf-8",
324
+ flag: "wx"
325
+ });
326
+ return;
327
+ } catch (error) {
328
+ if (error.code !== "EEXIST") throw error;
329
+ return ensureTerminalGitignoreEntry(gitignorePath, forceAppend);
330
+ }
331
+ }
332
+ if (!initialStats.isFile() || initialStats.isSymbolicLink()) {
333
+ throw credentialProtectionError(".gitignore must be a regular file");
334
+ }
335
+ const descriptor = openSync(gitignorePath, "a+");
336
+ try {
337
+ const openedStats = fstatSync(descriptor);
338
+ if (!openedStats.isFile() || openedStats.dev !== initialStats.dev || openedStats.ino !== initialStats.ino) {
339
+ throw credentialProtectionError(".gitignore changed while being checked");
340
+ }
341
+ const content = readFileSync(descriptor, "utf-8");
342
+ const lastNonEmptyLine = content.split(/\r?\n/).filter((line) => line.length > 0).at(-1);
343
+ if (!forceAppend && (lastNonEmptyLine === LOCAL_GITIGNORE_ENTRY || lastNonEmptyLine === `/${LOCAL_GITIGNORE_ENTRY}`)) {
344
+ return;
345
+ }
346
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
347
+ appendFileSync(
348
+ descriptor,
349
+ `${separator}${LOCAL_GITIGNORE_ENTRY}
350
+ `,
351
+ "utf-8"
352
+ );
353
+ } finally {
354
+ closeSync(descriptor);
355
+ }
356
+ }
357
+ function detectGitWorkTree(cwd) {
358
+ const result = runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
359
+ if (result.error) {
360
+ if (result.error.code === "ENOENT" && !hasGitMarker(cwd)) {
361
+ return "outside";
362
+ }
363
+ throw credentialProtectionError("Git repository detection failed");
126
364
  }
365
+ if (result.status === 0) {
366
+ if (result.stdout.trim() === "true") return "inside";
367
+ throw credentialProtectionError(
368
+ "the current directory is not a Git work tree"
369
+ );
370
+ }
371
+ if (result.status === 128 && !hasGitMarker(cwd)) return "outside";
372
+ throw credentialProtectionError("Git repository detection failed");
373
+ }
374
+ function assertCredentialPathIsUntracked(cwd) {
375
+ const result = runGit(cwd, [
376
+ "ls-files",
377
+ "-z",
378
+ "--",
379
+ `:(icase,literal)${LOCAL_CREDENTIAL_PATH}`
380
+ ]);
381
+ if (result.error || result.status !== 0) {
382
+ throw credentialProtectionError("Git tracked-file verification failed");
383
+ }
384
+ if (result.stdout.length > 0) {
385
+ throw credentialProtectionError(
386
+ ".01software/credentials.json is already tracked by Git"
387
+ );
388
+ }
389
+ }
390
+ function createTemporaryCredentialFileName() {
391
+ return `.${FILE_NAME}.${randomBytes(12).toString("hex")}.tmp`;
392
+ }
393
+ function areCredentialPathsIgnored(cwd, temporaryCredentialPath) {
394
+ return isPathIgnored(cwd, LOCAL_CREDENTIAL_PATH) && isPathIgnored(cwd, temporaryCredentialPath);
395
+ }
396
+ function isPathIgnored(cwd, relativePath) {
397
+ const result = runGit(cwd, [
398
+ "check-ignore",
399
+ "--quiet",
400
+ "--no-index",
401
+ "--",
402
+ relativePath
403
+ ]);
404
+ if (result.error || result.status !== 0 && result.status !== 1) {
405
+ throw credentialProtectionError("Git ignore verification failed");
406
+ }
407
+ return result.status === 0;
408
+ }
409
+ function runGit(cwd, args) {
410
+ const env = { ...process.env, LC_ALL: "C" };
411
+ for (const key of Object.keys(env)) {
412
+ const normalizedKey = key.toUpperCase();
413
+ if (SANITIZED_GIT_ENVIRONMENT_VARIABLES.has(normalizedKey) || normalizedKey.startsWith("GIT_CONFIG_KEY_") || normalizedKey.startsWith("GIT_CONFIG_VALUE_")) {
414
+ delete env[key];
415
+ }
416
+ }
417
+ return spawnSync("git", args, {
418
+ cwd,
419
+ encoding: "utf-8",
420
+ env
421
+ });
422
+ }
423
+ function hasGitMarker(startPath) {
424
+ let currentPath = startPath;
425
+ while (true) {
426
+ if (lstatIfPresent(join(currentPath, ".git"))) return true;
427
+ const parentPath = dirname(currentPath);
428
+ if (parentPath === currentPath) return false;
429
+ currentPath = parentPath;
430
+ }
431
+ }
432
+ function lstatIfPresent(filePath) {
433
+ try {
434
+ return lstatSync(filePath);
435
+ } catch (error) {
436
+ if (error.code === "ENOENT") return null;
437
+ throw credentialProtectionError("local Git metadata could not be inspected");
438
+ }
439
+ }
440
+ function credentialProtectionError(message) {
441
+ return new Error(`Cannot save local credentials: ${message}`);
127
442
  }
128
443
 
129
444
  // src/lib/output.ts
130
445
  import pc from "picocolors";
131
446
 
132
- // ../errors/src/admin-error.ts
447
+ // ../../internal/errors/src/admin-error.ts
133
448
  var ADMIN_ERROR_CODES = {
134
449
  permission: [
135
450
  "tenant_mismatch",
@@ -268,7 +583,7 @@ var CLI_I18N_KO = {
268
583
  RetiredHexCredentialsRejected: "\uC774\uC804 hex \uC790\uACA9 \uC99D\uBA85\uC740 \uB354 \uC774\uC0C1 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 `01 login`\uC73C\uB85C \uB2E4\uC2DC \uC778\uC99D\uD558\uC138\uC694.",
269
584
  InvalidJsonObject: "`--{{label}}` \uC778\uC790\uB294 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
270
585
  InvalidJsonArray: "`--{{label}}` \uC778\uC790\uB294 JSON \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
271
- InvalidJsonValue: "`--{{label}}` \uC778\uC790\uC758 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {{value}}",
586
+ InvalidJsonValue: "`--{{label}}` \uC778\uC790\uC758 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
272
587
  Empty: "(\uC5C6\uC74C)",
273
588
  Total: "\uCD1D {{n}}\uAC1C",
274
589
  PageOf: "{{page}} / {{total}} \uD398\uC774\uC9C0",
@@ -300,7 +615,7 @@ var CLI_I18N_EN = {
300
615
  RetiredHexCredentialsRejected: "Retired hex credentials are no longer accepted \u2014 run `01 login` to re-authenticate.",
301
616
  InvalidJsonObject: "--{{label}} must be a JSON object.",
302
617
  InvalidJsonArray: "--{{label}} must be a JSON array.",
303
- InvalidJsonValue: "Invalid JSON for --{{label}}: {{value}}",
618
+ InvalidJsonValue: "Invalid JSON for --{{label}}.",
304
619
  Empty: "(empty)",
305
620
  Total: "{{n}} total",
306
621
  PageOf: "page {{page}}/{{total}}",
@@ -552,40 +867,24 @@ function parseSelect(input) {
552
867
  }
553
868
 
554
869
  // src/lib/parse.ts
555
- function failArg(label, value, expectedKind) {
556
- const message = expectedKind === "object" ? t("InvalidJsonObject", { label }) : expectedKind === "array" ? t("InvalidJsonArray", { label }) : t("InvalidJsonValue", { label, value });
870
+ function failArg(label, _value, expectedKind) {
871
+ const message = expectedKind === "object" ? t("InvalidJsonObject", { label }) : expectedKind === "array" ? t("InvalidJsonArray", { label }) : t("InvalidJsonValue", { label });
557
872
  exitWithError({
558
873
  type: "validation",
559
874
  code: "invalid_argument",
560
875
  field: label,
561
- // `message` is read by `printError` for the stderr headline so the
562
- // user sees the label/value in the one-line summary; `detail` carries
563
- // the structured fields for `--format json` consumers.
876
+ // Keep rejected values out of terminal and structured output. `message`
877
+ // is the stderr headline; `detail` carries only the stable field label for
878
+ // machine consumers.
564
879
  message,
565
- detail: { message, value, field: label }
880
+ detail: { message, field: label }
566
881
  });
567
882
  }
568
- function stringifyValue(value) {
569
- try {
570
- return JSON.stringify(value);
571
- } catch {
572
- return String(value);
573
- }
574
- }
575
- function formatSchemaIssues(label, issues) {
576
- return issues.map((issue) => {
577
- const path = issue.path.length > 0 ? issue.path.join(".") : label;
578
- return `${path}: ${issue.message}`;
579
- }).join("; ");
580
- }
581
883
  function parseWithSchema(value, label, schema) {
582
884
  const parsed = schema.safeParse(value);
583
885
  if (parsed.success) return parsed.data;
584
- const issues = parsed.error.issues.map((issue) => ({
585
- path: issue.path.map(String),
586
- message: issue.message
587
- }));
588
- const message = `Invalid value for --${label}: ${formatSchemaIssues(label, issues)}`;
886
+ const issues = parsed.error.issues.map((issue) => ({ code: issue.code }));
887
+ const message = `Invalid value for --${label}.`;
589
888
  exitWithError({
590
889
  type: "validation",
591
890
  code: "invalid_argument",
@@ -593,7 +892,6 @@ function parseWithSchema(value, label, schema) {
593
892
  message,
594
893
  detail: {
595
894
  message,
596
- value: stringifyValue(value),
597
895
  field: label,
598
896
  issues
599
897
  }
@@ -804,7 +1102,7 @@ function registerCrudCommands(program2, getClient2, getFormat2) {
804
1102
  });
805
1103
  }
806
1104
 
807
- // ../contracts/src/tenant/index.ts
1105
+ // ../../internal/contracts/src/tenant/index.ts
808
1106
  import { z } from "zod";
809
1107
  var tenantFieldConfigStateSchema = z.object({
810
1108
  hiddenFields: z.array(z.string()),
@@ -953,10 +1251,10 @@ var collectionSchemaResponseSchema = z.object({
953
1251
  }).strict()
954
1252
  }).strict();
955
1253
 
956
- // ../contracts/src/ecommerce/index.ts
957
- import { z as z4 } from "zod";
1254
+ // ../../internal/contracts/src/ecommerce/index.ts
1255
+ import { z as z7 } from "zod";
958
1256
 
959
- // ../contracts/src/ecommerce/product-upsert.ts
1257
+ // ../../internal/contracts/src/ecommerce/product-upsert.ts
960
1258
  import { z as z2 } from "zod";
961
1259
  var IdSchema = z2.union([z2.string(), z2.number()]).transform(String);
962
1260
  var RemovedLegacyMediaFieldSchema = z2.unknown().optional();
@@ -980,6 +1278,7 @@ var productFieldShape = {
980
1278
  publishedAt: z2.string().optional().nullable(),
981
1279
  categories: z2.array(IdSchema).optional(),
982
1280
  tags: z2.array(IdSchema).optional(),
1281
+ markets: z2.array(IdSchema).optional().nullable(),
983
1282
  seo: z2.object({
984
1283
  title: z2.string().optional().nullable(),
985
1284
  description: z2.string().optional().nullable()
@@ -1035,6 +1334,26 @@ var VariantOptionValueObjectSchema = z2.object({
1035
1334
  }).refine((data) => Boolean(data.valueSlug ?? data.valueId ?? data.value), {
1036
1335
  message: "Variant option value object requires valueSlug, valueId, or value"
1037
1336
  });
1337
+ var MAX_CATALOG_AMOUNT_DECIMAL_PLACES = 2;
1338
+ function catalogAmountDecimalPlaces(value) {
1339
+ if (!Number.isFinite(value)) return 0;
1340
+ const text = String(value);
1341
+ const exponential = text.indexOf("e-");
1342
+ if (exponential !== -1) return Number(text.slice(exponential + 2)) || 0;
1343
+ const dot = text.indexOf(".");
1344
+ return dot === -1 ? 0 : text.length - dot - 1;
1345
+ }
1346
+ var CatalogAmountSchema = z2.number().min(0).refine(
1347
+ (value) => catalogAmountDecimalPlaces(value) <= MAX_CATALOG_AMOUNT_DECIMAL_PLACES,
1348
+ {
1349
+ message: `Amount must have at most ${MAX_CATALOG_AMOUNT_DECIMAL_PLACES} decimal places; the store currency may allow fewer.`
1350
+ }
1351
+ );
1352
+ var VariantMarketPriceInputSchema = z2.object({
1353
+ market: IdSchema,
1354
+ price: CatalogAmountSchema,
1355
+ compareAtPrice: CatalogAmountSchema.optional().nullable()
1356
+ });
1038
1357
  var VariantInputSchema = z2.object({
1039
1358
  id: IdSchema.optional(),
1040
1359
  optionValues: z2.union([
@@ -1046,8 +1365,8 @@ var VariantInputSchema = z2.object({
1046
1365
  ]).optional(),
1047
1366
  sku: z2.string().optional().nullable(),
1048
1367
  title: z2.string().optional().nullable(),
1049
- price: z2.number().min(0),
1050
- compareAtPrice: z2.number().min(0).optional().nullable(),
1368
+ price: CatalogAmountSchema,
1369
+ compareAtPrice: CatalogAmountSchema.optional().nullable(),
1051
1370
  stock: z2.number().int().min(0).optional(),
1052
1371
  isUnlimited: z2.boolean().optional(),
1053
1372
  inventoryPolicy: z2.enum(["deny", "continue"]).optional(),
@@ -1057,6 +1376,7 @@ var VariantInputSchema = z2.object({
1057
1376
  externalId: z2.string().optional().nullable(),
1058
1377
  isActive: z2.boolean().optional(),
1059
1378
  images: z2.array(IdSchema).optional(),
1379
+ marketPrices: z2.array(VariantMarketPriceInputSchema).optional(),
1060
1380
  thumbnail: RemovedLegacyMediaFieldSchema,
1061
1381
  featuredMediaItemId: RemovedLegacyMediaFieldSchema,
1062
1382
  metadata: z2.unknown().optional()
@@ -1109,260 +1429,1004 @@ var ProductUpsertSchema = ProductUpsertObjectSchema.superRefine(
1109
1429
  }
1110
1430
  );
1111
1431
 
1112
- // ../contracts/src/ecommerce/checkout.ts
1432
+ // ../../internal/contracts/src/ecommerce/cart.ts
1433
+ import { z as z4 } from "zod";
1434
+
1435
+ // ../../internal/contracts/src/ecommerce/commerce-common.ts
1113
1436
  import { z as z3 } from "zod";
1114
- var providerSlugSchema = z3.string().trim().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, "pgProvider must be lowercase slug");
1115
- var CheckoutShippingAddressSchema = z3.object({
1116
- recipientName: z3.string().min(1),
1117
- phone: z3.string().min(1),
1118
- postalCode: z3.string().min(1),
1119
- address: z3.string().min(1),
1120
- detailAddress: z3.string().min(1),
1121
- deliveryMessage: z3.string().optional()
1437
+ var currencyCodeSchema = z3.string().regex(
1438
+ /^[A-Z]{3}$/,
1439
+ "currency must be a three-letter uppercase ISO 4217 code"
1440
+ );
1441
+ var minorAmountSchema = z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
1442
+ var moneySchema = z3.object({
1443
+ amountMinor: minorAmountSchema,
1444
+ currency: currencyCodeSchema
1445
+ }).strict();
1446
+ var MAX_COMMERCE_EXTENSION_ENTRIES = 50;
1447
+ var MAX_COMMERCE_EXTENSION_KEY_LENGTH = 40;
1448
+ var MAX_COMMERCE_EXTENSION_VALUE_LENGTH = 500;
1449
+ var MAX_COMMERCE_RESOURCE_ID_LENGTH = 255;
1450
+ var MAX_COMMERCE_CAPABILITY_LENGTH = 512;
1451
+ var MAX_COMMERCE_CUSTOMER_TOKEN_LENGTH = 4096;
1452
+ var MAX_COMMERCE_SHORT_TEXT_LENGTH = 200;
1453
+ var MAX_COMMERCE_ADDRESS_LENGTH = 500;
1454
+ var MAX_COMMERCE_EMAIL_LENGTH = 254;
1455
+ var MAX_COMMERCE_PHONE_LENGTH = 32;
1456
+ var MAX_COMMERCE_POSTAL_CODE_LENGTH = 32;
1457
+ var commerceResourceIdSchema = z3.string().min(1).max(MAX_COMMERCE_RESOURCE_ID_LENGTH);
1458
+ var commerceCapabilitySchema = z3.string().min(1).max(MAX_COMMERCE_CAPABILITY_LENGTH);
1459
+ var commerceCustomerTokenSchema = z3.string().min(1).max(MAX_COMMERCE_CUSTOMER_TOKEN_LENGTH);
1460
+ var publicAttributeKeySchema = z3.string().min(1).max(MAX_COMMERCE_EXTENSION_KEY_LENGTH).refine((key) => !key.startsWith("__"), {
1461
+ message: "attribute key uses a reserved platform prefix"
1462
+ });
1463
+ var publicAttributeSchema = z3.object({
1464
+ key: publicAttributeKeySchema,
1465
+ value: z3.string().max(MAX_COMMERCE_EXTENSION_VALUE_LENGTH)
1466
+ }).strict();
1467
+ var publicAttributesSchema = z3.array(publicAttributeSchema).max(MAX_COMMERCE_EXTENSION_ENTRIES).superRefine((attributes, ctx) => {
1468
+ const seen = /* @__PURE__ */ new Set();
1469
+ attributes.forEach(({ key }, index) => {
1470
+ if (seen.has(key)) {
1471
+ ctx.addIssue({
1472
+ code: "custom",
1473
+ path: [index, "key"],
1474
+ message: "attribute keys must be unique"
1475
+ });
1476
+ }
1477
+ seen.add(key);
1478
+ });
1479
+ });
1480
+ var integrationMetadataKeySchema = z3.string().min(1).max(MAX_COMMERCE_EXTENSION_KEY_LENGTH).refine((key) => !key.includes("[") && !key.includes("]"), {
1481
+ message: "integration metadata keys must not use bracket notation"
1482
+ });
1483
+ var integrationMetadataSchema = z3.record(
1484
+ integrationMetadataKeySchema,
1485
+ z3.string().max(MAX_COMMERCE_EXTENSION_VALUE_LENGTH)
1486
+ ).superRefine((metadata, ctx) => {
1487
+ if (Object.keys(metadata).length > MAX_COMMERCE_EXTENSION_ENTRIES) {
1488
+ ctx.addIssue({
1489
+ code: "too_big",
1490
+ origin: "object",
1491
+ maximum: MAX_COMMERCE_EXTENSION_ENTRIES,
1492
+ inclusive: true,
1493
+ message: `integration metadata cannot contain more than ${MAX_COMMERCE_EXTENSION_ENTRIES} entries`
1494
+ });
1495
+ }
1496
+ });
1497
+ var customerSnapshotSchema = z3.object({
1498
+ email: z3.string().email().max(MAX_COMMERCE_EMAIL_LENGTH),
1499
+ name: z3.string().min(1).max(MAX_COMMERCE_SHORT_TEXT_LENGTH).optional(),
1500
+ phone: z3.string().min(1).max(MAX_COMMERCE_PHONE_LENGTH).optional()
1501
+ }).strict();
1502
+ var checkoutShippingAddressSchema = z3.object({
1503
+ recipientName: z3.string().min(1).max(MAX_COMMERCE_SHORT_TEXT_LENGTH),
1504
+ phone: z3.string().min(1).max(MAX_COMMERCE_PHONE_LENGTH),
1505
+ countryCode: z3.string().trim().length(2).optional(),
1506
+ regionCode: z3.string().trim().min(1).max(64).optional(),
1507
+ postalCode: z3.string().min(1).max(MAX_COMMERCE_POSTAL_CODE_LENGTH),
1508
+ address: z3.string().min(1).max(MAX_COMMERCE_ADDRESS_LENGTH),
1509
+ detailAddress: z3.string().min(1).max(MAX_COMMERCE_ADDRESS_LENGTH),
1510
+ deliveryMessage: z3.string().max(MAX_COMMERCE_EXTENSION_VALUE_LENGTH).optional()
1511
+ }).strict();
1512
+ var commerceIdempotencyKeySchema = z3.string().max(255).refine(
1513
+ (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
1514
+ value
1515
+ ) || /^ik_[A-Za-z0-9_-]{22}$/.test(value),
1516
+ "idempotencyKey must be a UUIDv4 or ik_-prefixed 128-bit base64url value"
1517
+ );
1518
+
1519
+ // ../../internal/contracts/src/ecommerce/cart.ts
1520
+ var MAX_CART_LINE_QUANTITY = 99;
1521
+ var MAX_CART_DISCOUNT_CODES = 250;
1522
+ var MAX_CART_DISCOUNT_CODE_LENGTH = 128;
1523
+ var cartDiscountCodeSchema = z4.object({
1524
+ code: z4.string().min(1).max(MAX_CART_DISCOUNT_CODE_LENGTH),
1525
+ applicable: z4.boolean()
1526
+ }).strict();
1527
+ var CART_WARNING_CODES = [
1528
+ "DISCOUNT_CODE_NOT_HONOURED",
1529
+ "DISCOUNT_CURRENTLY_INACTIVE",
1530
+ "DISCOUNT_CUSTOMER_NOT_ELIGIBLE",
1531
+ "DISCOUNT_CUSTOMER_USAGE_LIMIT_REACHED",
1532
+ "DISCOUNT_ELIGIBLE_CUSTOMER_MISSING",
1533
+ "DISCOUNT_NO_ENTITLED_LINE_ITEMS",
1534
+ "DISCOUNT_NO_ENTITLED_SHIPPING_LINES",
1535
+ "DISCOUNT_NOT_FOUND",
1536
+ "DISCOUNT_PURCHASE_NOT_IN_RANGE",
1537
+ "DISCOUNT_USAGE_LIMIT_REACHED",
1538
+ "MERCHANDISE_NOT_ENOUGH_STOCK",
1539
+ "MERCHANDISE_OUT_OF_STOCK"
1540
+ ];
1541
+ var cartWarningCodeSchema = z4.enum(CART_WARNING_CODES);
1542
+ var cartWarningSchema = z4.object({
1543
+ code: cartWarningCodeSchema,
1544
+ message: z4.string().min(1),
1545
+ target: z4.string().min(1)
1546
+ }).strict();
1547
+ var updateDiscountCodesBodySchema = z4.object({
1548
+ cartToken: commerceCapabilitySchema,
1549
+ expectedRevision: z4.number().int().nonnegative(),
1550
+ discountCodes: z4.array(z4.string().trim().min(1).max(MAX_CART_DISCOUNT_CODE_LENGTH)).max(
1551
+ MAX_CART_DISCOUNT_CODES,
1552
+ `discountCodes cannot contain more than ${MAX_CART_DISCOUNT_CODES} values`
1553
+ )
1554
+ }).strict();
1555
+ var updateDiscountCodesParamsSchema = updateDiscountCodesBodySchema.extend({
1556
+ idempotencyKey: commerceIdempotencyKeySchema
1557
+ });
1558
+ var MAX_CART_LINE_ADMISSIONS = 32;
1559
+ var publicMediaSizeSchema = z4.object({
1560
+ width: z4.number().nullable().optional(),
1561
+ height: z4.number().nullable().optional(),
1562
+ mimeType: z4.string().nullable().optional(),
1563
+ filename: z4.string().nullable().optional(),
1564
+ url: z4.string().nullable().optional()
1565
+ }).strict();
1566
+ var publicMediaAssetSchema = z4.union([
1567
+ z4.string(),
1568
+ z4.object({
1569
+ id: z4.union([z4.string(), z4.number()]).nullable().optional(),
1570
+ alt: z4.string().nullable().optional(),
1571
+ width: z4.number().nullable().optional(),
1572
+ height: z4.number().nullable().optional(),
1573
+ mimeType: z4.string().nullable().optional(),
1574
+ filename: z4.string().nullable().optional(),
1575
+ url: z4.string().nullable().optional(),
1576
+ sizes: z4.record(z4.string(), publicMediaSizeSchema).optional()
1577
+ }).strict()
1578
+ ]);
1579
+ var publicCartReferenceSchema = z4.object({
1580
+ id: z4.string().min(1),
1581
+ title: z4.string().min(1),
1582
+ handle: z4.string().min(1).nullable(),
1583
+ image: publicMediaAssetSchema.nullable()
1584
+ }).strict();
1585
+ var publicCartMarketSchema = z4.object({
1586
+ id: z4.string().min(1),
1587
+ handle: z4.string().min(1),
1588
+ name: z4.string().min(1)
1589
+ }).strict();
1590
+ var publicCartItemAdmissionSchema = z4.object({
1591
+ eventId: z4.string().min(1),
1592
+ occurrenceId: z4.string().min(1),
1593
+ admissionTypeId: z4.string().min(1)
1594
+ }).strict();
1595
+ var cartLineAdmissionsInputSchema = z4.array(publicCartItemAdmissionSchema).max(
1596
+ MAX_CART_LINE_ADMISSIONS,
1597
+ `admissions cannot contain more than ${MAX_CART_LINE_ADMISSIONS} values`
1598
+ ).min(1, "admissions must contain at least one value").superRefine((admissions, ctx) => {
1599
+ const seen = /* @__PURE__ */ new Set();
1600
+ admissions.forEach((admission, index) => {
1601
+ const key = `${admission.eventId}:${admission.occurrenceId}:${admission.admissionTypeId}`;
1602
+ if (seen.has(key)) {
1603
+ ctx.addIssue({
1604
+ code: "custom",
1605
+ path: [index],
1606
+ message: "admissions must be unique"
1607
+ });
1608
+ }
1609
+ seen.add(key);
1610
+ });
1611
+ });
1612
+ var cartTokenField = z4.string().min(1, "cartToken is required");
1613
+ var cartMarketField = z4.string().trim().min(1).optional();
1614
+ var cartEntityIdField = z4.union([z4.string(), z4.number()]).transform(String);
1615
+ var createCartSchema = z4.object({
1616
+ market: cartMarketField,
1617
+ attributes: publicAttributesSchema.optional()
1618
+ }).strict();
1619
+ var getCartSchema = z4.object({ cartToken: cartTokenField }).strict();
1620
+ var addCartItemSchema = z4.object({
1621
+ cartToken: cartTokenField,
1622
+ product: cartEntityIdField,
1623
+ variant: cartEntityIdField,
1624
+ option: cartEntityIdField.optional(),
1625
+ quantity: z4.number().int().positive("quantity must be a positive integer"),
1626
+ market: cartMarketField,
1627
+ admissions: cartLineAdmissionsInputSchema.optional()
1628
+ }).strict();
1629
+ var updateCartItemSchema = z4.object({
1630
+ cartToken: cartTokenField,
1631
+ cartItemId: cartEntityIdField,
1632
+ quantity: z4.number().int().positive("quantity must be a positive integer")
1633
+ }).strict();
1634
+ var removeCartItemSchema = z4.object({
1635
+ cartToken: cartTokenField,
1636
+ cartItemId: cartEntityIdField
1637
+ }).strict();
1638
+ var updateCartAttributesSchema = z4.object({
1639
+ cartToken: cartTokenField,
1640
+ attributes: publicAttributesSchema
1641
+ }).strict();
1642
+ var clearCartSchema = z4.object({ cartToken: cartTokenField }).strict();
1643
+ function compareAdmissions(left, right) {
1644
+ for (const field of ["eventId", "occurrenceId", "admissionTypeId"]) {
1645
+ if (left[field] < right[field]) return -1;
1646
+ if (left[field] > right[field]) return 1;
1647
+ }
1648
+ return 0;
1649
+ }
1650
+ var publicCartItemAdmissionsSchema = z4.array(publicCartItemAdmissionSchema).max(MAX_CART_LINE_ADMISSIONS).superRefine((admissions, ctx) => {
1651
+ let previous;
1652
+ admissions.forEach((admission, index) => {
1653
+ const comparison = previous ? compareAdmissions(admission, previous) : void 0;
1654
+ if (comparison !== void 0 && comparison <= 0) {
1655
+ ctx.addIssue({
1656
+ code: "custom",
1657
+ path: [index],
1658
+ message: comparison === 0 ? "admissions must be unique" : "admissions must be in canonical order"
1659
+ });
1660
+ }
1661
+ previous = admission;
1662
+ });
1663
+ });
1664
+ var publicCartItemSchema = z4.object({
1665
+ id: z4.string().min(1),
1666
+ product: publicCartReferenceSchema,
1667
+ variant: publicCartReferenceSchema,
1668
+ admissions: publicCartItemAdmissionsSchema,
1669
+ quantity: z4.number().int().positive().max(MAX_CART_LINE_QUANTITY),
1670
+ unitPriceMinor: minorAmountSchema,
1671
+ discountMinor: minorAmountSchema,
1672
+ estimatedLineTotalMinor: minorAmountSchema,
1673
+ requiresShipping: z4.boolean()
1674
+ }).strict().superRefine((item, ctx) => {
1675
+ const expectedLineTotal = BigInt(item.unitPriceMinor) * BigInt(item.quantity) - BigInt(item.discountMinor);
1676
+ if (expectedLineTotal < 0n) {
1677
+ ctx.addIssue({
1678
+ code: "custom",
1679
+ path: ["discountMinor"],
1680
+ message: "discountMinor cannot exceed the undiscounted line total"
1681
+ });
1682
+ return;
1683
+ }
1684
+ if (BigInt(item.estimatedLineTotalMinor) !== expectedLineTotal) {
1685
+ ctx.addIssue({
1686
+ code: "custom",
1687
+ path: ["estimatedLineTotalMinor"],
1688
+ message: "estimatedLineTotalMinor must equal unitPriceMinor * quantity - discountMinor"
1689
+ });
1690
+ }
1122
1691
  });
1123
- var CheckoutCustomerSnapshotSchema = z3.object({
1124
- name: z3.string().optional(),
1125
- email: z3.string().email("Invalid email format"),
1126
- phone: z3.string().optional()
1692
+ var publicCartSchema = z4.object({
1693
+ id: z4.string().min(1),
1694
+ status: z4.enum(["active", "completed", "abandoned"]),
1695
+ revision: z4.number().int().nonnegative(),
1696
+ currency: currencyCodeSchema,
1697
+ market: publicCartMarketSchema,
1698
+ subtotalMinor: minorAmountSchema,
1699
+ discountMinor: minorAmountSchema,
1700
+ estimatedTotalMinor: minorAmountSchema,
1701
+ discountCodes: z4.array(cartDiscountCodeSchema),
1702
+ attributes: publicAttributesSchema,
1703
+ items: z4.array(publicCartItemSchema),
1704
+ expiresAt: z4.string().datetime({ offset: true }),
1705
+ createdAt: z4.string().datetime({ offset: true }),
1706
+ updatedAt: z4.string().datetime({ offset: true })
1707
+ }).strict().superRefine((cart, ctx) => {
1708
+ const expectedSubtotal = cart.items.reduce(
1709
+ (total, item) => total + BigInt(item.unitPriceMinor) * BigInt(item.quantity),
1710
+ 0n
1711
+ );
1712
+ const expectedTotal = expectedSubtotal - BigInt(cart.discountMinor);
1713
+ const expectedDiscount = cart.items.reduce(
1714
+ (total, item) => total + BigInt(item.discountMinor),
1715
+ 0n
1716
+ );
1717
+ if (BigInt(cart.subtotalMinor) !== expectedSubtotal) {
1718
+ ctx.addIssue({
1719
+ code: "custom",
1720
+ path: ["subtotalMinor"],
1721
+ message: "subtotalMinor must equal the sum of undiscounted cart line totals"
1722
+ });
1723
+ }
1724
+ if (BigInt(cart.discountMinor) !== expectedDiscount) {
1725
+ ctx.addIssue({
1726
+ code: "custom",
1727
+ path: ["discountMinor"],
1728
+ message: "discountMinor must equal the sum of cart line discounts"
1729
+ });
1730
+ }
1731
+ if (expectedTotal < 0n) {
1732
+ ctx.addIssue({
1733
+ code: "custom",
1734
+ path: ["discountMinor"],
1735
+ message: "discountMinor cannot exceed subtotalMinor"
1736
+ });
1737
+ } else if (BigInt(cart.estimatedTotalMinor) !== expectedTotal) {
1738
+ ctx.addIssue({
1739
+ code: "custom",
1740
+ path: ["estimatedTotalMinor"],
1741
+ message: "estimatedTotalMinor must equal subtotalMinor - discountMinor"
1742
+ });
1743
+ }
1127
1744
  });
1128
- var CheckoutRequestSchema = z3.object({
1129
- cartId: z3.union([z3.string(), z3.number()]).transform(String),
1130
- cartToken: z3.string().min(1).optional(),
1131
- pgPaymentId: z3.string().min(1).optional(),
1132
- pgProvider: providerSlugSchema.optional(),
1133
- orderNumber: z3.string().min(1, "orderNumber is required"),
1134
- customerSnapshot: CheckoutCustomerSnapshotSchema,
1135
- shippingAddress: CheckoutShippingAddressSchema.optional(),
1136
- discountCode: z3.string().optional()
1137
- }).superRefine((data, ctx) => {
1138
- if (data.pgPaymentId && !data.pgProvider) {
1745
+ var cartMutationResultSchema = z4.object({
1746
+ cart: publicCartSchema,
1747
+ warnings: z4.array(cartWarningSchema)
1748
+ }).strict();
1749
+ var createCartParamsSchema = z4.object({
1750
+ market: commerceResourceIdSchema.optional(),
1751
+ attributes: publicAttributesSchema.optional()
1752
+ }).strict();
1753
+ var createCartResultSchema = z4.object({
1754
+ cart: publicCartSchema,
1755
+ warnings: z4.array(cartWarningSchema),
1756
+ cartToken: commerceCapabilitySchema
1757
+ }).strict().superRefine((result, ctx) => {
1758
+ if (result.cart.revision !== 0) {
1139
1759
  ctx.addIssue({
1140
- code: z3.ZodIssueCode.custom,
1141
- path: ["pgProvider"],
1142
- message: "pgProvider is required when pgPaymentId is provided"
1760
+ code: "custom",
1761
+ path: ["cart", "revision"],
1762
+ message: "a newly created Cart must start at revision 0"
1143
1763
  });
1144
1764
  }
1145
- if (data.pgProvider && !data.pgPaymentId) {
1765
+ });
1766
+ var retrieveCartParamsSchema = z4.object({ cartToken: commerceCapabilitySchema }).strict();
1767
+ var addItemAdmissionsSchema = z4.tuple([publicCartItemAdmissionSchema], publicCartItemAdmissionSchema).readonly().superRefine((admissions, ctx) => {
1768
+ if (admissions.length > MAX_CART_LINE_ADMISSIONS) {
1146
1769
  ctx.addIssue({
1147
- code: z3.ZodIssueCode.custom,
1148
- path: ["pgPaymentId"],
1149
- message: "pgPaymentId is required when pgProvider is provided"
1770
+ code: "too_big",
1771
+ origin: "array",
1772
+ maximum: MAX_CART_LINE_ADMISSIONS,
1773
+ inclusive: true,
1774
+ message: `admissions cannot contain more than ${MAX_CART_LINE_ADMISSIONS} values`
1150
1775
  });
1151
1776
  }
1777
+ admissions.forEach((admission, index) => {
1778
+ if (index === 0) return;
1779
+ const comparison = compareAdmissions(admission, admissions[index - 1]);
1780
+ if (comparison <= 0) {
1781
+ ctx.addIssue({
1782
+ code: "custom",
1783
+ path: [index],
1784
+ message: comparison === 0 ? "admissions must be unique" : "admissions must be in canonical order"
1785
+ });
1786
+ }
1787
+ });
1788
+ });
1789
+ var addItemBodySchema = z4.object({
1790
+ cartToken: commerceCapabilitySchema,
1791
+ expectedRevision: z4.number().int().nonnegative(),
1792
+ productId: commerceResourceIdSchema,
1793
+ variantId: commerceResourceIdSchema,
1794
+ quantity: z4.number().int().positive().max(MAX_CART_LINE_QUANTITY),
1795
+ admissions: addItemAdmissionsSchema.optional()
1796
+ }).strict();
1797
+ var addItemParamsSchema = addItemBodySchema.extend({
1798
+ idempotencyKey: commerceIdempotencyKeySchema
1799
+ });
1800
+ var updateItemBodySchema = z4.object({
1801
+ cartToken: commerceCapabilitySchema,
1802
+ expectedRevision: z4.number().int().nonnegative(),
1803
+ cartItemId: commerceResourceIdSchema,
1804
+ quantity: z4.number().int().positive().max(MAX_CART_LINE_QUANTITY)
1805
+ }).strict();
1806
+ var updateItemParamsSchema = updateItemBodySchema.extend({
1807
+ idempotencyKey: commerceIdempotencyKeySchema
1808
+ });
1809
+ var removeItemBodySchema = z4.object({
1810
+ cartToken: commerceCapabilitySchema,
1811
+ expectedRevision: z4.number().int().nonnegative(),
1812
+ cartItemId: commerceResourceIdSchema
1813
+ }).strict();
1814
+ var removeItemParamsSchema = removeItemBodySchema.extend({
1815
+ idempotencyKey: commerceIdempotencyKeySchema
1816
+ });
1817
+ var clearCartBodySchema = z4.object({
1818
+ cartToken: commerceCapabilitySchema,
1819
+ expectedRevision: z4.number().int().nonnegative()
1820
+ }).strict();
1821
+ var clearCartParamsSchema = clearCartBodySchema.extend({
1822
+ idempotencyKey: commerceIdempotencyKeySchema
1823
+ });
1824
+ var updateCartAttributesBodySchema = z4.object({
1825
+ cartToken: commerceCapabilitySchema,
1826
+ expectedRevision: z4.number().int().nonnegative(),
1827
+ attributes: publicAttributesSchema
1828
+ }).strict();
1829
+ var updateCartAttributesParamsSchema = updateCartAttributesBodySchema.extend({
1830
+ idempotencyKey: commerceIdempotencyKeySchema
1152
1831
  });
1153
1832
 
1154
- // ../contracts/src/ecommerce/index.ts
1155
- var transactionStatusSchema = z4.enum([
1156
- "pending",
1157
- "paid",
1158
- "failed",
1159
- "canceled",
1160
- "refunded"
1161
- ]);
1162
- var financialStatusSchema = z4.enum([
1163
- "pending",
1164
- "paid",
1165
- "canceled",
1166
- "partially_refunded",
1167
- "refunded"
1168
- ]);
1169
- var orderDisplayFinancialStatusSchema = z4.enum([
1170
- "pending",
1171
- "paid",
1172
- "partially_refunded",
1173
- "refunded",
1174
- "voided"
1175
- ]);
1176
- var confirmationStatusSchema = z4.enum(["unconfirmed", "confirmed"]);
1177
- var fulfillmentOrderStatusSchema = z4.enum([
1178
- "open",
1179
- "in_progress",
1180
- "on_hold",
1181
- "canceled",
1182
- "closed"
1833
+ // ../../internal/contracts/src/ecommerce/checkout.ts
1834
+ import { z as z6 } from "zod";
1835
+
1836
+ // ../../internal/contracts/src/ecommerce/payment-session.ts
1837
+ import { z as z5 } from "zod";
1838
+ var tossProviderOrderIdSchema = z5.string().min(6).max(64).regex(
1839
+ /^[A-Za-z0-9_-]+$/,
1840
+ "providerOrderId may contain only ASCII letters, digits, underscores, and hyphens"
1841
+ );
1842
+ var accountAliasUuidV4Schema = z5.string().regex(
1843
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
1844
+ "providerCustomerKey must be ANONYMOUS or a UUIDv4 account alias"
1845
+ );
1846
+ var tossProviderCustomerKeySchema = z5.union([
1847
+ z5.literal("ANONYMOUS"),
1848
+ accountAliasUuidV4Schema
1183
1849
  ]);
1184
- var shipmentStatusSchema = z4.enum([
1185
- "pending",
1186
- "shipped",
1187
- "delivered",
1188
- "canceled",
1189
- "failed"
1850
+ var createPaymentSessionBaseBodyShape = {
1851
+ checkoutToken: commerceCapabilitySchema
1852
+ };
1853
+ var createTossPaymentSessionBodySchema = z5.object({
1854
+ ...createPaymentSessionBaseBodyShape,
1855
+ provider: z5.literal("toss"),
1856
+ providerOptions: z5.object({ profile: z5.literal("widget_v2") }).strict()
1857
+ }).strict();
1858
+ var createPaymentSessionParamsSchema = createTossPaymentSessionBodySchema.extend({
1859
+ idempotencyKey: commerceIdempotencyKeySchema
1860
+ });
1861
+ var retrieveBrowserPaymentSessionParamsSchema = z5.object({
1862
+ paymentSessionId: commerceResourceIdSchema,
1863
+ checkoutToken: commerceCapabilitySchema
1864
+ }).strict();
1865
+ var retrieveServerPaymentSessionParamsSchema = z5.object({ paymentSessionId: commerceResourceIdSchema }).strict();
1866
+ var cancelBrowserPaymentSessionBodySchema = z5.object({
1867
+ paymentSessionId: commerceResourceIdSchema,
1868
+ checkoutToken: commerceCapabilitySchema
1869
+ }).strict();
1870
+ var cancelBrowserPaymentSessionParamsSchema = cancelBrowserPaymentSessionBodySchema.extend({
1871
+ idempotencyKey: commerceIdempotencyKeySchema
1872
+ });
1873
+ var cancelServerPaymentSessionBodySchema = z5.object({
1874
+ paymentSessionId: commerceResourceIdSchema
1875
+ }).strict();
1876
+ var cancelServerPaymentSessionParamsSchema = cancelServerPaymentSessionBodySchema.extend({
1877
+ idempotencyKey: commerceIdempotencyKeySchema
1878
+ });
1879
+ var cancelPaymentSessionParamsSchema = z5.union([
1880
+ cancelBrowserPaymentSessionParamsSchema,
1881
+ cancelServerPaymentSessionParamsSchema
1190
1882
  ]);
1191
- var orderDisplayFulfillmentStatusSchema = z4.enum([
1192
- "unfulfilled",
1193
- "in_progress",
1194
- "on_hold",
1195
- "partially_fulfilled",
1196
- "shipped",
1197
- "fulfilled",
1883
+ var confirmTossPaymentSessionBodySchema = z5.object({
1884
+ paymentSessionId: commerceResourceIdSchema,
1885
+ action: z5.object({
1886
+ type: z5.literal("toss_authorization"),
1887
+ paymentKey: z5.string().min(1).max(512),
1888
+ providerOrderId: tossProviderOrderIdSchema
1889
+ }).strict()
1890
+ }).strict();
1891
+ var confirmTossPaymentSessionParamsSchema = confirmTossPaymentSessionBodySchema.extend({
1892
+ idempotencyKey: commerceIdempotencyKeySchema
1893
+ });
1894
+ var paymentNextActionSchema = z5.object({
1895
+ type: z5.literal("toss_widget"),
1896
+ clientKey: z5.string().min(1).max(255),
1897
+ paymentMethodVariantKey: z5.string().min(1).max(255),
1898
+ agreementVariantKey: z5.string().min(1).max(255),
1899
+ providerOrderId: tossProviderOrderIdSchema,
1900
+ providerCustomerKey: tossProviderCustomerKeySchema,
1901
+ orderName: z5.string().min(1).max(200),
1902
+ customerName: z5.string().min(1).max(200).optional(),
1903
+ customerEmail: z5.string().email().max(254).optional()
1904
+ }).strict();
1905
+ var PAYMENT_SESSION_ROLES = [
1906
+ "active",
1907
+ "canonical_candidate",
1908
+ "canonical",
1909
+ "residual",
1910
+ "historical"
1911
+ ];
1912
+ var paymentSessionRoleSchema = z5.enum(PAYMENT_SESSION_ROLES);
1913
+ var PAYMENT_PROVIDER_STATUSES = [
1914
+ "requires_payment_method",
1915
+ "requires_action",
1916
+ "requires_confirmation",
1917
+ "initializing",
1918
+ "processing",
1919
+ "authorized",
1920
+ "canceling",
1921
+ "unknown",
1922
+ "captured",
1923
+ "failed",
1198
1924
  "canceled"
1199
- ]);
1200
- var orderReturnStatusSchema = z4.enum([
1201
- "no_return",
1202
- "return_requested",
1203
- "in_progress",
1204
- "returned"
1205
- ]);
1206
- var orderStatusSchema = z4.enum([
1207
- "pending",
1208
- "paid",
1209
- "canceled",
1210
- "refunded",
1211
- "preparing",
1212
- "shipped",
1213
- "delivered",
1214
- "confirmed",
1215
- "return_requested",
1216
- "return_processing",
1217
- "returned"
1218
- ]);
1219
- var entityIdSchema = z4.union([z4.string().min(1), z4.number()]).transform(String);
1220
- var createOrderItemSchema = z4.object({
1221
- // `product` and `option` are storefront-optional: the create-order
1222
- // endpoint derives the product from the variant's parent and snapshots
1223
- // option selection from the variant.
1224
- product: entityIdSchema.optional(),
1225
- variant: entityIdSchema,
1226
- option: entityIdSchema.optional(),
1227
- quantity: z4.number().int().positive("quantity must be a positive integer"),
1228
- unitPrice: z4.number().optional(),
1229
- totalPrice: z4.number().optional()
1925
+ ];
1926
+ var paymentProviderStatusSchema = z5.enum(PAYMENT_PROVIDER_STATUSES);
1927
+ var PAYMENT_RECONCILIATION_REASONS = [
1928
+ "provider_outcome_unknown",
1929
+ "local_commit_ambiguous",
1930
+ "amount_mismatch",
1931
+ "currency_mismatch",
1932
+ "excess_capture_pending_order",
1933
+ "inventory_unavailable",
1934
+ "admission_unavailable",
1935
+ "excess_capture",
1936
+ "late_capture",
1937
+ "cross_checkout_duplicate"
1938
+ ];
1939
+ var paymentReconciliationReasonSchema = z5.enum(
1940
+ PAYMENT_RECONCILIATION_REASONS
1941
+ );
1942
+ function requiredReconciliationSchema(reasons) {
1943
+ const reasonSchema = z5.enum(reasons);
1944
+ return z5.object({
1945
+ status: z5.literal("required"),
1946
+ reasons: z5.array(reasonSchema).min(1).readonly()
1947
+ }).strict().superRefine((value, ctx) => {
1948
+ const indexes = new Map(reasons.map((reason, index) => [reason, index]));
1949
+ let previous = -1;
1950
+ value.reasons.forEach((reason, index) => {
1951
+ const current = indexes.get(reason) ?? -1;
1952
+ if (current <= previous) {
1953
+ ctx.addIssue({
1954
+ code: "custom",
1955
+ path: ["reasons", index],
1956
+ message: "reconciliation reasons must be unique and canonical-sorted"
1957
+ });
1958
+ }
1959
+ previous = current;
1960
+ });
1961
+ });
1962
+ }
1963
+ var noReconciliationSchema = z5.object({ status: z5.literal("none") }).strict();
1964
+ var publicCaptureEvidenceSchema = z5.object({
1965
+ amountMinor: z5.number().int().nonnegative(),
1966
+ currency: currencyCodeSchema,
1967
+ observedAt: z5.string().datetime({ offset: true })
1968
+ }).strict();
1969
+ var reconciliationRefundEvidenceSchema = z5.object({
1970
+ providerRefundId: z5.string().min(1),
1971
+ money: moneySchema,
1972
+ observedAt: z5.string().datetime({ offset: true })
1973
+ }).strict();
1974
+ var PAYMENT_FAILURE_REASONS = [
1975
+ "payment_declined",
1976
+ "authentication_failed",
1977
+ "authorization_expired",
1978
+ "payment_method_unavailable",
1979
+ "provider_terminal_failure",
1980
+ "unknown_provider_terminal_failure"
1981
+ ];
1982
+ var paymentFailureReasonSchema = z5.enum(PAYMENT_FAILURE_REASONS);
1983
+ var orderReferenceSchema = z5.object({
1984
+ id: z5.string().min(1),
1985
+ orderNumber: z5.string().min(1)
1986
+ }).strict();
1987
+ var paymentSessionBaseShape = {
1988
+ id: z5.string().min(1),
1989
+ money: moneySchema,
1990
+ expiresAt: z5.string().datetime({ offset: true }).nullable()
1991
+ };
1992
+ var suppressedNotReadyShape = {
1993
+ presentationStatus: z5.literal("suppressed"),
1994
+ finalizationStatus: z5.literal("not_ready"),
1995
+ reconciliation: noReconciliationSchema,
1996
+ order: z5.null()
1997
+ };
1998
+ function stateSchemas(nextAction) {
1999
+ return [
2000
+ z5.object({
2001
+ providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
2002
+ sessionRole: z5.literal("active"),
2003
+ presentationStatus: z5.literal("actionable"),
2004
+ finalizationStatus: z5.literal("not_ready"),
2005
+ nextAction,
2006
+ reconciliation: noReconciliationSchema,
2007
+ order: z5.null()
2008
+ }),
2009
+ z5.object({
2010
+ providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
2011
+ sessionRole: z5.literal("residual"),
2012
+ ...suppressedNotReadyShape
2013
+ }),
2014
+ z5.object({
2015
+ providerStatus: z5.literal("requires_confirmation"),
2016
+ sessionRole: z5.enum(["active", "residual"]),
2017
+ ...suppressedNotReadyShape
2018
+ }),
2019
+ z5.object({
2020
+ providerStatus: z5.enum(["initializing", "processing", "authorized"]),
2021
+ sessionRole: z5.enum(["active", "residual"]),
2022
+ ...suppressedNotReadyShape
2023
+ }),
2024
+ z5.object({
2025
+ providerStatus: z5.literal("canceling"),
2026
+ sessionRole: z5.enum(["active", "residual"]),
2027
+ ...suppressedNotReadyShape
2028
+ }),
2029
+ z5.object({
2030
+ providerStatus: z5.literal("unknown"),
2031
+ sessionRole: z5.enum(["active", "residual"]),
2032
+ presentationStatus: z5.literal("suppressed"),
2033
+ finalizationStatus: z5.literal("not_ready"),
2034
+ reconciliation: requiredReconciliationSchema([
2035
+ "provider_outcome_unknown"
2036
+ ]),
2037
+ order: z5.null()
2038
+ }),
2039
+ z5.object({
2040
+ providerStatus: z5.literal("captured"),
2041
+ sessionRole: z5.enum(["canonical_candidate", "residual"]),
2042
+ presentationStatus: z5.literal("suppressed"),
2043
+ finalizationStatus: z5.literal("pending"),
2044
+ reconciliation: noReconciliationSchema,
2045
+ providerEvidence: publicCaptureEvidenceSchema,
2046
+ order: z5.null()
2047
+ }),
2048
+ z5.object({
2049
+ providerStatus: z5.literal("captured"),
2050
+ sessionRole: z5.enum(["canonical", "historical"]),
2051
+ presentationStatus: z5.literal("suppressed"),
2052
+ finalizationStatus: z5.literal("committed"),
2053
+ reconciliation: noReconciliationSchema,
2054
+ providerEvidence: publicCaptureEvidenceSchema,
2055
+ order: orderReferenceSchema
2056
+ }),
2057
+ z5.object({
2058
+ providerStatus: z5.literal("captured"),
2059
+ sessionRole: z5.enum(["canonical_candidate", "residual"]),
2060
+ presentationStatus: z5.literal("suppressed"),
2061
+ finalizationStatus: z5.literal("pending"),
2062
+ reconciliation: requiredReconciliationSchema([
2063
+ "local_commit_ambiguous",
2064
+ "amount_mismatch",
2065
+ "currency_mismatch",
2066
+ "excess_capture_pending_order"
2067
+ ]),
2068
+ providerEvidence: publicCaptureEvidenceSchema,
2069
+ order: z5.null()
2070
+ }),
2071
+ z5.object({
2072
+ providerStatus: z5.literal("captured"),
2073
+ sessionRole: z5.enum(["canonical", "residual"]),
2074
+ presentationStatus: z5.literal("suppressed"),
2075
+ finalizationStatus: z5.literal("committed"),
2076
+ reconciliation: requiredReconciliationSchema([
2077
+ "inventory_unavailable",
2078
+ "admission_unavailable",
2079
+ "amount_mismatch",
2080
+ "currency_mismatch",
2081
+ "excess_capture",
2082
+ "late_capture",
2083
+ "cross_checkout_duplicate"
2084
+ ]),
2085
+ providerEvidence: publicCaptureEvidenceSchema,
2086
+ order: orderReferenceSchema
2087
+ }),
2088
+ z5.object({
2089
+ providerStatus: z5.literal("failed"),
2090
+ sessionRole: z5.literal("historical"),
2091
+ presentationStatus: z5.literal("suppressed"),
2092
+ finalizationStatus: z5.literal("not_ready"),
2093
+ reconciliation: noReconciliationSchema,
2094
+ failure: z5.object({
2095
+ reason: paymentFailureReasonSchema,
2096
+ message: z5.string().min(1).optional()
2097
+ }).strict(),
2098
+ order: z5.null()
2099
+ }),
2100
+ z5.object({
2101
+ providerStatus: z5.literal("canceled"),
2102
+ sessionRole: z5.literal("historical"),
2103
+ presentationStatus: z5.literal("suppressed"),
2104
+ finalizationStatus: z5.literal("not_ready"),
2105
+ cancellationReason: z5.enum([
2106
+ "expired",
2107
+ "customer",
2108
+ "operator",
2109
+ "provider"
2110
+ ]),
2111
+ reconciliation: noReconciliationSchema,
2112
+ order: z5.null()
2113
+ })
2114
+ ];
2115
+ }
2116
+ function providerSessionSchemas(action) {
2117
+ return stateSchemas(action).map(
2118
+ (state) => z5.object({
2119
+ ...paymentSessionBaseShape,
2120
+ provider: z5.literal("toss"),
2121
+ ...state.shape
2122
+ }).strict()
2123
+ );
2124
+ }
2125
+ var publicPaymentSessionVariants = [
2126
+ ...providerSessionSchemas(paymentNextActionSchema)
2127
+ ];
2128
+ var publicPaymentSessionSchema = z5.union(publicPaymentSessionVariants);
2129
+ var CreatePaymentSessionParamsSchema = createPaymentSessionParamsSchema;
2130
+ var CancelServerPaymentSessionParamsSchema = cancelServerPaymentSessionParamsSchema;
2131
+ var ConfirmTossPaymentSessionParamsSchema = confirmTossPaymentSessionParamsSchema;
2132
+
2133
+ // ../../internal/contracts/src/ecommerce/checkout.ts
2134
+ var canonicalPublicCheckoutItemSchema = z6.object({
2135
+ id: z6.string().min(1),
2136
+ product: publicCartReferenceSchema,
2137
+ variant: publicCartReferenceSchema,
2138
+ productTitle: z6.string().min(1),
2139
+ variantTitle: z6.string().min(1).nullable(),
2140
+ sku: z6.string().min(1).nullable(),
2141
+ admissions: publicCartItemAdmissionsSchema,
2142
+ quantity: z6.number().int().positive().max(MAX_CART_LINE_QUANTITY),
2143
+ unitPriceMinor: minorAmountSchema,
2144
+ discountMinor: minorAmountSchema,
2145
+ lineTotalMinor: minorAmountSchema,
2146
+ requiresShipping: z6.boolean()
2147
+ }).strict().superRefine((item, ctx) => {
2148
+ const expectedLineTotal = BigInt(item.unitPriceMinor) * BigInt(item.quantity) - BigInt(item.discountMinor);
2149
+ if (expectedLineTotal < 0n) {
2150
+ ctx.addIssue({
2151
+ code: "custom",
2152
+ path: ["discountMinor"],
2153
+ message: "discountMinor cannot exceed the undiscounted line total"
2154
+ });
2155
+ return;
2156
+ }
2157
+ if (BigInt(item.lineTotalMinor) !== expectedLineTotal) {
2158
+ ctx.addIssue({
2159
+ code: "custom",
2160
+ path: ["lineTotalMinor"],
2161
+ message: "lineTotalMinor must equal unitPriceMinor * quantity - discountMinor"
2162
+ });
2163
+ }
2164
+ });
2165
+ var checkoutAmountsSchema = z6.object({
2166
+ currency: currencyCodeSchema,
2167
+ subtotalMinor: minorAmountSchema,
2168
+ shippingMinor: minorAmountSchema,
2169
+ discountMinor: minorAmountSchema,
2170
+ totalMinor: minorAmountSchema
2171
+ }).strict().superRefine((amounts, ctx) => {
2172
+ const expectedTotal = BigInt(amounts.subtotalMinor) + BigInt(amounts.shippingMinor) - BigInt(amounts.discountMinor);
2173
+ if (BigInt(amounts.totalMinor) !== expectedTotal) {
2174
+ ctx.addIssue({
2175
+ code: "custom",
2176
+ path: ["totalMinor"],
2177
+ message: "totalMinor must equal subtotalMinor + shippingMinor - discountMinor"
2178
+ });
2179
+ }
2180
+ });
2181
+ var checkoutPaymentSessionRefSchema = z6.object({
2182
+ id: z6.string().min(1),
2183
+ provider: z6.literal("toss"),
2184
+ providerStatus: paymentProviderStatusSchema
2185
+ }).strict();
2186
+ var openCheckoutPaymentStateSchema = z6.object({
2187
+ activePaymentSession: checkoutPaymentSessionRefSchema.nullable()
2188
+ }).strict();
2189
+ var spentCheckoutPaymentStateSchema = z6.object({
2190
+ activePaymentSession: z6.null()
2191
+ }).strict();
2192
+ var lightweightOrderReferenceSchema = z6.object({
2193
+ id: z6.string().min(1),
2194
+ orderNumber: z6.string().min(1)
1230
2195
  }).strict();
1231
- var providerSlugSchema2 = z4.string().trim().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, "pgProvider must be lowercase slug");
1232
- var createOrderSchema = z4.object({
1233
- pgPaymentId: z4.string().min(1).optional(),
1234
- pgProvider: providerSlugSchema2.optional(),
1235
- orderNumber: z4.string().min(1, "orderNumber is required"),
1236
- customer: entityIdSchema.optional(),
1237
- customerSnapshot: z4.object({
1238
- name: z4.string().optional(),
1239
- email: z4.string().email("Invalid email format"),
1240
- phone: z4.string().optional()
2196
+ var publicCheckoutBaseShape = {
2197
+ id: z6.string().min(1),
2198
+ amounts: checkoutAmountsSchema,
2199
+ items: z6.array(canonicalPublicCheckoutItemSchema).min(1),
2200
+ customerSnapshot: customerSnapshotSchema,
2201
+ shippingAddress: checkoutShippingAddressSchema.nullable(),
2202
+ attributes: publicAttributesSchema,
2203
+ expiresAt: z6.string().datetime({ offset: true }),
2204
+ createdAt: z6.string().datetime({ offset: true }),
2205
+ updatedAt: z6.string().datetime({ offset: true })
2206
+ };
2207
+ var abandonedReasonSchema = z6.enum(["expired", "customer", "operator"]);
2208
+ var publicCheckoutVariants = [
2209
+ z6.object({
2210
+ ...publicCheckoutBaseShape,
2211
+ status: z6.literal("open"),
2212
+ paymentState: openCheckoutPaymentStateSchema,
2213
+ order: z6.null()
2214
+ }).strict(),
2215
+ z6.object({
2216
+ ...publicCheckoutBaseShape,
2217
+ status: z6.literal("placed"),
2218
+ placedAt: z6.string().datetime({ offset: true }),
2219
+ paymentState: spentCheckoutPaymentStateSchema,
2220
+ order: lightweightOrderReferenceSchema
1241
2221
  }).strict(),
1242
- shippingAddress: z4.object({
1243
- postalCode: z4.string().optional(),
1244
- address: z4.string().optional(),
1245
- detailAddress: z4.string().optional(),
1246
- deliveryMessage: z4.string().optional(),
1247
- recipientName: z4.string().optional(),
1248
- phone: z4.string().optional()
1249
- }).strict().optional(),
1250
- orderItems: z4.array(createOrderItemSchema).min(1, "At least one order item is required").max(100, "Maximum 100 items per order"),
1251
- totalAmount: z4.number().nonnegative("totalAmount must be non-negative"),
1252
- shippingAmount: z4.number().min(0).optional(),
1253
- discountCode: z4.string().optional()
1254
- }).strict().superRefine((data, ctx) => {
1255
- if (data.pgPaymentId && !data.pgProvider) {
2222
+ z6.object({
2223
+ ...publicCheckoutBaseShape,
2224
+ status: z6.literal("placed"),
2225
+ placedAt: z6.string().datetime({ offset: true }),
2226
+ placedVia: z6.literal("late_capture"),
2227
+ abandonedAt: z6.string().datetime({ offset: true }),
2228
+ abandonedReason: abandonedReasonSchema,
2229
+ paymentState: spentCheckoutPaymentStateSchema,
2230
+ order: lightweightOrderReferenceSchema
2231
+ }).strict(),
2232
+ z6.object({
2233
+ ...publicCheckoutBaseShape,
2234
+ status: z6.literal("abandoned"),
2235
+ abandonedAt: z6.string().datetime({ offset: true }),
2236
+ abandonedReason: abandonedReasonSchema,
2237
+ paymentState: z6.object({
2238
+ activePaymentSession: z6.null()
2239
+ }).strict(),
2240
+ order: z6.null()
2241
+ }).strict()
2242
+ ];
2243
+ function validateCheckoutSnapshot(input, ctx) {
2244
+ const checkout = input;
2245
+ const expectedSubtotal = checkout.items.reduce(
2246
+ (total, item) => total + BigInt(item.unitPriceMinor) * BigInt(item.quantity),
2247
+ 0n
2248
+ );
2249
+ const expectedDiscount = checkout.items.reduce(
2250
+ (total, item) => total + BigInt(item.discountMinor),
2251
+ 0n
2252
+ );
2253
+ if (BigInt(checkout.amounts.subtotalMinor) !== expectedSubtotal) {
1256
2254
  ctx.addIssue({
1257
- code: z4.ZodIssueCode.custom,
1258
- path: ["pgProvider"],
1259
- message: "pgProvider is required when pgPaymentId is provided"
2255
+ code: "custom",
2256
+ path: ["amounts", "subtotalMinor"],
2257
+ message: "subtotalMinor must equal the sum of undiscounted checkout line totals"
1260
2258
  });
1261
2259
  }
1262
- if (data.pgProvider && !data.pgPaymentId) {
2260
+ if (BigInt(checkout.amounts.discountMinor) !== expectedDiscount) {
1263
2261
  ctx.addIssue({
1264
- code: z4.ZodIssueCode.custom,
1265
- path: ["pgPaymentId"],
1266
- message: "pgPaymentId is required when pgProvider is provided"
2262
+ code: "custom",
2263
+ path: ["amounts", "discountMinor"],
2264
+ message: "discountMinor must equal the sum of checkout line discounts"
1267
2265
  });
1268
2266
  }
2267
+ }
2268
+ var publicCheckoutSchema = z6.union(publicCheckoutVariants).superRefine(validateCheckoutSnapshot);
2269
+ var serverCheckoutExtraShape = {
2270
+ clientReferenceId: commerceResourceIdSchema.nullable(),
2271
+ integrationMetadata: integrationMetadataSchema
2272
+ };
2273
+ var serverCheckoutVariants = publicCheckoutVariants.map(
2274
+ (variant) => variant.extend(serverCheckoutExtraShape)
2275
+ );
2276
+ var serverCheckoutSchema = z6.union(serverCheckoutVariants).superRefine(validateCheckoutSnapshot);
2277
+ var retrieveCheckoutParamsSchema = z6.object({ checkoutToken: commerceCapabilitySchema }).strict();
2278
+ var retrieveServerCheckoutParamsSchema = z6.object({ checkoutId: commerceResourceIdSchema }).strict();
2279
+ var createCheckoutBodySchema = z6.object({
2280
+ cartToken: commerceCapabilitySchema,
2281
+ customer: customerSnapshotSchema,
2282
+ shippingAddress: checkoutShippingAddressSchema.optional()
2283
+ }).strict();
2284
+ var createCheckoutParamsSchema = createCheckoutBodySchema.extend({
2285
+ idempotencyKey: commerceIdempotencyKeySchema
1269
2286
  });
1270
- var updateTransactionSchema = z4.object({
1271
- pgPaymentId: z4.string().min(1, "pgPaymentId is required").describe("PG payment ID (required)"),
1272
- status: transactionStatusSchema.describe(
1273
- "New transaction status (required)"
1274
- ),
1275
- paymentMethod: z4.string().optional().describe("Payment method (optional)"),
1276
- receiptUrl: z4.string().optional().describe("Receipt URL (optional)"),
1277
- paymentKey: z4.string().min(1).optional().describe("Provider payment key for verified paid confirmation"),
1278
- amount: z4.number().int().positive().optional().describe("Provider-confirmed amount for verified paid confirmation")
2287
+ var createServerCheckoutBodySchema = createCheckoutBodySchema.safeExtend({
2288
+ clientReferenceId: commerceResourceIdSchema.optional(),
2289
+ integrationMetadata: integrationMetadataSchema.optional()
1279
2290
  }).strict();
1280
- var confirmPaymentSchema = z4.object({
1281
- orderNumber: z4.string().min(1).optional(),
1282
- pgPaymentId: z4.string().min(1, "pgPaymentId is required").describe("Provider payment identifier stored on the transaction"),
1283
- pgProvider: providerSlugSchema2.describe(
1284
- "Payment provider slug, e.g. toss, portone, stripe"
1285
- ),
1286
- pgOrderId: z4.string().min(1).optional(),
1287
- amount: z4.number().int().nonnegative("amount must be non-negative").describe("Provider-confirmed amount in minor units"),
1288
- currency: z4.string().min(1).optional(),
1289
- paymentMethod: z4.string().optional(),
1290
- receiptUrl: z4.string().url().optional(),
1291
- approvedAt: z4.string().optional(),
1292
- providerStatus: z4.string().optional(),
1293
- providerEventId: z4.string().min(1).optional(),
1294
- confirmationSource: z4.enum([
1295
- "provider_webhook",
1296
- "provider_lookup",
1297
- "provider_api_confirm",
1298
- "manual_server"
1299
- ]).optional(),
1300
- paymentKey: z4.string().min(1).optional().describe(
1301
- "Optional provider payment key from the client confirm handshake; stored for BFF/provider refund workflows (also accepted as metadata.tossPaymentKey). Local cancel does not read this field."
1302
- ),
1303
- metadata: z4.record(z4.string(), z4.unknown()).optional()
2291
+ var createServerCheckoutParamsSchema = createServerCheckoutBodySchema.extend({
2292
+ idempotencyKey: commerceIdempotencyKeySchema
2293
+ });
2294
+ var createCheckoutResultSchema = z6.object({
2295
+ checkout: publicCheckoutSchema,
2296
+ checkoutToken: commerceCapabilitySchema
2297
+ }).strict();
2298
+ var createServerCheckoutResultSchema = z6.object({
2299
+ checkout: serverCheckoutSchema,
2300
+ checkoutToken: commerceCapabilitySchema
1304
2301
  }).strict();
1305
- var confirmPaymentResponseSchema = z4.object({
1306
- orderId: z4.string().min(1),
1307
- transactionId: z4.string().min(1),
1308
- status: z4.literal("paid"),
1309
- alreadyConfirmed: z4.boolean().optional()
2302
+ var canonicalCheckoutQuerySchema = z6.object({
2303
+ limit: z6.number().int().min(1).max(100).default(20),
2304
+ cursor: z6.string().min(1).max(512).optional(),
2305
+ status: z6.array(z6.enum(["open", "placed", "abandoned"])).optional(),
2306
+ createdAtFrom: z6.string().datetime({ offset: true }).optional(),
2307
+ createdAtTo: z6.string().datetime({ offset: true }).optional(),
2308
+ clientReferenceId: commerceResourceIdSchema.optional()
1310
2309
  }).strict();
2310
+ var checkoutListItemVariants = publicCheckoutVariants.map(
2311
+ (variant) => variant.omit({
2312
+ items: true,
2313
+ shippingAddress: true,
2314
+ attributes: true
2315
+ }).extend({ clientReferenceId: commerceResourceIdSchema.nullable() })
2316
+ );
2317
+ var checkoutListItemSchema = z6.union(checkoutListItemVariants);
2318
+ var canonicalCheckoutQueryResultSchema = z6.object({
2319
+ data: z6.array(checkoutListItemSchema),
2320
+ hasMore: z6.boolean(),
2321
+ nextCursor: z6.string().min(1).max(512).nullable()
2322
+ }).strict();
2323
+ var CreateServerCheckoutParamsSchema = createServerCheckoutParamsSchema;
2324
+ var CanonicalCheckoutQuerySchema = canonicalCheckoutQuerySchema;
2325
+
2326
+ // ../../internal/contracts/src/ecommerce/index.ts
2327
+ var transactionStatusSchema = z7.enum([
2328
+ "pending",
2329
+ "paid",
2330
+ "success",
2331
+ "failed",
2332
+ "error",
2333
+ "canceled",
2334
+ "refunded"
2335
+ ]);
2336
+ var transactionKindSchema = z7.enum([
2337
+ "authorization",
2338
+ "sale",
2339
+ "capture",
2340
+ "void",
2341
+ "refund"
2342
+ ]);
1311
2343
  var RETURN_REASON_CODES = [
1312
2344
  "change_of_mind",
1313
2345
  "defective",
1314
2346
  "wrong_delivery",
1315
2347
  "damaged",
2348
+ "size_too_large",
2349
+ "size_too_small",
2350
+ "color",
2351
+ "style",
2352
+ "not_as_described",
2353
+ "wrong_item",
2354
+ "unknown",
1316
2355
  "other"
1317
2356
  ];
1318
- var returnReasonSchema = z4.enum(RETURN_REASON_CODES);
2357
+ var returnReasonSchema = z7.enum(RETURN_REASON_CODES);
1319
2358
  var RETURN_REJECTION_REASON_CODES = [
1320
2359
  "window_expired",
1321
2360
  "used_or_damaged",
1322
2361
  "non_returnable",
1323
2362
  "other"
1324
2363
  ];
1325
- var returnRejectionReasonCodeSchema = z4.enum(
2364
+ var returnRejectionReasonCodeSchema = z7.enum(
1326
2365
  RETURN_REJECTION_REASON_CODES
1327
2366
  );
1328
- var restockActionSchema = z4.enum(["return_to_stock", "discard"]);
1329
- var returnWithRefundItemSchema = z4.object({
1330
- orderItem: z4.union([z4.string().min(1), z4.number()]).transform(String),
1331
- quantity: z4.number().int().positive("quantity must be a positive integer"),
2367
+ var RETURN_CANCELLATION_REASON_CODES = [
2368
+ "items_not_received",
2369
+ "customer_withdrew",
2370
+ "merchant_decision",
2371
+ "other"
2372
+ ];
2373
+ var returnCancellationReasonCodeSchema = z7.enum(
2374
+ RETURN_CANCELLATION_REASON_CODES
2375
+ );
2376
+ var restockActionSchema = z7.enum(["return_to_stock", "discard"]);
2377
+ var returnWithRefundItemSchema = z7.object({
2378
+ orderItem: z7.union([z7.string().min(1), z7.number()]).transform(String),
2379
+ quantity: z7.number().int().positive("quantity must be a positive integer"),
1332
2380
  restockAction: restockActionSchema.default("return_to_stock"),
1333
- restockingFee: z4.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
2381
+ restockingFee: z7.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
1334
2382
  }).strict();
1335
- var returnWithRefundSchema = z4.object({
1336
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number (required)"),
2383
+ var returnWithRefundSchema = z7.object({
2384
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
1337
2385
  reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1338
- reasonDetail: z4.string().optional().describe("Detailed reason text (optional)"),
1339
- returnItems: z4.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1340
- refundAmount: z4.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
1341
- returnShippingFee: z4.number().min(0, "returnShippingFee must be non-negative").optional().describe(
2386
+ reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
2387
+ returnItems: z7.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
2388
+ refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
2389
+ returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe(
1342
2390
  "Return shipping fee charged to the customer (ADR 0005 \xA7Gap 1)"
1343
2391
  ),
1344
- initialShippingRefundAmount: z4.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
1345
- initialShippingRefundOverrideNote: z4.string().min(1).optional().describe(
2392
+ initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
2393
+ initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
1346
2394
  "Operator audit note required when overriding policy suggestion"
1347
2395
  ),
1348
- pgPaymentId: z4.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
1349
- paymentKey: z4.string().min(1).optional().describe("Provider payment key for verified refund"),
1350
- refundReceiptUrl: z4.string().optional().describe("Refund receipt URL (optional)")
2396
+ pgPaymentId: z7.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
2397
+ paymentKey: z7.string().min(1).optional().describe("Provider payment key for verified refund"),
2398
+ refundReceiptUrl: z7.string().optional().describe("Refund receipt URL (optional)")
1351
2399
  }).strict();
1352
- var createReturnSchema = z4.object({
1353
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number (required)"),
2400
+ var createReturnSchema = z7.object({
2401
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
1354
2402
  reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1355
- reasonDetail: z4.string().optional().describe("Detailed reason text (optional)"),
1356
- returnItems: z4.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1357
- refundAmount: z4.number().min(0, "refundAmount must be non-negative").describe(
2403
+ reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
2404
+ returnItems: z7.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
2405
+ refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe(
1358
2406
  "Line refund amount before initial shipping refund (required, min 0)"
1359
2407
  ),
1360
- returnShippingFee: z4.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
1361
- initialShippingRefundAmount: z4.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
1362
- initialShippingRefundOverrideNote: z4.string().min(1).optional().describe(
2408
+ returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
2409
+ initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
2410
+ initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
1363
2411
  "Operator audit note required when overriding policy suggestion"
1364
2412
  )
1365
2413
  }).strict();
2414
+ var updateReturnSchema = z7.object({
2415
+ returnId: z7.union([z7.string(), z7.number()]).transform(String),
2416
+ // Only non-financial transitions are accepted here. `completed` is included
2417
+ // so the server can return the canonical `server_derived` error instead of
2418
+ // a generic validation failure.
2419
+ status: z7.enum([
2420
+ "processing",
2421
+ "approved",
2422
+ "rejected",
2423
+ "canceled",
2424
+ "completed"
2425
+ ]),
2426
+ reasonCode: returnRejectionReasonCodeSchema.optional(),
2427
+ cancellationReasonCode: returnCancellationReasonCodeSchema.optional(),
2428
+ cancellationReasonDetail: z7.string().trim().min(1).max(2e3).optional()
2429
+ }).strict();
1366
2430
  var CANCEL_REASON_CODES = [
1367
2431
  "customer",
1368
2432
  "inventory",
@@ -1371,245 +2435,159 @@ var CANCEL_REASON_CODES = [
1371
2435
  "staff",
1372
2436
  "other"
1373
2437
  ];
1374
- var cancelReasonCodeSchema = z4.enum(CANCEL_REASON_CODES);
2438
+ var cancelReasonCodeSchema = z7.enum(CANCEL_REASON_CODES);
1375
2439
  var FORCE_RESOLUTION_REASON_CODES = [
1376
2440
  "staff_error",
1377
2441
  "wrong_fulfillment",
1378
2442
  "other"
1379
2443
  ];
1380
- var forceResolutionReasonCodeSchema = z4.enum(
2444
+ var forceResolutionReasonCodeSchema = z7.enum(
1381
2445
  FORCE_RESOLUTION_REASON_CODES
1382
2446
  );
1383
- var idempotencyKeySchema = z4.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
2447
+ var idempotencyKeySchema = z7.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
1384
2448
  /^[\x21-\x7E]+$/,
1385
2449
  "idempotencyKey must contain only printable header-safe characters"
1386
2450
  );
1387
- var cancelOrderSchema = z4.object({
1388
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number to cancel"),
2451
+ var cancelOrderSchema = z7.object({
2452
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number to cancel"),
1389
2453
  reasonCode: cancelReasonCodeSchema.default("other").describe("Operator-selected cancel reason code"),
1390
- reasonDetail: z4.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
2454
+ reasonDetail: z7.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
1391
2455
  }).strict();
1392
2456
  var cancelOrderResponseBaseSchema = {
1393
- orderId: z4.string().min(1)
2457
+ orderId: z7.string().min(1)
1394
2458
  };
1395
2459
  var unpaidLocalCancelCommittedResponseFields = {
1396
- refundedAmount: z4.literal(0),
1397
- providerRefunded: z4.literal(false)
2460
+ refundedAmount: z7.literal(0),
2461
+ providerRefunded: z7.literal(false)
1398
2462
  };
1399
2463
  var paidLocalCancelCommittedResponseFields = {
1400
- transactionId: z4.string().min(1),
1401
- refundedAmount: z4.literal(0),
1402
- providerRefunded: z4.literal(false),
1403
- refundPending: z4.literal(true)
1404
- };
1405
- var legacyProviderRefundResponseFields = {
1406
- transactionId: z4.string().min(1),
1407
- refundedAmount: z4.number().int().positive(),
1408
- refundSeq: z4.number().int().positive(),
1409
- providerRefunded: z4.literal(true)
2464
+ transactionId: z7.string().min(1),
2465
+ refundedAmount: z7.literal(0),
2466
+ providerRefunded: z7.literal(false),
2467
+ refundPending: z7.literal(true)
1410
2468
  };
1411
2469
  var alreadyCanceledResponseFields = {
1412
- refundedAmount: z4.number().int().nonnegative(),
1413
- providerRefunded: z4.literal(false)
2470
+ refundedAmount: z7.number().int().nonnegative(),
2471
+ providerRefunded: z7.literal(false)
1414
2472
  };
1415
2473
  var alreadyCanceledRefundPendingResponseFields = {
1416
- transactionId: z4.string().min(1),
1417
- refundedAmount: z4.number().int().nonnegative(),
1418
- providerRefunded: z4.literal(false),
1419
- refundPending: z4.literal(true)
2474
+ transactionId: z7.string().min(1),
2475
+ refundedAmount: z7.number().int().nonnegative(),
2476
+ providerRefunded: z7.literal(false),
2477
+ refundPending: z7.literal(true)
1420
2478
  };
1421
- var cancelOrderReconciliationStatusSchema = z4.enum([
1422
- "paid",
1423
- "preparing",
1424
- "shipped",
1425
- "delivered",
1426
- "confirmed",
1427
- "return_requested",
1428
- "return_processing",
1429
- "returned",
1430
- "refunded"
1431
- ]);
1432
- var cancelOrderResponseSchema = z4.union([
1433
- z4.object({
2479
+ var cancelOrderResponseSchema = z7.union([
2480
+ z7.object({
1434
2481
  ...cancelOrderResponseBaseSchema,
1435
2482
  ...paidLocalCancelCommittedResponseFields,
1436
- status: z4.literal("canceled"),
1437
- cancelCommitted: z4.literal(true)
2483
+ status: z7.literal("canceled"),
2484
+ cancelCommitted: z7.literal(true)
1438
2485
  }).strict(),
1439
- z4.object({
2486
+ z7.object({
1440
2487
  ...cancelOrderResponseBaseSchema,
1441
2488
  ...unpaidLocalCancelCommittedResponseFields,
1442
- status: z4.literal("canceled"),
1443
- cancelCommitted: z4.literal(true)
2489
+ status: z7.literal("canceled"),
2490
+ cancelCommitted: z7.literal(true)
1444
2491
  }).strict(),
1445
- z4.object({
2492
+ z7.object({
1446
2493
  ...cancelOrderResponseBaseSchema,
1447
2494
  ...alreadyCanceledResponseFields,
1448
- status: z4.literal("canceled"),
1449
- cancelCommitted: z4.literal(false),
1450
- alreadyCanceled: z4.literal(true)
2495
+ status: z7.literal("canceled"),
2496
+ cancelCommitted: z7.literal(false),
2497
+ alreadyCanceled: z7.literal(true)
1451
2498
  }).strict(),
1452
- z4.object({
2499
+ z7.object({
1453
2500
  ...cancelOrderResponseBaseSchema,
1454
2501
  ...alreadyCanceledRefundPendingResponseFields,
1455
- status: z4.literal("canceled"),
1456
- cancelCommitted: z4.literal(false),
1457
- alreadyCanceled: z4.literal(true)
1458
- }).strict(),
1459
- z4.object({
1460
- ...cancelOrderResponseBaseSchema,
1461
- ...legacyProviderRefundResponseFields,
1462
- status: cancelOrderReconciliationStatusSchema,
1463
- cancelCommitted: z4.literal(false),
1464
- reconciliationRequired: z4.literal(true)
2502
+ status: z7.literal("canceled"),
2503
+ cancelCommitted: z7.literal(false),
2504
+ alreadyCanceled: z7.literal(true)
1465
2505
  }).strict()
1466
2506
  ]);
1467
- var resolveCancelRefundOutcomeSchema = z4.enum(["succeeded", "failed"]);
1468
- var resolveCancelRefundSchema = z4.object({
1469
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
2507
+ var resolveCancelRefundOutcomeSchema = z7.enum(["succeeded", "failed"]);
2508
+ var resolveCancelRefundSchema = z7.object({
2509
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
1470
2510
  idempotencyKey: idempotencyKeySchema.describe(
1471
2511
  "Stable key for this PG refund result report"
1472
2512
  ),
1473
2513
  outcome: resolveCancelRefundOutcomeSchema.describe(
1474
2514
  "PG refund result reported by the storefront or BFF"
1475
2515
  ),
1476
- refundedAmount: z4.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
1477
- pgProvider: z4.string().trim().min(1, "pgProvider is required").regex(
2516
+ refundedAmount: z7.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
2517
+ pgProvider: z7.string().trim().min(1, "pgProvider is required").regex(
1478
2518
  /^[a-z0-9][a-z0-9_-]*$/,
1479
2519
  "pgProvider must be a lowercase provider slug"
1480
2520
  ),
1481
- pgRefundId: z4.string().trim().min(1).optional()
2521
+ pgRefundId: z7.string().trim().min(1).optional()
1482
2522
  }).strict().superRefine((value, ctx) => {
1483
2523
  if (value.outcome === "succeeded" && value.refundedAmount <= 0) {
1484
2524
  ctx.addIssue({
1485
- code: z4.ZodIssueCode.custom,
2525
+ code: z7.ZodIssueCode.custom,
1486
2526
  path: ["refundedAmount"],
1487
2527
  message: "refundedAmount must be positive when outcome is succeeded"
1488
2528
  });
1489
2529
  }
1490
2530
  if (value.outcome === "succeeded" && !value.pgRefundId) {
1491
2531
  ctx.addIssue({
1492
- code: z4.ZodIssueCode.custom,
2532
+ code: z7.ZodIssueCode.custom,
1493
2533
  path: ["pgRefundId"],
1494
2534
  message: "pgRefundId is required when outcome is succeeded"
1495
2535
  });
1496
2536
  }
1497
2537
  if (value.outcome === "failed" && value.refundedAmount !== 0) {
1498
2538
  ctx.addIssue({
1499
- code: z4.ZodIssueCode.custom,
2539
+ code: z7.ZodIssueCode.custom,
1500
2540
  path: ["refundedAmount"],
1501
2541
  message: "refundedAmount must be 0 when outcome is failed"
1502
2542
  });
1503
2543
  }
1504
2544
  });
1505
- var resolveCancelRefundResponseSchema = z4.union([
1506
- z4.object({
1507
- orderId: z4.string().min(1),
1508
- transactionId: z4.string().min(1),
1509
- refundTransactionId: z4.string().min(1),
1510
- refundedAmount: z4.number().int().positive(),
1511
- refundStatus: z4.literal("succeeded"),
1512
- transactionStatus: z4.literal("refunded")
2545
+ var resolveCancelRefundResponseSchema = z7.union([
2546
+ z7.object({
2547
+ orderId: z7.string().min(1),
2548
+ transactionId: z7.string().min(1),
2549
+ refundTransactionId: z7.string().min(1),
2550
+ refundedAmount: z7.number().int().positive(),
2551
+ refundStatus: z7.literal("succeeded"),
2552
+ transactionStatus: z7.enum(["paid", "refunded"])
1513
2553
  }).strict(),
1514
- z4.object({
1515
- orderId: z4.string().min(1),
1516
- transactionId: z4.string().min(1),
1517
- refundTransactionId: z4.string().min(1),
1518
- refundedAmount: z4.literal(0),
1519
- refundStatus: z4.literal("failed"),
1520
- transactionStatus: z4.literal("paid")
2554
+ z7.object({
2555
+ orderId: z7.string().min(1),
2556
+ transactionId: z7.string().min(1),
2557
+ refundTransactionId: z7.string().min(1),
2558
+ refundedAmount: z7.literal(0),
2559
+ refundStatus: z7.literal("failed"),
2560
+ transactionStatus: z7.literal("paid")
1521
2561
  }).strict()
1522
2562
  ]);
1523
2563
 
1524
2564
  // src/commands/order.ts
1525
- import { z as z5 } from "zod";
1526
- var idSchema = z5.union([z5.string().min(1), z5.number()]).transform(String);
1527
- var customerSnapshotSchema = z5.object({
1528
- email: z5.string().email("Invalid email format"),
1529
- name: z5.string().optional(),
1530
- phone: z5.string().optional()
1531
- }).strict();
1532
- var orderStatusSchema2 = z5.enum(["confirmed"]);
1533
- var fulfillmentStatusSchema = z5.enum(["shipped", "delivered", "failed"]);
1534
- var updateFulfillmentSchema = z5.object({
2565
+ import { z as z8 } from "zod";
2566
+ var idSchema = z8.union([z8.string().min(1), z8.number()]).transform(String);
2567
+ var orderStatusSchema = z8.enum(["confirmed"]);
2568
+ var fulfillmentStatusSchema = z8.enum(["shipped", "delivered", "failed"]);
2569
+ var updateFulfillmentSchema = z8.object({
1535
2570
  fulfillmentId: idSchema,
1536
2571
  status: fulfillmentStatusSchema.optional(),
1537
- carrier: z5.string().min(1).optional(),
1538
- trackingNumber: z5.string().min(1).optional()
2572
+ carrier: z8.string().min(1).optional(),
2573
+ trackingNumber: z8.string().min(1).optional()
1539
2574
  }).refine(
1540
2575
  (value) => value.status !== void 0 || value.carrier !== void 0 || value.trackingNumber !== void 0,
1541
2576
  {
1542
2577
  message: "status, carrier, or trackingNumber is required"
1543
2578
  }
1544
2579
  );
1545
- var fulfillmentItemsSchema = z5.array(
1546
- z5.object({
2580
+ var fulfillmentItemsSchema = z8.array(
2581
+ z8.object({
1547
2582
  orderItem: idSchema,
1548
- quantity: z5.number().int().positive("quantity must be a positive integer")
2583
+ quantity: z8.number().int().positive("quantity must be a positive integer")
1549
2584
  }).strict()
1550
2585
  ).min(1, "At least one fulfillment item is required").max(100, "Maximum 100 items per fulfillment");
1551
2586
  function registerOrderCommands(program2, getClient2, getFormat2) {
1552
2587
  const order = program2.command("order").description("Order management");
1553
- order.command("create").description("Create a new order").option("--payment-id <id>", "Payment ID").option("--provider <slug>", "Payment provider slug").requiredOption("--order-number <num>", "Order number").requiredOption("--email <email>", "Customer email").option("--customer <id>", "Customer ID").option("--name <name>", "Customer name").option("--phone <phone>", "Customer phone").requiredOption("--shipping-address <json>", "Shipping address (JSON)").requiredOption("--products <json>", "Order products array (JSON)").requiredOption("--total-amount <n>", "Total amount", parseFloat).option("--shipping-amount <n>", "Shipping amount", parseFloat).option("--discount-code <code>", "Discount code").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1554
- try {
1555
- const parsed = parseWithSchema(
1556
- {
1557
- ...opts.paymentId ? { pgPaymentId: opts.paymentId } : {},
1558
- ...opts.provider ? { pgProvider: opts.provider } : {},
1559
- orderNumber: opts.orderNumber,
1560
- customerSnapshot: {
1561
- email: opts.email,
1562
- name: opts.name,
1563
- phone: opts.phone
1564
- },
1565
- customer: opts.customer,
1566
- shippingAddress: parseJson(
1567
- opts.shippingAddress,
1568
- "shipping-address"
1569
- ),
1570
- orderItems: parseJsonArray(opts.products, "products"),
1571
- totalAmount: opts.totalAmount,
1572
- shippingAmount: opts.shippingAmount,
1573
- discountCode: opts.discountCode
1574
- },
1575
- "order",
1576
- createOrderSchema
1577
- );
1578
- const { pgPaymentId, pgProvider, ...orderData } = parsed;
1579
- const data = pgPaymentId ? { ...orderData, pgPaymentId, pgProvider } : orderData;
1580
- if (opts.dryRun) {
1581
- printResult(
1582
- { dryRun: true, valid: true, action: "order create", data },
1583
- getFormat2()
1584
- );
1585
- return;
1586
- }
1587
- const client = getClient2();
1588
- const result = await client.commerce.orders.create(data);
1589
- printResult(result, getFormat2());
1590
- } catch (e) {
1591
- exitWithError(e);
1592
- }
1593
- });
1594
- order.command("get <orderNumber>").description("Get an order by order number").action(async (orderNumber) => {
1595
- try {
1596
- const client = getClient2();
1597
- const {
1598
- docs: [order2]
1599
- } = await client.collections.from("orders").find({
1600
- where: { orderNumber: { equals: orderNumber } },
1601
- limit: 1,
1602
- depth: 1
1603
- });
1604
- if (!order2) throw new Error("Order not found");
1605
- printResult(order2, getFormat2());
1606
- } catch (e) {
1607
- exitWithError(e);
1608
- }
1609
- });
1610
2588
  order.command("update <orderNumber>").description("Confirm a delivered order purchase").requiredOption("--status <status>", "New status (confirmed only)").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
1611
2589
  try {
1612
- const status = parseWithSchema(opts.status, "status", orderStatusSchema2);
2590
+ const status = parseWithSchema(opts.status, "status", orderStatusSchema);
1613
2591
  const data = { orderNumber, status };
1614
2592
  if (opts.dryRun) {
1615
2593
  printResult(
@@ -1653,40 +2631,6 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
1653
2631
  exitWithError(e);
1654
2632
  }
1655
2633
  });
1656
- order.command("checkout").description("Convert a cart to an order").requiredOption("--cart-id <id>", "Cart ID").option("--payment-id <id>", "Payment ID (optional for free orders)").option("--provider <slug>", "Payment provider slug").requiredOption("--order-number <num>", "Order number").requiredOption("--customer <json>", "Customer snapshot (JSON)").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1657
- try {
1658
- const customerSnapshot = parseWithSchema(
1659
- parseJson(opts.customer, "customer"),
1660
- "customer",
1661
- customerSnapshotSchema
1662
- );
1663
- const parsed = parseWithSchema(
1664
- {
1665
- cartId: opts.cartId,
1666
- ...opts.paymentId ? { pgPaymentId: opts.paymentId } : {},
1667
- ...opts.provider ? { pgProvider: opts.provider } : {},
1668
- orderNumber: opts.orderNumber,
1669
- customerSnapshot
1670
- },
1671
- "checkout",
1672
- CheckoutRequestSchema
1673
- );
1674
- const { pgPaymentId, pgProvider, ...checkoutData } = parsed;
1675
- const data = pgPaymentId ? { ...checkoutData, pgPaymentId, pgProvider } : checkoutData;
1676
- if (opts.dryRun) {
1677
- printResult(
1678
- { dryRun: true, valid: true, action: "order checkout", data },
1679
- getFormat2()
1680
- );
1681
- return;
1682
- }
1683
- const client = getClient2();
1684
- const result = await client.commerce.orders.checkout(data);
1685
- printResult(result, getFormat2());
1686
- } catch (e) {
1687
- exitWithError(e);
1688
- }
1689
- });
1690
2634
  order.command("prepare <orderNumber>").description("Move paid order fulfillment work to preparation").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
1691
2635
  try {
1692
2636
  const data = { orderNumber };
@@ -1770,8 +2714,8 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
1770
2714
  }
1771
2715
 
1772
2716
  // src/commands/return.ts
1773
- import { z as z6 } from "zod";
1774
- var returnStatusSchema = z6.enum([
2717
+ import { z as z9 } from "zod";
2718
+ var returnStatusSchema = z9.enum([
1775
2719
  "processing",
1776
2720
  "approved",
1777
2721
  "rejected",
@@ -1900,146 +2844,227 @@ function registerReturnCommands(program2, getClient2, getFormat2) {
1900
2844
  });
1901
2845
  }
1902
2846
 
2847
+ // src/lib/sensitive-json-input.ts
2848
+ import { createReadStream } from "fs";
2849
+ import { stdin as processStdin } from "process";
2850
+ var SENSITIVE_JSON_MAX_BYTES = 1024 * 1024;
2851
+ function invalidInput(message) {
2852
+ return {
2853
+ type: "validation",
2854
+ code: "invalid_argument",
2855
+ field: "data",
2856
+ message,
2857
+ detail: { message, field: "data" }
2858
+ };
2859
+ }
2860
+ function isInvalidInput(error) {
2861
+ if (!error || typeof error !== "object") return false;
2862
+ const value = error;
2863
+ return value.type === "validation" && value.code === "invalid_argument";
2864
+ }
2865
+ async function readBoundedUtf8(source) {
2866
+ const chunks = [];
2867
+ let totalBytes = 0;
2868
+ for await (const chunk of source) {
2869
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2870
+ totalBytes += buffer.byteLength;
2871
+ if (totalBytes > SENSITIVE_JSON_MAX_BYTES) {
2872
+ throw invalidInput(
2873
+ `Sensitive commerce data must be ${SENSITIVE_JSON_MAX_BYTES} bytes or fewer.`
2874
+ );
2875
+ }
2876
+ chunks.push(buffer);
2877
+ }
2878
+ return Buffer.concat(chunks, totalBytes).toString("utf8");
2879
+ }
2880
+ async function readSensitiveJsonObject(opts, sources = {}) {
2881
+ const sourceCount = [opts.dataStdin, opts.dataFile].filter(Boolean).length;
2882
+ if (sourceCount !== 1) {
2883
+ throw invalidInput(
2884
+ "Exactly one of --data-stdin or --data-file is required for sensitive commerce data."
2885
+ );
2886
+ }
2887
+ let raw;
2888
+ const input = sources.stdin ?? processStdin;
2889
+ try {
2890
+ if (opts.dataStdin) {
2891
+ if (input.isTTY === true) {
2892
+ throw invalidInput(
2893
+ "Sensitive commerce data cannot be read from an interactive terminal. Pipe JSON to --data-stdin or use --data-file."
2894
+ );
2895
+ }
2896
+ raw = await readBoundedUtf8(input);
2897
+ } else {
2898
+ const file = (sources.openFile ?? createReadStream)(opts.dataFile);
2899
+ try {
2900
+ raw = await readBoundedUtf8(file);
2901
+ } finally {
2902
+ file.destroy?.();
2903
+ }
2904
+ }
2905
+ } catch (error) {
2906
+ if (isInvalidInput(error)) throw error;
2907
+ throw invalidInput("Sensitive commerce data could not be read.");
2908
+ }
2909
+ let parsed;
2910
+ try {
2911
+ parsed = JSON.parse(raw);
2912
+ } catch {
2913
+ throw invalidInput("Sensitive commerce data must be valid JSON.");
2914
+ }
2915
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
2916
+ throw invalidInput("Sensitive commerce data must be a JSON object.");
2917
+ }
2918
+ return parsed;
2919
+ }
2920
+ function sensitiveDryRunResult(action) {
2921
+ return { dryRun: true, valid: true, action };
2922
+ }
2923
+
1903
2924
  // src/commands/cart.ts
1904
2925
  function registerCartCommands(program2, getClient2, getFormat2) {
1905
2926
  const cart = program2.command("cart").description("Cart management");
1906
- cart.command("create").description("Create a cart and print its cartToken handle").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2927
+ cart.command("create").description("Create a cart and print its cartToken capability").option("--data-stdin", "Read market and attributes JSON from stdin").option("--data-file <path>", "Read market and attributes JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1907
2928
  try {
2929
+ const input = opts.dataStdin || opts.dataFile ? await readSensitiveJsonObject(opts) : {};
2930
+ const data = parseWithSchema(
2931
+ input,
2932
+ "cart create",
2933
+ createCartParamsSchema
2934
+ );
1908
2935
  if (opts.dryRun) {
1909
- printResult(
1910
- { dryRun: true, valid: true, action: "cart create", data: {} },
1911
- getFormat2()
1912
- );
2936
+ printResult(sensitiveDryRunResult("cart create"), getFormat2());
1913
2937
  return;
1914
2938
  }
1915
- const client = getClient2();
1916
- const result = await client.commerce.cart.create();
1917
- printResult(result, getFormat2());
1918
- } catch (e) {
1919
- exitWithError(e);
2939
+ printResult(await getClient2().commerce.carts.create(data), getFormat2());
2940
+ } catch (error) {
2941
+ exitWithError(error);
1920
2942
  }
1921
2943
  });
1922
- cart.command("add <cartToken>").description("Add an item to a cart (addressed by its cartToken)").requiredOption("--product <id>", "Product ID").requiredOption("--variant <id>", "Variant ID").requiredOption("--option <id>", "Option ID").requiredOption(
1923
- "--quantity <n>",
1924
- "Quantity",
1925
- (v) => parseInt(v, 10)
1926
- ).option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
2944
+ cart.command("retrieve").description("Retrieve a cart by its capability token").option("--data-stdin", "Read the Cart capability JSON from stdin").option("--data-file <path>", "Read the Cart capability JSON from file").action(async (opts) => {
1927
2945
  try {
1928
- const data = {
1929
- cartToken,
1930
- product: opts.product,
1931
- variant: opts.variant,
1932
- option: opts.option,
1933
- quantity: opts.quantity
1934
- };
2946
+ const data = parseWithSchema(
2947
+ await readSensitiveJsonObject(opts),
2948
+ "cart retrieve",
2949
+ retrieveCartParamsSchema
2950
+ );
2951
+ printResult(
2952
+ await getClient2().commerce.carts.retrieve(data),
2953
+ getFormat2()
2954
+ );
2955
+ } catch (error) {
2956
+ exitWithError(error);
2957
+ }
2958
+ });
2959
+ cart.command("add").description("Add an item to a cart").option("--data-stdin", "Read capability and item JSON from stdin").option("--data-file <path>", "Read capability and item JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2960
+ try {
2961
+ const data = parseWithSchema(
2962
+ await readSensitiveJsonObject(opts),
2963
+ "cart add",
2964
+ addItemParamsSchema
2965
+ );
1935
2966
  if (opts.dryRun) {
1936
- printResult(
1937
- { dryRun: true, valid: true, action: "cart add", data },
1938
- getFormat2()
1939
- );
2967
+ printResult(sensitiveDryRunResult("cart add"), getFormat2());
1940
2968
  return;
1941
2969
  }
1942
- const client = getClient2();
1943
- const result = await client.commerce.cart.addItem(data);
1944
- printResult(result, getFormat2());
1945
- } catch (e) {
1946
- exitWithError(e);
2970
+ printResult(await getClient2().commerce.carts.addItem(data), getFormat2());
2971
+ } catch (error) {
2972
+ exitWithError(error);
1947
2973
  }
1948
2974
  });
1949
- cart.command("update <cartItemId>").description("Update cart item quantity").requiredOption("--cart-token <token>", "Cart token (cart handle)").requiredOption(
1950
- "--quantity <n>",
1951
- "New quantity",
1952
- (v) => parseInt(v, 10)
1953
- ).option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
2975
+ cart.command("update").description("Update cart item quantity").option("--data-stdin", "Read capability and item JSON from stdin").option("--data-file <path>", "Read capability and item JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1954
2976
  try {
1955
- const data = {
1956
- cartToken: opts.cartToken,
1957
- cartItemId,
1958
- quantity: opts.quantity
1959
- };
2977
+ const data = parseWithSchema(
2978
+ await readSensitiveJsonObject(opts),
2979
+ "cart update",
2980
+ updateItemParamsSchema
2981
+ );
1960
2982
  if (opts.dryRun) {
1961
- printResult(
1962
- { dryRun: true, valid: true, action: "cart update", data },
1963
- getFormat2()
1964
- );
2983
+ printResult(sensitiveDryRunResult("cart update"), getFormat2());
1965
2984
  return;
1966
2985
  }
1967
- const client = getClient2();
1968
- const result = await client.commerce.cart.updateItem(data);
1969
- printResult(result, getFormat2());
1970
- } catch (e) {
1971
- exitWithError(e);
2986
+ printResult(
2987
+ await getClient2().commerce.carts.updateItem(data),
2988
+ getFormat2()
2989
+ );
2990
+ } catch (error) {
2991
+ exitWithError(error);
1972
2992
  }
1973
2993
  });
1974
- cart.command("remove <cartItemId>").description("Remove an item from a cart").requiredOption("--cart-token <token>", "Cart token (cart handle)").option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
2994
+ cart.command("remove").description("Remove an item from a cart").option("--data-stdin", "Read capability and item JSON from stdin").option("--data-file <path>", "Read capability and item JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1975
2995
  try {
1976
- const data = { cartToken: opts.cartToken, cartItemId };
1977
- if (opts.dryRun) {
1978
- printResult(
1979
- { dryRun: true, valid: true, action: "cart remove", data },
1980
- getFormat2()
1981
- );
2996
+ const data = parseWithSchema(
2997
+ await readSensitiveJsonObject(opts),
2998
+ "cart remove",
2999
+ removeItemParamsSchema
3000
+ );
3001
+ if (opts.dryRun) {
3002
+ printResult(sensitiveDryRunResult("cart remove"), getFormat2());
1982
3003
  return;
1983
3004
  }
1984
- const client = getClient2();
1985
- const result = await client.commerce.cart.removeItem(data);
1986
- printResult(result, getFormat2());
1987
- } catch (e) {
1988
- exitWithError(e);
3005
+ printResult(
3006
+ await getClient2().commerce.carts.removeItem(data),
3007
+ getFormat2()
3008
+ );
3009
+ } catch (error) {
3010
+ exitWithError(error);
1989
3011
  }
1990
3012
  });
1991
- cart.command("clear <cartToken>").description("Remove all items from a cart").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3013
+ cart.command("clear").description("Remove all items from a cart").option("--data-stdin", "Read the Cart capability JSON from stdin").option("--data-file <path>", "Read the Cart capability JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1992
3014
  try {
3015
+ const data = parseWithSchema(
3016
+ await readSensitiveJsonObject(opts),
3017
+ "cart clear",
3018
+ clearCartParamsSchema
3019
+ );
1993
3020
  if (opts.dryRun) {
1994
- printResult(
1995
- { dryRun: true, valid: true, action: "cart clear", data: { cartToken } },
1996
- getFormat2()
1997
- );
3021
+ printResult(sensitiveDryRunResult("cart clear"), getFormat2());
1998
3022
  return;
1999
3023
  }
2000
- const client = getClient2();
2001
- const result = await client.commerce.cart.clear({ cartToken });
2002
- printResult(result, getFormat2());
2003
- } catch (e) {
2004
- exitWithError(e);
3024
+ printResult(await getClient2().commerce.carts.clear(data), getFormat2());
3025
+ } catch (error) {
3026
+ exitWithError(error);
2005
3027
  }
2006
3028
  });
2007
- cart.command("apply-discount <cartToken>").description("Apply a discount code to a cart").requiredOption("--code <code>", "Discount code").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3029
+ cart.command("discounts").description("Replace all cart discount codes; pass [] to clear").option("--data-stdin", "Read capability and discount JSON from stdin").option("--data-file <path>", "Read capability and discount JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2008
3030
  try {
2009
- const data = { cartToken, discountCode: opts.code };
3031
+ const data = parseWithSchema(
3032
+ await readSensitiveJsonObject(opts),
3033
+ "cart discounts",
3034
+ updateDiscountCodesParamsSchema
3035
+ );
2010
3036
  if (opts.dryRun) {
2011
- printResult(
2012
- { dryRun: true, valid: true, action: "cart apply-discount", data },
2013
- getFormat2()
2014
- );
3037
+ printResult(sensitiveDryRunResult("cart discounts"), getFormat2());
2015
3038
  return;
2016
3039
  }
2017
- const client = getClient2();
2018
- const result = await client.commerce.cart.applyDiscount(data);
2019
- printResult(result, getFormat2());
2020
- } catch (e) {
2021
- exitWithError(e);
3040
+ printResult(
3041
+ await getClient2().commerce.carts.updateDiscountCodes(data),
3042
+ getFormat2()
3043
+ );
3044
+ } catch (error) {
3045
+ exitWithError(error);
2022
3046
  }
2023
3047
  });
2024
- cart.command("remove-discount <cartToken>").description("Remove the discount from a cart").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3048
+ cart.command("attributes").description("Replace all public cart attributes; pass [] to clear").option("--data-stdin", "Read capability and attributes JSON from stdin").option(
3049
+ "--data-file <path>",
3050
+ "Read capability and attributes JSON from file"
3051
+ ).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2025
3052
  try {
3053
+ const data = parseWithSchema(
3054
+ await readSensitiveJsonObject(opts),
3055
+ "cart attributes",
3056
+ updateCartAttributesParamsSchema
3057
+ );
2026
3058
  if (opts.dryRun) {
2027
- printResult(
2028
- {
2029
- dryRun: true,
2030
- valid: true,
2031
- action: "cart remove-discount",
2032
- data: { cartToken }
2033
- },
2034
- getFormat2()
2035
- );
3059
+ printResult(sensitiveDryRunResult("cart attributes"), getFormat2());
2036
3060
  return;
2037
3061
  }
2038
- const client = getClient2();
2039
- const result = await client.commerce.cart.removeDiscount({ cartToken });
2040
- printResult(result, getFormat2());
2041
- } catch (e) {
2042
- exitWithError(e);
3062
+ printResult(
3063
+ await getClient2().commerce.carts.updateAttributes(data),
3064
+ getFormat2()
3065
+ );
3066
+ } catch (error) {
3067
+ exitWithError(error);
2043
3068
  }
2044
3069
  });
2045
3070
  }
@@ -2063,124 +3088,196 @@ function registerStockCommands(program2, getClient2, getFormat2) {
2063
3088
  }
2064
3089
 
2065
3090
  // src/commands/transaction.ts
2066
- import { z as z7 } from "zod";
2067
- var transactionAmountSchema = z7.object({
2068
- amount: z7.string().regex(/^[1-9]\d*$/, "amount must be a positive integer").transform(Number)
2069
- });
2070
- var confirmPaymentAmountSchema = z7.object({
2071
- amount: z7.string().regex(/^(0|[1-9]\d*)$/, "amount must be a non-negative integer").transform(Number)
2072
- });
2073
- var idempotencyKeyOptionSchema = z7.object({
2074
- idempotencyKey: idempotencyKeySchema
2075
- });
2076
- function parseTransactionAmount(value) {
2077
- if (value === void 0) return void 0;
2078
- return parseWithSchema({ amount: value }, "amount", transactionAmountSchema).amount;
2079
- }
2080
- function parseConfirmPaymentAmount(value) {
2081
- return parseWithSchema({ amount: value }, "amount", confirmPaymentAmountSchema).amount;
2082
- }
2083
- function parseIdempotencyKey(value) {
2084
- if (value === void 0) return void 0;
2085
- return parseWithSchema(
2086
- { idempotencyKey: value },
2087
- "idempotencyKey",
2088
- idempotencyKeyOptionSchema
2089
- ).idempotencyKey;
2090
- }
2091
- function registerTransactionCommands(program2, getClient2, getFormat2) {
2092
- const tx = program2.command("transaction").description("Transaction management");
2093
- tx.command("update").description("Update transaction status").requiredOption("--payment-id <id>", "Payment ID").requiredOption(
2094
- "--status <status>",
2095
- "New status (pending, paid, failed, canceled, refunded)"
2096
- ).option("--payment-method <method>", "Payment method").option("--receipt-url <url>", "Receipt URL").option("--payment-key <key>", "Provider payment key").option("--amount <n>", "Provider-confirmed amount").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3091
+ function registerCommerceSessionCommands(program2, getClient2, getFormat2) {
3092
+ const checkout = program2.command("checkout").description("Checkout management");
3093
+ checkout.command("create").description("Create a Checkout from a server-authoritative Cart").option("--data-stdin", "Read capability and customer JSON from stdin").option("--data-file <path>", "Read capability and customer JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2097
3094
  try {
2098
3095
  const data = parseWithSchema(
2099
- {
2100
- pgPaymentId: opts.paymentId,
2101
- status: opts.status,
2102
- paymentMethod: opts.paymentMethod,
2103
- receiptUrl: opts.receiptUrl,
2104
- paymentKey: opts.paymentKey,
2105
- amount: parseTransactionAmount(opts.amount)
2106
- },
2107
- "transaction",
2108
- updateTransactionSchema
3096
+ await readSensitiveJsonObject(opts),
3097
+ "checkout",
3098
+ CreateServerCheckoutParamsSchema
3099
+ );
3100
+ if (opts.dryRun) {
3101
+ printResult(sensitiveDryRunResult("checkout create"), getFormat2());
3102
+ return;
3103
+ }
3104
+ printResult(
3105
+ await getClient2().commerce.checkouts.create(data),
3106
+ getFormat2()
3107
+ );
3108
+ } catch (error) {
3109
+ exitWithError(error);
3110
+ }
3111
+ });
3112
+ checkout.command("retrieve <checkoutId>").description("Retrieve a Checkout by ID with server credentials").action(async (checkoutId) => {
3113
+ try {
3114
+ printResult(
3115
+ await getClient2().commerce.checkouts.retrieve({ checkoutId }),
3116
+ getFormat2()
3117
+ );
3118
+ } catch (error) {
3119
+ exitWithError(error);
3120
+ }
3121
+ });
3122
+ checkout.command("query").description("Query lightweight Checkouts with server credentials").option("--params <json>", "Checkout query parameters (JSON)", "{}").action(async (opts) => {
3123
+ try {
3124
+ const data = parseWithSchema(
3125
+ parseJson(opts.params, "params"),
3126
+ "checkout query",
3127
+ CanonicalCheckoutQuerySchema
3128
+ );
3129
+ printResult(
3130
+ await getClient2().commerce.checkouts.query(data),
3131
+ getFormat2()
3132
+ );
3133
+ } catch (error) {
3134
+ exitWithError(error);
3135
+ }
3136
+ });
3137
+ const sessions = program2.command("payment-session").description("PaymentSession management");
3138
+ sessions.command("create").description("Create a canonical provider-configured PaymentSession").option("--data-stdin", "Read canonical create parameters from stdin").option("--data-file <path>", "Read canonical create parameters from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3139
+ try {
3140
+ const data = parseWithSchema(
3141
+ await readSensitiveJsonObject(opts),
3142
+ "payment-session",
3143
+ CreatePaymentSessionParamsSchema
2109
3144
  );
2110
3145
  if (opts.dryRun) {
2111
3146
  printResult(
2112
- { dryRun: true, valid: true, action: "transaction update", data },
3147
+ sensitiveDryRunResult("payment-session create"),
2113
3148
  getFormat2()
2114
3149
  );
2115
3150
  return;
2116
3151
  }
2117
- const client = getClient2();
2118
- const result = await client.commerce.orders.updateTransaction(data);
2119
- printResult(result, getFormat2());
2120
- } catch (e) {
2121
- exitWithError(e);
3152
+ printResult(
3153
+ await getClient2().commerce.paymentSessions.create(data),
3154
+ getFormat2()
3155
+ );
3156
+ } catch (error) {
3157
+ exitWithError(error);
3158
+ }
3159
+ });
3160
+ sessions.command("retrieve <paymentSessionId>").description("Retrieve a PaymentSession").action(async (paymentSessionId) => {
3161
+ try {
3162
+ printResult(
3163
+ await getClient2().commerce.paymentSessions.retrieve({
3164
+ paymentSessionId
3165
+ }),
3166
+ getFormat2()
3167
+ );
3168
+ } catch (error) {
3169
+ exitWithError(error);
2122
3170
  }
2123
3171
  });
2124
- tx.command("confirm-payment").description("Confirm a provider-verified payment for an order").requiredOption("--payment-id <id>", "Provider payment identifier (pgPaymentId)").requiredOption(
2125
- "--provider <slug>",
2126
- "Payment provider slug (toss, portone, stripe, ...)"
2127
- ).requiredOption(
2128
- "--amount <n>",
2129
- "Provider-confirmed amount in minor units"
2130
- ).option("--order-number <num>", "Order number").option("--pg-order-id <id>", "Provider order id").option("--currency <code>", "Currency code").option("--payment-method <method>", "Payment method").option("--receipt-url <url>", "Receipt URL").option("--approved-at <ts>", "Provider approval timestamp").option("--provider-status <status>", "Provider status").option("--provider-event-id <id>", "Provider event id (idempotency)").option("--idempotency-key <key>", "Idempotency key header override").option(
2131
- "--confirmation-source <src>",
2132
- "provider_webhook | provider_lookup | provider_api_confirm | manual_server"
2133
- ).option("--payment-key <key>", "Provider payment key").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3172
+ sessions.command("cancel").description("Cancel a PaymentSession and release its payment slot").option("--data-stdin", "Read session ID and idempotency key from stdin").option(
3173
+ "--data-file <path>",
3174
+ "Read session ID and idempotency key from file"
3175
+ ).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2134
3176
  try {
2135
3177
  const data = parseWithSchema(
2136
- {
2137
- pgPaymentId: opts.paymentId,
2138
- pgProvider: opts.provider,
2139
- amount: parseConfirmPaymentAmount(opts.amount),
2140
- ...opts.orderNumber ? { orderNumber: opts.orderNumber } : {},
2141
- ...opts.pgOrderId ? { pgOrderId: opts.pgOrderId } : {},
2142
- ...opts.currency ? { currency: opts.currency } : {},
2143
- ...opts.paymentMethod ? { paymentMethod: opts.paymentMethod } : {},
2144
- ...opts.receiptUrl ? { receiptUrl: opts.receiptUrl } : {},
2145
- ...opts.approvedAt ? { approvedAt: opts.approvedAt } : {},
2146
- ...opts.providerStatus ? { providerStatus: opts.providerStatus } : {},
2147
- ...opts.providerEventId ? { providerEventId: opts.providerEventId } : {},
2148
- ...opts.confirmationSource ? { confirmationSource: opts.confirmationSource } : {},
2149
- ...opts.paymentKey ? { paymentKey: opts.paymentKey } : {}
2150
- },
2151
- "confirm-payment",
2152
- confirmPaymentSchema
3178
+ await readSensitiveJsonObject(opts),
3179
+ "payment-session cancel",
3180
+ CancelServerPaymentSessionParamsSchema
2153
3181
  );
2154
- const sdkParams = {
2155
- ...data,
2156
- ...opts.idempotencyKey !== void 0 ? { idempotencyKey: parseIdempotencyKey(opts.idempotencyKey) } : {}
2157
- };
2158
3182
  if (opts.dryRun) {
2159
3183
  printResult(
2160
- {
2161
- dryRun: true,
2162
- valid: true,
2163
- action: "transaction confirm-payment",
2164
- data: sdkParams
2165
- },
3184
+ sensitiveDryRunResult("payment-session cancel"),
2166
3185
  getFormat2()
2167
3186
  );
2168
3187
  return;
2169
3188
  }
2170
- const client = getClient2();
2171
- const result = await client.commerce.orders.confirmPayment(
2172
- sdkParams
3189
+ printResult(
3190
+ await getClient2().commerce.paymentSessions.cancel(data),
3191
+ getFormat2()
2173
3192
  );
2174
- printResult(result, getFormat2());
2175
- } catch (e) {
2176
- exitWithError(e);
3193
+ } catch (error) {
3194
+ exitWithError(error);
3195
+ }
3196
+ });
3197
+ sessions.command("confirm-toss").description("Confirm a Toss authorization with the typed server action").option("--data-stdin", "Read Toss confirmation params from stdin").option("--data-file <path>", "Read Toss confirmation params from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3198
+ try {
3199
+ const data = parseWithSchema(
3200
+ await readSensitiveJsonObject(opts),
3201
+ "payment-session confirm-toss",
3202
+ ConfirmTossPaymentSessionParamsSchema
3203
+ );
3204
+ if (opts.dryRun) {
3205
+ printResult(
3206
+ sensitiveDryRunResult("payment-session confirm-toss"),
3207
+ getFormat2()
3208
+ );
3209
+ return;
3210
+ }
3211
+ printResult(
3212
+ await getClient2().commerce.paymentSessions.confirm(data),
3213
+ getFormat2()
3214
+ );
3215
+ } catch (error) {
3216
+ exitWithError(error);
2177
3217
  }
2178
3218
  });
2179
3219
  }
2180
3220
 
3221
+ // src/lib/product-detail-options.ts
3222
+ function invalidOption(message, field, value) {
3223
+ const error = new Error(message);
3224
+ Object.assign(error, {
3225
+ type: "validation",
3226
+ code: "invalid_argument",
3227
+ field,
3228
+ detail: { message, value, field }
3229
+ });
3230
+ return error;
3231
+ }
3232
+ function parseLocale(value) {
3233
+ if (value == null) return void 0;
3234
+ if (value === "ko" || value === "en" || value === "all") return value;
3235
+ throw invalidOption("--locale must be one of: ko, en, all", "locale", value);
3236
+ }
3237
+ function parseFallbackLocale(value) {
3238
+ if (value == null) return void 0;
3239
+ if (value === "ko" || value === "en") return value;
3240
+ if (value === "none") return false;
3241
+ throw invalidOption(
3242
+ "--fallback-locale must be one of: ko, en, none",
3243
+ "fallbackLocale",
3244
+ value
3245
+ );
3246
+ }
3247
+ function productDetailParamsFromOptions(options) {
3248
+ const hasId = typeof options.id === "string" && options.id.length > 0;
3249
+ const hasSlug = typeof options.slug === "string" && options.slug.length > 0;
3250
+ if (hasId === hasSlug) {
3251
+ throw invalidOption(
3252
+ "Exactly one of --id or --slug is required.",
3253
+ "product",
3254
+ { id: options.id, slug: options.slug }
3255
+ );
3256
+ }
3257
+ const locale = parseLocale(options.locale);
3258
+ const fallbackLocale = parseFallbackLocale(options.fallbackLocale);
3259
+ const identifier = hasId ? { id: options.id } : { slug: options.slug };
3260
+ return {
3261
+ ...identifier,
3262
+ ...options.market !== void 0 ? { market: options.market } : {},
3263
+ ...locale !== void 0 ? { locale } : {},
3264
+ ...fallbackLocale !== void 0 ? { fallbackLocale } : {}
3265
+ };
3266
+ }
3267
+
2181
3268
  // src/commands/product.ts
2182
3269
  function registerProductCommands(program2, getClient2, getFormat2) {
2183
3270
  const product = program2.command("product").description("Product management");
3271
+ product.command("detail").description("Get shaped product detail by slug or ID").option("--slug <slug>", "Product slug").option("--id <id>", "Product ID").option("--market <market>", "Market slug").option("--locale <locale>", "Locale: ko, en, or all").option("--fallback-locale <locale>", "Fallback locale: ko, en, or none").action(async (opts) => {
3272
+ try {
3273
+ const params = productDetailParamsFromOptions(opts);
3274
+ const client = getClient2();
3275
+ const result = await client.commerce.product.detail(params);
3276
+ printResult(result, getFormat2());
3277
+ } catch (e) {
3278
+ exitWithError(e);
3279
+ }
3280
+ });
2184
3281
  product.command("upsert").description("Create or update a product graph (options, values, variants)").requiredOption("--input <json>", "Product upsert input (JSON)").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2185
3282
  try {
2186
3283
  const data = parseWithSchema(
@@ -2234,12 +3331,14 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
2234
3331
  "--order-amount <n>",
2235
3332
  "Order amount",
2236
3333
  (v) => parseInt(v, 10)
2237
- ).option("--shipping-policy-id <id>", "Shipping policy ID").option("--postal-code <code>", "Destination postal code").action(async (opts) => {
3334
+ ).option("--shipping-policy-id <id>", "Shipping policy ID").option("--country-code <code>", "Destination country code").option("--region-code <code>", "Destination region code").option("--postal-code <code>", "Destination postal code").action(async (opts) => {
2238
3335
  try {
2239
3336
  const client = getClient2();
2240
3337
  const result = await client.commerce.shipping.calculate({
2241
3338
  orderAmount: opts.orderAmount,
2242
3339
  ...opts.shippingPolicyId ? { shippingPolicyId: opts.shippingPolicyId } : {},
3340
+ ...opts.countryCode ? { countryCode: opts.countryCode } : {},
3341
+ ...opts.regionCode ? { regionCode: opts.regionCode } : {},
2243
3342
  ...opts.postalCode ? { postalCode: opts.postalCode } : {}
2244
3343
  });
2245
3344
  printResult(result, getFormat2());
@@ -2250,7 +3349,7 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
2250
3349
  }
2251
3350
 
2252
3351
  // src/commands/auth.ts
2253
- import { randomBytes } from "crypto";
3352
+ import { randomBytes as randomBytes2 } from "crypto";
2254
3353
  import { createServer } from "http";
2255
3354
  import { execFile, exec } from "child_process";
2256
3355
  import { platform } from "os";
@@ -2425,7 +3524,7 @@ Logged in successfully!`));
2425
3524
  }
2426
3525
  function registerAuthCommands(program2) {
2427
3526
  program2.command("login").description("Login via browser and store credentials").action(async () => {
2428
- const state = randomBytes(32).toString("hex");
3527
+ const state = randomBytes2(32).toString("hex");
2429
3528
  try {
2430
3529
  const { port } = await startAuthServer({
2431
3530
  state,
@@ -2514,7 +3613,7 @@ ${loginUrl}`));
2514
3613
  );
2515
3614
  process.exit(3);
2516
3615
  }
2517
- const state = randomBytes(32).toString("hex");
3616
+ const state = randomBytes2(32).toString("hex");
2518
3617
  const isLocal = !!opts.local;
2519
3618
  try {
2520
3619
  const { port } = await startAuthServer({
@@ -2613,9 +3712,15 @@ function registerFeatureCommands(program2, getClient2, getFormat2) {
2613
3712
  }
2614
3713
 
2615
3714
  // src/commands/agent.ts
3715
+ import { SERVER_COLLECTIONS as SERVER_COLLECTIONS4 } from "@01.software/sdk";
3716
+
3717
+ // src/lib/agent-plan-allowlist.ts
2616
3718
  import { SERVER_COLLECTIONS as SERVER_COLLECTIONS3 } from "@01.software/sdk";
2617
3719
 
2618
3720
  // src/lib/agent-output.ts
3721
+ import { randomBytes as randomBytes3 } from "crypto";
3722
+ import fs from "fs";
3723
+ import path from "path";
2619
3724
  var PLAN_AGENT_ERROR_CODES = /* @__PURE__ */ new Set([
2620
3725
  "PLAN_EXPIRED",
2621
3726
  "PLAN_MISMATCH",
@@ -2632,9 +3737,95 @@ function agentPlanError(code, message) {
2632
3737
  });
2633
3738
  return error;
2634
3739
  }
3740
+ var AGENT_OUTPUT_FILE_MODE = 384;
3741
+ var isWin32 = process.platform === "win32";
2635
3742
  function stringifyAgentJson(data, options = {}) {
2636
3743
  return options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
2637
3744
  }
3745
+ function invalidOutputTarget(message, outputFile) {
3746
+ const error = new Error(message);
3747
+ Object.assign(error, {
3748
+ type: "validation",
3749
+ code: "invalid_argument",
3750
+ field: "outputFile",
3751
+ detail: { message, field: "outputFile", value: outputFile }
3752
+ });
3753
+ return error;
3754
+ }
3755
+ function assertSafeAgentOutputTarget(outputFile) {
3756
+ let stat;
3757
+ try {
3758
+ stat = fs.lstatSync(outputFile);
3759
+ } catch (error) {
3760
+ if (error.code === "ENOENT") return;
3761
+ throw error;
3762
+ }
3763
+ if (stat.isSymbolicLink()) {
3764
+ throw invalidOutputTarget(
3765
+ `Refusing to write Agent output through a symlink: ${outputFile}.`,
3766
+ outputFile
3767
+ );
3768
+ }
3769
+ if (!stat.isFile()) {
3770
+ throw invalidOutputTarget(
3771
+ `Refusing to write Agent output to a non-regular file: ${outputFile}.`,
3772
+ outputFile
3773
+ );
3774
+ }
3775
+ if (!isWin32 && stat.nlink > 1) {
3776
+ throw invalidOutputTarget(
3777
+ `Refusing to write Agent output to a file with multiple hard links: ${outputFile}.`,
3778
+ outputFile
3779
+ );
3780
+ }
3781
+ }
3782
+ function writeAgentOutputFile(requestedPath, content) {
3783
+ const outputFile = path.resolve(requestedPath);
3784
+ assertSafeAgentOutputTarget(outputFile);
3785
+ const tempFile = path.join(
3786
+ path.dirname(outputFile),
3787
+ `.${path.basename(outputFile)}.${randomBytes3(12).toString("hex")}.tmp`
3788
+ );
3789
+ let tempFd;
3790
+ try {
3791
+ tempFd = fs.openSync(tempFile, "wx", AGENT_OUTPUT_FILE_MODE);
3792
+ if (!isWin32) {
3793
+ fs.fchmodSync(tempFd, AGENT_OUTPUT_FILE_MODE);
3794
+ }
3795
+ fs.writeFileSync(tempFd, content, { encoding: "utf8" });
3796
+ fs.closeSync(tempFd);
3797
+ tempFd = void 0;
3798
+ fs.renameSync(tempFile, outputFile);
3799
+ } catch (error) {
3800
+ if (tempFd !== void 0) {
3801
+ try {
3802
+ fs.closeSync(tempFd);
3803
+ } catch {
3804
+ }
3805
+ }
3806
+ try {
3807
+ fs.unlinkSync(tempFile);
3808
+ } catch {
3809
+ }
3810
+ throw error;
3811
+ }
3812
+ return {
3813
+ outputFile,
3814
+ bytes: Buffer.byteLength(content, "utf8")
3815
+ };
3816
+ }
3817
+ function resolveAgentPrintOptions(command) {
3818
+ const options = command.optsWithGlobals();
3819
+ return {
3820
+ pretty: Boolean(options.pretty),
3821
+ outputFile: options.outputFile
3822
+ };
3823
+ }
3824
+ function validateAgentOutputTarget(options) {
3825
+ if (options.outputFile !== void 0) {
3826
+ assertSafeAgentOutputTarget(path.resolve(options.outputFile));
3827
+ }
3828
+ }
2638
3829
  function asRecord(value) {
2639
3830
  return value && typeof value === "object" ? value : null;
2640
3831
  }
@@ -2660,6 +3851,20 @@ function isRawNotFound(error) {
2660
3851
  const raw = asRecord(error);
2661
3852
  return raw?.status === 404 || raw?.code === "not_found" || raw?.name === "GoneError";
2662
3853
  }
3854
+ function isSdkUsageLimitError(error) {
3855
+ const raw = asRecord(error);
3856
+ return raw?.name === "UsageLimitError" || raw?.code === "USAGE_LIMIT_ERROR";
3857
+ }
3858
+ function agentErrorEnvelope(code, message, retryable, retryAfter) {
3859
+ return {
3860
+ error: {
3861
+ code,
3862
+ message,
3863
+ retryable,
3864
+ ...typeof retryAfter === "number" ? { retryAfter } : {}
3865
+ }
3866
+ };
3867
+ }
2663
3868
  function agentErrorExitCode(code) {
2664
3869
  switch (code) {
2665
3870
  case "INVALID_INPUT":
@@ -2683,22 +3888,30 @@ function classifyAgentError(error) {
2683
3888
  const detailRecord = detail && typeof detail === "object" ? detail : null;
2684
3889
  const agentCode = raw?.agentCode ?? detailRecord?.agentCode;
2685
3890
  if (typeof agentCode === "string" && PLAN_AGENT_ERROR_CODES.has(agentCode)) {
2686
- return { error: { code: agentCode, message } };
3891
+ return agentErrorEnvelope(agentCode, message, false);
2687
3892
  }
2688
3893
  const adminError = classifyError(error);
2689
3894
  if (isRawNotFound(error) || adminError.code === "not_found") {
2690
- return { error: { code: "NOT_FOUND", message } };
3895
+ return agentErrorEnvelope("NOT_FOUND", message, false);
2691
3896
  }
2692
3897
  if (adminError.type === "permission") {
2693
- return { error: { code: "AUTH_FAILED", message } };
3898
+ return agentErrorEnvelope("AUTH_FAILED", message, false);
2694
3899
  }
2695
3900
  if (adminError.type === "validation" && adminError.code !== "unknown") {
2696
- return { error: { code: "INVALID_INPUT", message } };
3901
+ return agentErrorEnvelope("INVALID_INPUT", message, false);
2697
3902
  }
2698
- return { error: { code: "INTERNAL", message } };
3903
+ const retryable = !isSdkUsageLimitError(error) && (adminError.type === "network" || adminError.type === "degraded");
3904
+ const retryAfter = retryable && adminError.type === "degraded" && adminError.code === "rate_limited" ? adminError.retryAfter : void 0;
3905
+ return agentErrorEnvelope("INTERNAL", message, retryable, retryAfter);
2699
3906
  }
2700
3907
  function printAgentSuccess(data, options = {}) {
2701
- console.log(stringifyAgentJson(data, options));
3908
+ const output = stringifyAgentJson(data, options);
3909
+ if (options.outputFile !== void 0) {
3910
+ const receipt = writeAgentOutputFile(options.outputFile, output);
3911
+ console.log(JSON.stringify(receipt));
3912
+ return;
3913
+ }
3914
+ console.log(output);
2702
3915
  }
2703
3916
  function printAgentError(error, options = {}) {
2704
3917
  const envelope = classifyAgentError(error);
@@ -2711,25 +3924,77 @@ function exitWithAgentError(error, options = {}) {
2711
3924
  }
2712
3925
 
2713
3926
  // src/lib/agent-plan-allowlist.ts
2714
- var AGENT_PLAN_COLLECTIONS = [
2715
- "articles",
2716
- "article-categories",
2717
- "article-tags",
2718
- "links",
2719
- "link-categories",
2720
- "link-tags"
2721
- ];
2722
- var AGENT_PLAN_COLLECTION_SET = new Set(AGENT_PLAN_COLLECTIONS);
3927
+ var AGENT_PLAN_CAPABILITIES = {
3928
+ articles: ["create", "update", "delete"],
3929
+ "article-categories": ["create", "update", "delete"],
3930
+ "article-tags": ["create", "update", "delete"],
3931
+ links: ["create", "update", "delete"],
3932
+ "link-categories": ["create", "update", "delete"],
3933
+ "link-tags": ["create", "update", "delete"],
3934
+ documents: ["create", "update"],
3935
+ "document-categories": ["create", "update"],
3936
+ "document-types": ["create", "update"],
3937
+ canvases: ["create", "update", "delete"],
3938
+ "canvas-categories": ["create", "update", "delete"],
3939
+ "canvas-tags": ["create", "update", "delete"]
3940
+ };
3941
+ var SERVER_COLLECTION_SET = new Set(SERVER_COLLECTIONS3);
3942
+ var AGENT_PLAN_OPERATIONS = /* @__PURE__ */ new Set([
3943
+ "create",
3944
+ "update",
3945
+ "delete"
3946
+ ]);
3947
+ function assertAgentPlanCapabilityMatrix(matrix) {
3948
+ for (const [collection, operations] of Object.entries(matrix)) {
3949
+ if (!SERVER_COLLECTION_SET.has(collection)) {
3950
+ throw new Error(
3951
+ `Agent plan capability collection "${collection}" is not a ServerCollection.`
3952
+ );
3953
+ }
3954
+ if (operations.length === 0) {
3955
+ throw new Error(
3956
+ `Agent plan capability collection "${collection}" has no operations.`
3957
+ );
3958
+ }
3959
+ const seen = /* @__PURE__ */ new Set();
3960
+ for (const operation of operations) {
3961
+ if (!AGENT_PLAN_OPERATIONS.has(operation)) {
3962
+ throw new Error(
3963
+ `Agent plan capability operation "${operation}" is invalid for "${collection}".`
3964
+ );
3965
+ }
3966
+ if (seen.has(operation)) {
3967
+ throw new Error(
3968
+ `Agent plan capability operation "${operation}" is duplicated for "${collection}".`
3969
+ );
3970
+ }
3971
+ seen.add(operation);
3972
+ }
3973
+ }
3974
+ }
3975
+ assertAgentPlanCapabilityMatrix(AGENT_PLAN_CAPABILITIES);
2723
3976
  function isAgentPlanCollection(collection) {
2724
- return AGENT_PLAN_COLLECTION_SET.has(collection);
3977
+ return Object.prototype.hasOwnProperty.call(
3978
+ AGENT_PLAN_CAPABILITIES,
3979
+ collection
3980
+ );
3981
+ }
3982
+ function getAgentPlanOperations(collection) {
3983
+ return isAgentPlanCollection(collection) ? AGENT_PLAN_CAPABILITIES[collection] : [];
2725
3984
  }
2726
- function assertAgentPlanCollection(collection) {
2727
- if (isAgentPlanCollection(collection)) {
3985
+ function isAgentPlanCapability(collection, operation) {
3986
+ if (!isAgentPlanCollection(collection)) return false;
3987
+ return AGENT_PLAN_CAPABILITIES[collection].includes(
3988
+ operation
3989
+ );
3990
+ }
3991
+ function assertAgentPlanCapability(collection, operation) {
3992
+ if (isAgentPlanCapability(collection, operation)) {
2728
3993
  return collection;
2729
3994
  }
2730
3995
  throw agentPlanError(
2731
3996
  "UNSUPPORTED_OPERATION",
2732
- `Collection "${collection}" is not supported for agent plan mutations.`
3997
+ `Operation "${operation}" is not supported for collection "${collection}" in agent plan mutations.`
2733
3998
  );
2734
3999
  }
2735
4000
 
@@ -2832,6 +4097,12 @@ function parseStoredAgentPlan(raw, planId) {
2832
4097
  if (operation !== "create" && operation !== "update" && operation !== "delete") {
2833
4098
  throw agentPlanError("PLAN_MISMATCH", "Stored plan has invalid operation.");
2834
4099
  }
4100
+ if (!isAgentPlanCapability(collection, operation)) {
4101
+ throw agentPlanError(
4102
+ "PLAN_MISMATCH",
4103
+ "Stored plan has unsupported collection operation."
4104
+ );
4105
+ }
2835
4106
  const payload = record.payload == null ? void 0 : typeof record.payload === "object" && !Array.isArray(record.payload) ? record.payload : (() => {
2836
4107
  throw agentPlanError("PLAN_MISMATCH", "Stored plan payload is invalid.");
2837
4108
  })();
@@ -2856,6 +4127,12 @@ function parseStoredAgentPlan(raw, planId) {
2856
4127
  };
2857
4128
  }
2858
4129
  function assertStoredPlanHashMatches(plan) {
4130
+ if (!isAgentPlanCapability(plan.collection, plan.operation)) {
4131
+ throw agentPlanError(
4132
+ "PLAN_MISMATCH",
4133
+ "Stored plan has unsupported collection operation."
4134
+ );
4135
+ }
2859
4136
  const expected = hashAgentPlanEnvelope({
2860
4137
  operation: plan.operation,
2861
4138
  collection: plan.collection,
@@ -3076,10 +4353,10 @@ function isSecretFieldName(key) {
3076
4353
  const snake = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
3077
4354
  return SECRET_KEY.test(snake);
3078
4355
  }
3079
- function assertNoSecretFields(value, path = "") {
4356
+ function assertNoSecretFields(value, path2 = "") {
3080
4357
  if (Array.isArray(value)) {
3081
4358
  value.forEach((item, index) => {
3082
- assertNoSecretFields(item, `${path}[${index}]`);
4359
+ assertNoSecretFields(item, `${path2}[${index}]`);
3083
4360
  });
3084
4361
  return;
3085
4362
  }
@@ -3087,7 +4364,7 @@ function assertNoSecretFields(value, path = "") {
3087
4364
  return;
3088
4365
  }
3089
4366
  for (const [key, child] of Object.entries(value)) {
3090
- const fieldPath = path ? `${path}.${key}` : key;
4367
+ const fieldPath = path2 ? `${path2}.${key}` : key;
3091
4368
  if (isSecretFieldName(key)) {
3092
4369
  throw agentPlanError(
3093
4370
  "INVALID_INPUT",
@@ -3129,7 +4406,7 @@ function buildConfirmCommand(planToken, planHash) {
3129
4406
  }
3130
4407
 
3131
4408
  // src/commands/agent-plan.ts
3132
- function invalidInput(message, field) {
4409
+ function invalidInput2(message, field) {
3133
4410
  const error = new Error(message);
3134
4411
  Object.assign(error, {
3135
4412
  type: "validation",
@@ -3143,7 +4420,7 @@ function handleCommanderError(error) {
3143
4420
  if (error.code === "commander.helpDisplayed") {
3144
4421
  process.exit(error.exitCode);
3145
4422
  }
3146
- exitWithAgentError(invalidInput(error.message, "command"));
4423
+ exitWithAgentError(invalidInput2(error.message, "command"));
3147
4424
  }
3148
4425
  function configureAgentParser(command) {
3149
4426
  command.configureOutput({
@@ -3161,9 +4438,12 @@ async function captureBaseUpdatedAt(client, collection, documentId) {
3161
4438
  return readDocumentUpdatedAt(doc);
3162
4439
  }
3163
4440
  async function runPlanAction(input) {
3164
- const collection = assertAgentPlanCollection(input.collection);
4441
+ const collection = assertAgentPlanCapability(
4442
+ input.collection,
4443
+ input.operation
4444
+ );
3165
4445
  if (input.operation !== "create" && !input.documentId) {
3166
- throw invalidInput("Missing required argument: id", "id");
4446
+ throw invalidInput2("Missing required argument: id", "id");
3167
4447
  }
3168
4448
  let payload;
3169
4449
  if (input.operation === "create" || input.operation === "update") {
@@ -3225,7 +4505,7 @@ async function runPlanAction(input) {
3225
4505
  documentId: stored.documentId,
3226
4506
  confirmCommand: buildConfirmCommand(planToken, stored.planHash)
3227
4507
  },
3228
- { pretty: Boolean(input.pretty) }
4508
+ input.outputOptions
3229
4509
  );
3230
4510
  }
3231
4511
  function registerAgentPlanCommands(agent, getClient2) {
@@ -3233,63 +4513,83 @@ function registerAgentPlanCommands(agent, getClient2) {
3233
4513
  agent.command("plan").description("Plan a mutation without executing")
3234
4514
  );
3235
4515
  configureAgentParser(
3236
- plan.command("create <collection>").description("Plan a document create").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented JSON").action(
3237
- async (collection, opts) => {
4516
+ plan.command("create <collection>").description("Plan a document create").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented success JSON").option(
4517
+ "--output-file <path>",
4518
+ "Write success JSON to a secure file and print a receipt"
4519
+ ).action(
4520
+ async (collection, opts, command) => {
4521
+ const outputOptions = resolveAgentPrintOptions(command);
3238
4522
  try {
4523
+ validateAgentOutputTarget(outputOptions);
3239
4524
  await runPlanAction({
3240
4525
  operation: "create",
3241
4526
  collection,
3242
4527
  dataStdin: opts.dataStdin,
3243
4528
  dataFile: opts.dataFile,
3244
- pretty: opts.pretty,
4529
+ outputOptions,
3245
4530
  getClient: getClient2
3246
4531
  });
3247
4532
  } catch (error) {
3248
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4533
+ exitWithAgentError(error, outputOptions);
3249
4534
  }
3250
4535
  }
3251
4536
  )
3252
4537
  );
3253
4538
  configureAgentParser(
3254
- plan.command("update <collection> <id>").description("Plan a document update").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented JSON").action(
3255
- async (collection, id, opts) => {
4539
+ plan.command("update <collection> <id>").description("Plan a document update").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented success JSON").option(
4540
+ "--output-file <path>",
4541
+ "Write success JSON to a secure file and print a receipt"
4542
+ ).action(
4543
+ async (collection, id, opts, command) => {
4544
+ const outputOptions = resolveAgentPrintOptions(command);
3256
4545
  try {
4546
+ validateAgentOutputTarget(outputOptions);
3257
4547
  await runPlanAction({
3258
4548
  operation: "update",
3259
4549
  collection,
3260
4550
  documentId: id,
3261
4551
  dataStdin: opts.dataStdin,
3262
4552
  dataFile: opts.dataFile,
3263
- pretty: opts.pretty,
4553
+ outputOptions,
3264
4554
  getClient: getClient2
3265
4555
  });
3266
4556
  } catch (error) {
3267
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4557
+ exitWithAgentError(error, outputOptions);
3268
4558
  }
3269
4559
  }
3270
4560
  )
3271
4561
  );
3272
4562
  configureAgentParser(
3273
- plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented JSON").action(
3274
- async (collection, id, opts) => {
4563
+ plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented success JSON").option(
4564
+ "--output-file <path>",
4565
+ "Write success JSON to a secure file and print a receipt"
4566
+ ).action(
4567
+ async (collection, id, opts, command) => {
4568
+ const outputOptions = resolveAgentPrintOptions(command);
3275
4569
  try {
4570
+ validateAgentOutputTarget(outputOptions);
3276
4571
  await runPlanAction({
3277
4572
  operation: "delete",
3278
4573
  collection,
3279
4574
  documentId: id,
3280
- pretty: opts.pretty,
4575
+ outputOptions,
3281
4576
  getClient: getClient2
3282
4577
  });
3283
4578
  } catch (error) {
3284
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4579
+ exitWithAgentError(error, outputOptions);
3285
4580
  }
3286
4581
  }
3287
4582
  )
3288
4583
  );
3289
4584
  configureAgentParser(
3290
- agent.command("confirm <planToken>").description("Execute a previously planned mutation").requiredOption("--hash <planHash>", "Plan hash from plan output").option("--pretty", "Print 2-space indented JSON").action(
3291
- async (planTokenRaw, opts) => {
4585
+ agent.command("confirm <planToken>").description("Execute a previously planned mutation").requiredOption("--hash <planHash>", "Plan hash from plan output").option("--pretty", "Print 2-space indented success JSON").option(
4586
+ "--output-file <path>",
4587
+ "Write success JSON to a secure file and print a receipt"
4588
+ ).action(
4589
+ async (planTokenRaw, opts, command) => {
4590
+ const outputOptions = resolveAgentPrintOptions(command);
3292
4591
  try {
4592
+ validateAgentOutputTarget(outputOptions);
3293
4593
  const token = decodePlanToken(planTokenRaw);
3294
4594
  if (token.planHash !== opts.hash) {
3295
4595
  throw agentPlanError(
@@ -3335,10 +4635,10 @@ function registerAgentPlanCommands(agent, getClient2) {
3335
4635
  id: doc && typeof doc === "object" && "id" in doc ? String(doc.id) : plan2.documentId,
3336
4636
  doc
3337
4637
  },
3338
- { pretty: Boolean(opts.pretty) }
4638
+ outputOptions
3339
4639
  );
3340
4640
  } catch (error) {
3341
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4641
+ exitWithAgentError(error, outputOptions);
3342
4642
  }
3343
4643
  }
3344
4644
  )
@@ -3354,7 +4654,7 @@ function buildStableAgentFields() {
3354
4654
  updatedAt: { type: "string", queryable: true }
3355
4655
  };
3356
4656
  }
3357
- function invalidInput2(message, field, value) {
4657
+ function invalidInput3(message, field, value) {
3358
4658
  const error = new Error(message);
3359
4659
  Object.assign(error, {
3360
4660
  type: "validation",
@@ -3367,13 +4667,13 @@ function invalidInput2(message, field, value) {
3367
4667
  function parsePositiveInteger(value, field) {
3368
4668
  if (value == null) return void 0;
3369
4669
  if (/^[1-9]\d*$/.test(value)) return Number(value);
3370
- throw invalidInput2(`--${field} must be a positive integer`, field, value);
4670
+ throw invalidInput3(`--${field} must be a positive integer`, field, value);
3371
4671
  }
3372
4672
  function handleCommanderError2(error) {
3373
4673
  if (error.code === "commander.helpDisplayed") {
3374
4674
  process.exit(error.exitCode);
3375
4675
  }
3376
- exitWithAgentError(invalidInput2(error.message, "command"));
4676
+ exitWithAgentError(invalidInput3(error.message, "command"));
3377
4677
  }
3378
4678
  function configureAgentParser2(command) {
3379
4679
  command.configureOutput({
@@ -3394,23 +4694,27 @@ function parseWhere(value) {
3394
4694
  return parsed;
3395
4695
  }
3396
4696
  } catch {
3397
- throw invalidInput2("--where must be a JSON object", "where", value);
4697
+ throw invalidInput3("--where must be a JSON object", "where", value);
3398
4698
  }
3399
- throw invalidInput2("--where must be a JSON object", "where", value);
4699
+ throw invalidInput3("--where must be a JSON object", "where", value);
3400
4700
  }
3401
4701
  var AGENT_READ_OPERATIONS = [
3402
4702
  "query",
3403
4703
  "get"
3404
4704
  ];
3405
- var AGENT_PLAN_OPERATIONS = [
3406
- "plan:create",
3407
- "plan:update",
3408
- "plan:delete"
4705
+ var AGENT_PRODUCT_OPERATIONS = [
4706
+ "detail"
3409
4707
  ];
3410
4708
  function buildAgentManifest() {
3411
4709
  const collections = {};
3412
- for (const collection of SERVER_COLLECTIONS3) {
3413
- const operations = isAgentPlanCollection(collection) ? [...AGENT_READ_OPERATIONS, ...AGENT_PLAN_OPERATIONS] : [...AGENT_READ_OPERATIONS];
4710
+ for (const collection of SERVER_COLLECTIONS4) {
4711
+ const operations = [
4712
+ ...AGENT_READ_OPERATIONS,
4713
+ ...collection === "products" ? AGENT_PRODUCT_OPERATIONS : [],
4714
+ ...getAgentPlanOperations(collection).map(
4715
+ (operation) => `plan:${operation}`
4716
+ )
4717
+ ];
3414
4718
  collections[collection] = {
3415
4719
  operations,
3416
4720
  fields: buildStableAgentFields()
@@ -3418,6 +4722,10 @@ function buildAgentManifest() {
3418
4722
  }
3419
4723
  return {
3420
4724
  version: AGENT_PROTOCOL_VERSION,
4725
+ discovery: {
4726
+ context: { counts: true },
4727
+ schema: { mode: "effective" }
4728
+ },
3421
4729
  collections
3422
4730
  };
3423
4731
  }
@@ -3426,27 +4734,75 @@ function validateAgentCollection(collection) {
3426
4734
  return validateCollection(collection);
3427
4735
  } catch (error) {
3428
4736
  const message = error instanceof Error ? error.message : `Unknown collection "${collection}".`;
3429
- throw invalidInput2(message, "collection", collection);
4737
+ throw invalidInput3(message, "collection", collection);
3430
4738
  }
3431
4739
  }
3432
4740
  function registerAgentCommands(program2, getClient2) {
3433
4741
  const agent = configureAgentParser2(
3434
- program2.command("agent").description("Machine-stable CLI namespace for coding agents").addHelpText(
4742
+ program2.command("agent").description("Machine-stable CLI namespace for coding agents").option("--pretty", "Print 2-space indented success JSON").option(
4743
+ "--output-file <path>",
4744
+ "Write success JSON to a secure file and print a receipt"
4745
+ ).addHelpText(
3435
4746
  "after",
3436
4747
  "\nContract: docs/agent-cli-contract.md\nJSON only on stdout; no TTY effects."
3437
4748
  )
3438
4749
  );
3439
4750
  configureAgentParser2(
3440
- agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented JSON").action((opts) => {
3441
- printAgentSuccess(buildAgentManifest(), {
3442
- pretty: Boolean(opts.pretty)
3443
- });
4751
+ agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented success JSON").option(
4752
+ "--output-file <path>",
4753
+ "Write success JSON to a secure file and print a receipt"
4754
+ ).action((_opts, command) => {
4755
+ const outputOptions = resolveAgentPrintOptions(command);
4756
+ try {
4757
+ validateAgentOutputTarget(outputOptions);
4758
+ printAgentSuccess(buildAgentManifest(), outputOptions);
4759
+ } catch (error) {
4760
+ exitWithAgentError(error, outputOptions);
4761
+ }
4762
+ })
4763
+ );
4764
+ configureAgentParser2(
4765
+ agent.command("context").description("Get the active tenant context").option("--counts", "Include collection document counts").option("--pretty", "Print 2-space indented success JSON").option(
4766
+ "--output-file <path>",
4767
+ "Write success JSON to a secure file and print a receipt"
4768
+ ).action(async (opts, command) => {
4769
+ const outputOptions = resolveAgentPrintOptions(command);
4770
+ try {
4771
+ validateAgentOutputTarget(outputOptions);
4772
+ const client = getClient2();
4773
+ const result = opts.counts ? await client.tenant.context({ includeCounts: true }) : await client.tenant.context();
4774
+ printAgentSuccess(result, outputOptions);
4775
+ } catch (error) {
4776
+ exitWithAgentError(error, outputOptions);
4777
+ }
4778
+ })
4779
+ );
4780
+ configureAgentParser2(
4781
+ agent.command("schema <collection>").description("Get the effective field schema for a collection").option("--pretty", "Print 2-space indented success JSON").option(
4782
+ "--output-file <path>",
4783
+ "Write success JSON to a secure file and print a receipt"
4784
+ ).action(async (collection, _opts, command) => {
4785
+ const outputOptions = resolveAgentPrintOptions(command);
4786
+ try {
4787
+ validateAgentOutputTarget(outputOptions);
4788
+ const col = validateAgentCollection(collection);
4789
+ const client = getClient2();
4790
+ const result = await client.tenant.collectionSchema(col);
4791
+ printAgentSuccess(result, outputOptions);
4792
+ } catch (error) {
4793
+ exitWithAgentError(error, outputOptions);
4794
+ }
3444
4795
  })
3445
4796
  );
3446
4797
  configureAgentParser2(
3447
- agent.command("query <collection>").description("Query documents from a collection").option("--where <json>", "Filter conditions as a JSON object").option("--limit <n>", "Max results").option("--page <n>", "Page number").option("--sort <field>", "Sort field; prefix with - for descending").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented JSON").action(
3448
- async (collection, opts) => {
4798
+ agent.command("query <collection>").description("Query documents from a collection").option("--where <json>", "Filter conditions as a JSON object").option("--limit <n>", "Max results").option("--page <n>", "Page number").option("--sort <field>", "Sort field; prefix with - for descending").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented success JSON").option(
4799
+ "--output-file <path>",
4800
+ "Write success JSON to a secure file and print a receipt"
4801
+ ).action(
4802
+ async (collection, opts, command) => {
4803
+ const outputOptions = resolveAgentPrintOptions(command);
3449
4804
  try {
4805
+ validateAgentOutputTarget(outputOptions);
3450
4806
  const col = validateAgentCollection(collection);
3451
4807
  const options = {};
3452
4808
  const where = parseWhere(opts.where);
@@ -3459,28 +4815,55 @@ function registerAgentCommands(program2, getClient2) {
3459
4815
  if (opts.select) options.select = parseSelect(opts.select);
3460
4816
  const client = getClient2();
3461
4817
  const result = await client.collections.from(col).find(options);
3462
- printAgentSuccess(result, { pretty: Boolean(opts.pretty) });
4818
+ printAgentSuccess(result, outputOptions);
3463
4819
  } catch (error) {
3464
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4820
+ exitWithAgentError(error, outputOptions);
3465
4821
  }
3466
4822
  }
3467
4823
  )
3468
4824
  );
3469
4825
  configureAgentParser2(
3470
- agent.command("get <collection> [id]").description("Get a document by ID").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented JSON").action(
3471
- async (collection, id, opts) => {
4826
+ agent.command("get <collection> [id]").description("Get a document by ID").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented success JSON").option(
4827
+ "--output-file <path>",
4828
+ "Write success JSON to a secure file and print a receipt"
4829
+ ).action(
4830
+ async (collection, id, opts, command) => {
4831
+ const outputOptions = resolveAgentPrintOptions(command);
3472
4832
  try {
4833
+ validateAgentOutputTarget(outputOptions);
3473
4834
  const col = validateAgentCollection(collection);
3474
4835
  if (!id) {
3475
- throw invalidInput2("Missing required argument: id", "id");
4836
+ throw invalidInput3("Missing required argument: id", "id");
3476
4837
  }
3477
4838
  const options = {};
3478
4839
  if (opts.select) options.select = parseSelect(opts.select);
3479
4840
  const client = getClient2();
3480
4841
  const result = await client.collections.from(col).findById(id, options);
3481
- printAgentSuccess(result, { pretty: Boolean(opts.pretty) });
4842
+ printAgentSuccess(result, outputOptions);
4843
+ } catch (error) {
4844
+ exitWithAgentError(error, outputOptions);
4845
+ }
4846
+ }
4847
+ )
4848
+ );
4849
+ const product = configureAgentParser2(
4850
+ agent.command("product").description("Shaped product reads")
4851
+ );
4852
+ configureAgentParser2(
4853
+ product.command("detail").description("Get shaped product detail by slug or ID").option("--slug <slug>", "Product slug").option("--id <id>", "Product ID").option("--market <market>", "Market slug").option("--locale <locale>", "Locale: ko, en, or all").option("--fallback-locale <locale>", "Fallback locale: ko, en, or none").option("--pretty", "Print 2-space indented success JSON").option(
4854
+ "--output-file <path>",
4855
+ "Write success JSON to a secure file and print a receipt"
4856
+ ).action(
4857
+ async (opts, command) => {
4858
+ const outputOptions = resolveAgentPrintOptions(command);
4859
+ try {
4860
+ validateAgentOutputTarget(outputOptions);
4861
+ const params = productDetailParamsFromOptions(opts);
4862
+ const client = getClient2();
4863
+ const result = await client.commerce.product.detail(params);
4864
+ printAgentSuccess(result, outputOptions);
3482
4865
  } catch (error) {
3483
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4866
+ exitWithAgentError(error, outputOptions);
3484
4867
  }
3485
4868
  }
3486
4869
  )
@@ -3510,7 +4893,7 @@ registerOrderCommands(program, getClient, getFormat);
3510
4893
  registerReturnCommands(program, getClient, getFormat);
3511
4894
  registerCartCommands(program, getClient, getFormat);
3512
4895
  registerStockCommands(program, getClient, getFormat);
3513
- registerTransactionCommands(program, getClient, getFormat);
4896
+ registerCommerceSessionCommands(program, getClient, getFormat);
3514
4897
  registerProductCommands(program, getClient, getFormat);
3515
4898
  registerDiscountCommands(program, getClient, getFormat);
3516
4899
  registerShippingCommands(program, getClient, getFormat);