@01.software/cli 0.16.0 → 0.17.1
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 +2133 -719
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
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
|
-
|
|
27
|
+
renameSync,
|
|
20
28
|
unlinkSync,
|
|
29
|
+
writeFileSync,
|
|
21
30
|
appendFileSync
|
|
22
31
|
} from "fs";
|
|
23
|
-
import {
|
|
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
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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);
|
|
126
355
|
}
|
|
127
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");
|
|
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}`);
|
|
442
|
+
}
|
|
128
443
|
|
|
129
444
|
// src/lib/output.ts
|
|
130
445
|
import pc from "picocolors";
|
|
131
446
|
|
|
132
|
-
//
|
|
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
|
|
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}}
|
|
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,
|
|
556
|
-
const message = expectedKind === "object" ? t("InvalidJsonObject", { label }) : expectedKind === "array" ? t("InvalidJsonArray", { label }) : t("InvalidJsonValue", { label
|
|
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
|
-
//
|
|
562
|
-
//
|
|
563
|
-
//
|
|
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,
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
957
|
-
import { z as
|
|
1254
|
+
// ../../internal/contracts/src/ecommerce/index.ts
|
|
1255
|
+
import { z as z7 } from "zod";
|
|
958
1256
|
|
|
959
|
-
//
|
|
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:
|
|
1050
|
-
compareAtPrice:
|
|
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,1035 @@ var ProductUpsertSchema = ProductUpsertObjectSchema.superRefine(
|
|
|
1109
1429
|
}
|
|
1110
1430
|
);
|
|
1111
1431
|
|
|
1112
|
-
//
|
|
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
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
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
|
+
});
|
|
1122
1479
|
});
|
|
1123
|
-
var
|
|
1124
|
-
|
|
1125
|
-
email: z3.string().email("Invalid email format"),
|
|
1126
|
-
phone: z3.string().optional()
|
|
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"
|
|
1127
1482
|
});
|
|
1128
|
-
var
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
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) {
|
|
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) {
|
|
1139
1488
|
ctx.addIssue({
|
|
1140
|
-
code:
|
|
1141
|
-
|
|
1142
|
-
|
|
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`
|
|
1143
1494
|
});
|
|
1144
1495
|
}
|
|
1145
|
-
|
|
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) {
|
|
1146
1677
|
ctx.addIssue({
|
|
1147
|
-
code:
|
|
1148
|
-
path: ["
|
|
1149
|
-
message: "
|
|
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"
|
|
1150
1689
|
});
|
|
1151
1690
|
}
|
|
1152
1691
|
});
|
|
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
|
+
}
|
|
1744
|
+
});
|
|
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) {
|
|
1759
|
+
ctx.addIssue({
|
|
1760
|
+
code: "custom",
|
|
1761
|
+
path: ["cart", "revision"],
|
|
1762
|
+
message: "a newly created Cart must start at revision 0"
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
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) {
|
|
1769
|
+
ctx.addIssue({
|
|
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`
|
|
1775
|
+
});
|
|
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
|
|
1831
|
+
});
|
|
1153
1832
|
|
|
1154
|
-
//
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
"
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
"
|
|
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
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
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 customPaymentProviderSchema = z5.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9_-]{0,63}$/i, "provider must be a slug");
|
|
1895
|
+
var customPaymentIdentitySchema = z5.string().trim().min(1);
|
|
1896
|
+
var customPaymentCurrencyInputSchema = z5.string().trim().min(1).regex(/^[A-Za-z]{3}$/, "currency must be a three-letter ISO 4217 code").transform((currency) => currency.toUpperCase());
|
|
1897
|
+
var createCustomPaymentSessionBodySchema = z5.object({
|
|
1898
|
+
provider: customPaymentProviderSchema,
|
|
1899
|
+
checkoutToken: commerceCapabilitySchema
|
|
1900
|
+
}).strict();
|
|
1901
|
+
var createCustomPaymentSessionParamsSchema = createCustomPaymentSessionBodySchema.extend({
|
|
1902
|
+
idempotencyKey: commerceIdempotencyKeySchema
|
|
1903
|
+
});
|
|
1904
|
+
var customPaymentSessionSchema = z5.object({
|
|
1905
|
+
id: commerceResourceIdSchema,
|
|
1906
|
+
provider: customPaymentProviderSchema,
|
|
1907
|
+
providerSessionId: customPaymentIdentitySchema,
|
|
1908
|
+
money: moneySchema
|
|
1909
|
+
}).strict();
|
|
1910
|
+
var confirmCustomPaymentSessionBodySchema = z5.object({
|
|
1911
|
+
provider: customPaymentProviderSchema,
|
|
1912
|
+
providerSessionId: customPaymentIdentitySchema,
|
|
1913
|
+
providerPaymentId: customPaymentIdentitySchema,
|
|
1914
|
+
providerEventId: customPaymentIdentitySchema,
|
|
1915
|
+
amount: z5.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
1916
|
+
currency: customPaymentCurrencyInputSchema,
|
|
1917
|
+
occurredAt: customPaymentIdentitySchema
|
|
1918
|
+
}).strict();
|
|
1919
|
+
var confirmCustomPaymentSessionResponseSchema = z5.object({
|
|
1920
|
+
orderId: commerceResourceIdSchema,
|
|
1921
|
+
transactionId: commerceResourceIdSchema,
|
|
1922
|
+
status: z5.literal("paid"),
|
|
1923
|
+
alreadyConfirmed: z5.boolean().optional()
|
|
1924
|
+
}).strict();
|
|
1925
|
+
var paymentNextActionSchema = z5.object({
|
|
1926
|
+
type: z5.literal("toss_widget"),
|
|
1927
|
+
clientKey: z5.string().min(1).max(255),
|
|
1928
|
+
paymentMethodVariantKey: z5.string().min(1).max(255),
|
|
1929
|
+
agreementVariantKey: z5.string().min(1).max(255),
|
|
1930
|
+
providerOrderId: tossProviderOrderIdSchema,
|
|
1931
|
+
providerCustomerKey: tossProviderCustomerKeySchema,
|
|
1932
|
+
orderName: z5.string().min(1).max(200),
|
|
1933
|
+
customerName: z5.string().min(1).max(200).optional(),
|
|
1934
|
+
customerEmail: z5.string().email().max(254).optional()
|
|
1935
|
+
}).strict();
|
|
1936
|
+
var PAYMENT_SESSION_ROLES = [
|
|
1937
|
+
"active",
|
|
1938
|
+
"canonical_candidate",
|
|
1939
|
+
"canonical",
|
|
1940
|
+
"residual",
|
|
1941
|
+
"historical"
|
|
1942
|
+
];
|
|
1943
|
+
var paymentSessionRoleSchema = z5.enum(PAYMENT_SESSION_ROLES);
|
|
1944
|
+
var PAYMENT_PROVIDER_STATUSES = [
|
|
1945
|
+
"requires_payment_method",
|
|
1946
|
+
"requires_action",
|
|
1947
|
+
"requires_confirmation",
|
|
1948
|
+
"initializing",
|
|
1949
|
+
"processing",
|
|
1950
|
+
"authorized",
|
|
1951
|
+
"canceling",
|
|
1952
|
+
"unknown",
|
|
1953
|
+
"captured",
|
|
1954
|
+
"failed",
|
|
1198
1955
|
"canceled"
|
|
1199
|
-
]
|
|
1200
|
-
var
|
|
1201
|
-
|
|
1202
|
-
"
|
|
1203
|
-
"
|
|
1204
|
-
"
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
"
|
|
1208
|
-
"
|
|
1209
|
-
"
|
|
1210
|
-
"
|
|
1211
|
-
"
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1956
|
+
];
|
|
1957
|
+
var paymentProviderStatusSchema = z5.enum(PAYMENT_PROVIDER_STATUSES);
|
|
1958
|
+
var PAYMENT_RECONCILIATION_REASONS = [
|
|
1959
|
+
"provider_outcome_unknown",
|
|
1960
|
+
"local_commit_ambiguous",
|
|
1961
|
+
"amount_mismatch",
|
|
1962
|
+
"currency_mismatch",
|
|
1963
|
+
"excess_capture_pending_order",
|
|
1964
|
+
"inventory_unavailable",
|
|
1965
|
+
"admission_unavailable",
|
|
1966
|
+
"excess_capture",
|
|
1967
|
+
"late_capture",
|
|
1968
|
+
"cross_checkout_duplicate"
|
|
1969
|
+
];
|
|
1970
|
+
var paymentReconciliationReasonSchema = z5.enum(
|
|
1971
|
+
PAYMENT_RECONCILIATION_REASONS
|
|
1972
|
+
);
|
|
1973
|
+
function requiredReconciliationSchema(reasons) {
|
|
1974
|
+
const reasonSchema = z5.enum(reasons);
|
|
1975
|
+
return z5.object({
|
|
1976
|
+
status: z5.literal("required"),
|
|
1977
|
+
reasons: z5.array(reasonSchema).min(1).readonly()
|
|
1978
|
+
}).strict().superRefine((value, ctx) => {
|
|
1979
|
+
const indexes = new Map(reasons.map((reason, index) => [reason, index]));
|
|
1980
|
+
let previous = -1;
|
|
1981
|
+
value.reasons.forEach((reason, index) => {
|
|
1982
|
+
const current = indexes.get(reason) ?? -1;
|
|
1983
|
+
if (current <= previous) {
|
|
1984
|
+
ctx.addIssue({
|
|
1985
|
+
code: "custom",
|
|
1986
|
+
path: ["reasons", index],
|
|
1987
|
+
message: "reconciliation reasons must be unique and canonical-sorted"
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
previous = current;
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
var noReconciliationSchema = z5.object({ status: z5.literal("none") }).strict();
|
|
1995
|
+
var publicCaptureEvidenceSchema = z5.object({
|
|
1996
|
+
amountMinor: z5.number().int().nonnegative(),
|
|
1997
|
+
currency: currencyCodeSchema,
|
|
1998
|
+
observedAt: z5.string().datetime({ offset: true })
|
|
1999
|
+
}).strict();
|
|
2000
|
+
var reconciliationRefundEvidenceSchema = z5.object({
|
|
2001
|
+
providerRefundId: z5.string().min(1),
|
|
2002
|
+
money: moneySchema,
|
|
2003
|
+
observedAt: z5.string().datetime({ offset: true })
|
|
2004
|
+
}).strict();
|
|
2005
|
+
var PAYMENT_FAILURE_REASONS = [
|
|
2006
|
+
"payment_declined",
|
|
2007
|
+
"authentication_failed",
|
|
2008
|
+
"authorization_expired",
|
|
2009
|
+
"payment_method_unavailable",
|
|
2010
|
+
"provider_terminal_failure",
|
|
2011
|
+
"unknown_provider_terminal_failure"
|
|
2012
|
+
];
|
|
2013
|
+
var paymentFailureReasonSchema = z5.enum(PAYMENT_FAILURE_REASONS);
|
|
2014
|
+
var orderReferenceSchema = z5.object({
|
|
2015
|
+
id: z5.string().min(1),
|
|
2016
|
+
orderNumber: z5.string().min(1)
|
|
2017
|
+
}).strict();
|
|
2018
|
+
var paymentSessionBaseShape = {
|
|
2019
|
+
id: z5.string().min(1),
|
|
2020
|
+
money: moneySchema,
|
|
2021
|
+
expiresAt: z5.string().datetime({ offset: true }).nullable()
|
|
2022
|
+
};
|
|
2023
|
+
var suppressedNotReadyShape = {
|
|
2024
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2025
|
+
finalizationStatus: z5.literal("not_ready"),
|
|
2026
|
+
reconciliation: noReconciliationSchema,
|
|
2027
|
+
order: z5.null()
|
|
2028
|
+
};
|
|
2029
|
+
function stateSchemas(nextAction) {
|
|
2030
|
+
return [
|
|
2031
|
+
z5.object({
|
|
2032
|
+
providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
|
|
2033
|
+
sessionRole: z5.literal("active"),
|
|
2034
|
+
presentationStatus: z5.literal("actionable"),
|
|
2035
|
+
finalizationStatus: z5.literal("not_ready"),
|
|
2036
|
+
nextAction,
|
|
2037
|
+
reconciliation: noReconciliationSchema,
|
|
2038
|
+
order: z5.null()
|
|
2039
|
+
}),
|
|
2040
|
+
z5.object({
|
|
2041
|
+
providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
|
|
2042
|
+
sessionRole: z5.literal("residual"),
|
|
2043
|
+
...suppressedNotReadyShape
|
|
2044
|
+
}),
|
|
2045
|
+
z5.object({
|
|
2046
|
+
providerStatus: z5.literal("requires_confirmation"),
|
|
2047
|
+
sessionRole: z5.enum(["active", "residual"]),
|
|
2048
|
+
...suppressedNotReadyShape
|
|
2049
|
+
}),
|
|
2050
|
+
z5.object({
|
|
2051
|
+
providerStatus: z5.enum(["initializing", "processing", "authorized"]),
|
|
2052
|
+
sessionRole: z5.enum(["active", "residual"]),
|
|
2053
|
+
...suppressedNotReadyShape
|
|
2054
|
+
}),
|
|
2055
|
+
z5.object({
|
|
2056
|
+
providerStatus: z5.literal("canceling"),
|
|
2057
|
+
sessionRole: z5.enum(["active", "residual"]),
|
|
2058
|
+
...suppressedNotReadyShape
|
|
2059
|
+
}),
|
|
2060
|
+
z5.object({
|
|
2061
|
+
providerStatus: z5.literal("unknown"),
|
|
2062
|
+
sessionRole: z5.enum(["active", "residual"]),
|
|
2063
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2064
|
+
finalizationStatus: z5.literal("not_ready"),
|
|
2065
|
+
reconciliation: requiredReconciliationSchema([
|
|
2066
|
+
"provider_outcome_unknown"
|
|
2067
|
+
]),
|
|
2068
|
+
order: z5.null()
|
|
2069
|
+
}),
|
|
2070
|
+
z5.object({
|
|
2071
|
+
providerStatus: z5.literal("captured"),
|
|
2072
|
+
sessionRole: z5.enum(["canonical_candidate", "residual"]),
|
|
2073
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2074
|
+
finalizationStatus: z5.literal("pending"),
|
|
2075
|
+
reconciliation: noReconciliationSchema,
|
|
2076
|
+
providerEvidence: publicCaptureEvidenceSchema,
|
|
2077
|
+
order: z5.null()
|
|
2078
|
+
}),
|
|
2079
|
+
z5.object({
|
|
2080
|
+
providerStatus: z5.literal("captured"),
|
|
2081
|
+
sessionRole: z5.enum(["canonical", "historical"]),
|
|
2082
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2083
|
+
finalizationStatus: z5.literal("committed"),
|
|
2084
|
+
reconciliation: noReconciliationSchema,
|
|
2085
|
+
providerEvidence: publicCaptureEvidenceSchema,
|
|
2086
|
+
order: orderReferenceSchema
|
|
2087
|
+
}),
|
|
2088
|
+
z5.object({
|
|
2089
|
+
providerStatus: z5.literal("captured"),
|
|
2090
|
+
sessionRole: z5.enum(["canonical_candidate", "residual"]),
|
|
2091
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2092
|
+
finalizationStatus: z5.literal("pending"),
|
|
2093
|
+
reconciliation: requiredReconciliationSchema([
|
|
2094
|
+
"local_commit_ambiguous",
|
|
2095
|
+
"amount_mismatch",
|
|
2096
|
+
"currency_mismatch",
|
|
2097
|
+
"excess_capture_pending_order"
|
|
2098
|
+
]),
|
|
2099
|
+
providerEvidence: publicCaptureEvidenceSchema,
|
|
2100
|
+
order: z5.null()
|
|
2101
|
+
}),
|
|
2102
|
+
z5.object({
|
|
2103
|
+
providerStatus: z5.literal("captured"),
|
|
2104
|
+
sessionRole: z5.enum(["canonical", "residual"]),
|
|
2105
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2106
|
+
finalizationStatus: z5.literal("committed"),
|
|
2107
|
+
reconciliation: requiredReconciliationSchema([
|
|
2108
|
+
"inventory_unavailable",
|
|
2109
|
+
"admission_unavailable",
|
|
2110
|
+
"amount_mismatch",
|
|
2111
|
+
"currency_mismatch",
|
|
2112
|
+
"excess_capture",
|
|
2113
|
+
"late_capture",
|
|
2114
|
+
"cross_checkout_duplicate"
|
|
2115
|
+
]),
|
|
2116
|
+
providerEvidence: publicCaptureEvidenceSchema,
|
|
2117
|
+
order: orderReferenceSchema
|
|
2118
|
+
}),
|
|
2119
|
+
z5.object({
|
|
2120
|
+
providerStatus: z5.literal("failed"),
|
|
2121
|
+
sessionRole: z5.literal("historical"),
|
|
2122
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2123
|
+
finalizationStatus: z5.literal("not_ready"),
|
|
2124
|
+
reconciliation: noReconciliationSchema,
|
|
2125
|
+
failure: z5.object({
|
|
2126
|
+
reason: paymentFailureReasonSchema,
|
|
2127
|
+
message: z5.string().min(1).optional()
|
|
2128
|
+
}).strict(),
|
|
2129
|
+
order: z5.null()
|
|
2130
|
+
}),
|
|
2131
|
+
z5.object({
|
|
2132
|
+
providerStatus: z5.literal("canceled"),
|
|
2133
|
+
sessionRole: z5.literal("historical"),
|
|
2134
|
+
presentationStatus: z5.literal("suppressed"),
|
|
2135
|
+
finalizationStatus: z5.literal("not_ready"),
|
|
2136
|
+
cancellationReason: z5.enum([
|
|
2137
|
+
"expired",
|
|
2138
|
+
"customer",
|
|
2139
|
+
"operator",
|
|
2140
|
+
"provider"
|
|
2141
|
+
]),
|
|
2142
|
+
reconciliation: noReconciliationSchema,
|
|
2143
|
+
order: z5.null()
|
|
2144
|
+
})
|
|
2145
|
+
];
|
|
2146
|
+
}
|
|
2147
|
+
function providerSessionSchemas(action) {
|
|
2148
|
+
return stateSchemas(action).map(
|
|
2149
|
+
(state) => z5.object({
|
|
2150
|
+
...paymentSessionBaseShape,
|
|
2151
|
+
provider: z5.literal("toss"),
|
|
2152
|
+
...state.shape
|
|
2153
|
+
}).strict()
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
var publicPaymentSessionVariants = [
|
|
2157
|
+
...providerSessionSchemas(paymentNextActionSchema)
|
|
2158
|
+
];
|
|
2159
|
+
var publicPaymentSessionSchema = z5.union(publicPaymentSessionVariants);
|
|
2160
|
+
var CreatePaymentSessionParamsSchema = createPaymentSessionParamsSchema;
|
|
2161
|
+
var CancelServerPaymentSessionParamsSchema = cancelServerPaymentSessionParamsSchema;
|
|
2162
|
+
var ConfirmTossPaymentSessionParamsSchema = confirmTossPaymentSessionParamsSchema;
|
|
2163
|
+
|
|
2164
|
+
// ../../internal/contracts/src/ecommerce/checkout.ts
|
|
2165
|
+
var canonicalPublicCheckoutItemSchema = z6.object({
|
|
2166
|
+
id: z6.string().min(1),
|
|
2167
|
+
product: publicCartReferenceSchema,
|
|
2168
|
+
variant: publicCartReferenceSchema,
|
|
2169
|
+
productTitle: z6.string().min(1),
|
|
2170
|
+
variantTitle: z6.string().min(1).nullable(),
|
|
2171
|
+
sku: z6.string().min(1).nullable(),
|
|
2172
|
+
admissions: publicCartItemAdmissionsSchema,
|
|
2173
|
+
quantity: z6.number().int().positive().max(MAX_CART_LINE_QUANTITY),
|
|
2174
|
+
unitPriceMinor: minorAmountSchema,
|
|
2175
|
+
discountMinor: minorAmountSchema,
|
|
2176
|
+
lineTotalMinor: minorAmountSchema,
|
|
2177
|
+
requiresShipping: z6.boolean()
|
|
2178
|
+
}).strict().superRefine((item, ctx) => {
|
|
2179
|
+
const expectedLineTotal = BigInt(item.unitPriceMinor) * BigInt(item.quantity) - BigInt(item.discountMinor);
|
|
2180
|
+
if (expectedLineTotal < 0n) {
|
|
2181
|
+
ctx.addIssue({
|
|
2182
|
+
code: "custom",
|
|
2183
|
+
path: ["discountMinor"],
|
|
2184
|
+
message: "discountMinor cannot exceed the undiscounted line total"
|
|
2185
|
+
});
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
if (BigInt(item.lineTotalMinor) !== expectedLineTotal) {
|
|
2189
|
+
ctx.addIssue({
|
|
2190
|
+
code: "custom",
|
|
2191
|
+
path: ["lineTotalMinor"],
|
|
2192
|
+
message: "lineTotalMinor must equal unitPriceMinor * quantity - discountMinor"
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2196
|
+
var checkoutAmountsSchema = z6.object({
|
|
2197
|
+
currency: currencyCodeSchema,
|
|
2198
|
+
subtotalMinor: minorAmountSchema,
|
|
2199
|
+
shippingMinor: minorAmountSchema,
|
|
2200
|
+
discountMinor: minorAmountSchema,
|
|
2201
|
+
totalMinor: minorAmountSchema
|
|
2202
|
+
}).strict().superRefine((amounts, ctx) => {
|
|
2203
|
+
const expectedTotal = BigInt(amounts.subtotalMinor) + BigInt(amounts.shippingMinor) - BigInt(amounts.discountMinor);
|
|
2204
|
+
if (BigInt(amounts.totalMinor) !== expectedTotal) {
|
|
2205
|
+
ctx.addIssue({
|
|
2206
|
+
code: "custom",
|
|
2207
|
+
path: ["totalMinor"],
|
|
2208
|
+
message: "totalMinor must equal subtotalMinor + shippingMinor - discountMinor"
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
var checkoutPaymentSessionRefSchema = z6.object({
|
|
2213
|
+
id: z6.string().min(1),
|
|
2214
|
+
provider: z6.literal("toss"),
|
|
2215
|
+
providerStatus: paymentProviderStatusSchema
|
|
2216
|
+
}).strict();
|
|
2217
|
+
var openCheckoutPaymentStateSchema = z6.object({
|
|
2218
|
+
activePaymentSession: checkoutPaymentSessionRefSchema.nullable()
|
|
1230
2219
|
}).strict();
|
|
1231
|
-
var
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
2220
|
+
var spentCheckoutPaymentStateSchema = z6.object({
|
|
2221
|
+
activePaymentSession: z6.null()
|
|
2222
|
+
}).strict();
|
|
2223
|
+
var lightweightOrderReferenceSchema = z6.object({
|
|
2224
|
+
id: z6.string().min(1),
|
|
2225
|
+
orderNumber: z6.string().min(1)
|
|
2226
|
+
}).strict();
|
|
2227
|
+
var publicCheckoutBaseShape = {
|
|
2228
|
+
id: z6.string().min(1),
|
|
2229
|
+
amounts: checkoutAmountsSchema,
|
|
2230
|
+
items: z6.array(canonicalPublicCheckoutItemSchema).min(1),
|
|
2231
|
+
customerSnapshot: customerSnapshotSchema,
|
|
2232
|
+
shippingAddress: checkoutShippingAddressSchema.nullable(),
|
|
2233
|
+
attributes: publicAttributesSchema,
|
|
2234
|
+
expiresAt: z6.string().datetime({ offset: true }),
|
|
2235
|
+
createdAt: z6.string().datetime({ offset: true }),
|
|
2236
|
+
updatedAt: z6.string().datetime({ offset: true })
|
|
2237
|
+
};
|
|
2238
|
+
var abandonedReasonSchema = z6.enum(["expired", "customer", "operator"]);
|
|
2239
|
+
var publicCheckoutVariants = [
|
|
2240
|
+
z6.object({
|
|
2241
|
+
...publicCheckoutBaseShape,
|
|
2242
|
+
status: z6.literal("open"),
|
|
2243
|
+
paymentState: openCheckoutPaymentStateSchema,
|
|
2244
|
+
order: z6.null()
|
|
2245
|
+
}).strict(),
|
|
2246
|
+
z6.object({
|
|
2247
|
+
...publicCheckoutBaseShape,
|
|
2248
|
+
status: z6.literal("placed"),
|
|
2249
|
+
placedAt: z6.string().datetime({ offset: true }),
|
|
2250
|
+
paymentState: spentCheckoutPaymentStateSchema,
|
|
2251
|
+
order: lightweightOrderReferenceSchema
|
|
1241
2252
|
}).strict(),
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
2253
|
+
z6.object({
|
|
2254
|
+
...publicCheckoutBaseShape,
|
|
2255
|
+
status: z6.literal("placed"),
|
|
2256
|
+
placedAt: z6.string().datetime({ offset: true }),
|
|
2257
|
+
placedVia: z6.literal("late_capture"),
|
|
2258
|
+
abandonedAt: z6.string().datetime({ offset: true }),
|
|
2259
|
+
abandonedReason: abandonedReasonSchema,
|
|
2260
|
+
paymentState: spentCheckoutPaymentStateSchema,
|
|
2261
|
+
order: lightweightOrderReferenceSchema
|
|
2262
|
+
}).strict(),
|
|
2263
|
+
z6.object({
|
|
2264
|
+
...publicCheckoutBaseShape,
|
|
2265
|
+
status: z6.literal("abandoned"),
|
|
2266
|
+
abandonedAt: z6.string().datetime({ offset: true }),
|
|
2267
|
+
abandonedReason: abandonedReasonSchema,
|
|
2268
|
+
paymentState: z6.object({
|
|
2269
|
+
activePaymentSession: z6.null()
|
|
2270
|
+
}).strict(),
|
|
2271
|
+
order: z6.null()
|
|
2272
|
+
}).strict()
|
|
2273
|
+
];
|
|
2274
|
+
function validateCheckoutSnapshot(input, ctx) {
|
|
2275
|
+
const checkout = input;
|
|
2276
|
+
const expectedSubtotal = checkout.items.reduce(
|
|
2277
|
+
(total, item) => total + BigInt(item.unitPriceMinor) * BigInt(item.quantity),
|
|
2278
|
+
0n
|
|
2279
|
+
);
|
|
2280
|
+
const expectedDiscount = checkout.items.reduce(
|
|
2281
|
+
(total, item) => total + BigInt(item.discountMinor),
|
|
2282
|
+
0n
|
|
2283
|
+
);
|
|
2284
|
+
if (BigInt(checkout.amounts.subtotalMinor) !== expectedSubtotal) {
|
|
1256
2285
|
ctx.addIssue({
|
|
1257
|
-
code:
|
|
1258
|
-
path: ["
|
|
1259
|
-
message: "
|
|
2286
|
+
code: "custom",
|
|
2287
|
+
path: ["amounts", "subtotalMinor"],
|
|
2288
|
+
message: "subtotalMinor must equal the sum of undiscounted checkout line totals"
|
|
1260
2289
|
});
|
|
1261
2290
|
}
|
|
1262
|
-
if (
|
|
2291
|
+
if (BigInt(checkout.amounts.discountMinor) !== expectedDiscount) {
|
|
1263
2292
|
ctx.addIssue({
|
|
1264
|
-
code:
|
|
1265
|
-
path: ["
|
|
1266
|
-
message: "
|
|
2293
|
+
code: "custom",
|
|
2294
|
+
path: ["amounts", "discountMinor"],
|
|
2295
|
+
message: "discountMinor must equal the sum of checkout line discounts"
|
|
1267
2296
|
});
|
|
1268
2297
|
}
|
|
2298
|
+
}
|
|
2299
|
+
var publicCheckoutSchema = z6.union(publicCheckoutVariants).superRefine(validateCheckoutSnapshot);
|
|
2300
|
+
var serverCheckoutExtraShape = {
|
|
2301
|
+
clientReferenceId: commerceResourceIdSchema.nullable(),
|
|
2302
|
+
integrationMetadata: integrationMetadataSchema
|
|
2303
|
+
};
|
|
2304
|
+
var serverCheckoutVariants = publicCheckoutVariants.map(
|
|
2305
|
+
(variant) => variant.extend(serverCheckoutExtraShape)
|
|
2306
|
+
);
|
|
2307
|
+
var serverCheckoutSchema = z6.union(serverCheckoutVariants).superRefine(validateCheckoutSnapshot);
|
|
2308
|
+
var retrieveCheckoutParamsSchema = z6.object({ checkoutToken: commerceCapabilitySchema }).strict();
|
|
2309
|
+
var retrieveServerCheckoutParamsSchema = z6.object({ checkoutId: commerceResourceIdSchema }).strict();
|
|
2310
|
+
var createCheckoutBodySchema = z6.object({
|
|
2311
|
+
cartToken: commerceCapabilitySchema,
|
|
2312
|
+
customer: customerSnapshotSchema,
|
|
2313
|
+
shippingAddress: checkoutShippingAddressSchema.optional()
|
|
2314
|
+
}).strict();
|
|
2315
|
+
var createCheckoutParamsSchema = createCheckoutBodySchema.extend({
|
|
2316
|
+
idempotencyKey: commerceIdempotencyKeySchema
|
|
1269
2317
|
});
|
|
1270
|
-
var
|
|
1271
|
-
|
|
1272
|
-
|
|
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")
|
|
2318
|
+
var createServerCheckoutBodySchema = createCheckoutBodySchema.safeExtend({
|
|
2319
|
+
clientReferenceId: commerceResourceIdSchema.optional(),
|
|
2320
|
+
integrationMetadata: integrationMetadataSchema.optional()
|
|
1279
2321
|
}).strict();
|
|
1280
|
-
var
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
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()
|
|
2322
|
+
var createServerCheckoutParamsSchema = createServerCheckoutBodySchema.extend({
|
|
2323
|
+
idempotencyKey: commerceIdempotencyKeySchema
|
|
2324
|
+
});
|
|
2325
|
+
var createCheckoutResultSchema = z6.object({
|
|
2326
|
+
checkout: publicCheckoutSchema,
|
|
2327
|
+
checkoutToken: commerceCapabilitySchema
|
|
2328
|
+
}).strict();
|
|
2329
|
+
var createServerCheckoutResultSchema = z6.object({
|
|
2330
|
+
checkout: serverCheckoutSchema,
|
|
2331
|
+
checkoutToken: commerceCapabilitySchema
|
|
1304
2332
|
}).strict();
|
|
1305
|
-
var
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
status:
|
|
1309
|
-
|
|
2333
|
+
var canonicalCheckoutQuerySchema = z6.object({
|
|
2334
|
+
limit: z6.number().int().min(1).max(100).default(20),
|
|
2335
|
+
cursor: z6.string().min(1).max(512).optional(),
|
|
2336
|
+
status: z6.array(z6.enum(["open", "placed", "abandoned"])).optional(),
|
|
2337
|
+
createdAtFrom: z6.string().datetime({ offset: true }).optional(),
|
|
2338
|
+
createdAtTo: z6.string().datetime({ offset: true }).optional(),
|
|
2339
|
+
clientReferenceId: commerceResourceIdSchema.optional()
|
|
2340
|
+
}).strict();
|
|
2341
|
+
var checkoutListItemVariants = publicCheckoutVariants.map(
|
|
2342
|
+
(variant) => variant.omit({
|
|
2343
|
+
items: true,
|
|
2344
|
+
shippingAddress: true,
|
|
2345
|
+
attributes: true
|
|
2346
|
+
}).extend({ clientReferenceId: commerceResourceIdSchema.nullable() })
|
|
2347
|
+
);
|
|
2348
|
+
var checkoutListItemSchema = z6.union(checkoutListItemVariants);
|
|
2349
|
+
var canonicalCheckoutQueryResultSchema = z6.object({
|
|
2350
|
+
data: z6.array(checkoutListItemSchema),
|
|
2351
|
+
hasMore: z6.boolean(),
|
|
2352
|
+
nextCursor: z6.string().min(1).max(512).nullable()
|
|
1310
2353
|
}).strict();
|
|
2354
|
+
var CreateServerCheckoutParamsSchema = createServerCheckoutParamsSchema;
|
|
2355
|
+
var CanonicalCheckoutQuerySchema = canonicalCheckoutQuerySchema;
|
|
2356
|
+
|
|
2357
|
+
// ../../internal/contracts/src/ecommerce/index.ts
|
|
2358
|
+
var transactionStatusSchema = z7.enum([
|
|
2359
|
+
"pending",
|
|
2360
|
+
"paid",
|
|
2361
|
+
"success",
|
|
2362
|
+
"failed",
|
|
2363
|
+
"error",
|
|
2364
|
+
"canceled",
|
|
2365
|
+
"refunded"
|
|
2366
|
+
]);
|
|
2367
|
+
var transactionKindSchema = z7.enum([
|
|
2368
|
+
"authorization",
|
|
2369
|
+
"sale",
|
|
2370
|
+
"capture",
|
|
2371
|
+
"void",
|
|
2372
|
+
"refund"
|
|
2373
|
+
]);
|
|
1311
2374
|
var RETURN_REASON_CODES = [
|
|
1312
2375
|
"change_of_mind",
|
|
1313
2376
|
"defective",
|
|
1314
2377
|
"wrong_delivery",
|
|
1315
2378
|
"damaged",
|
|
2379
|
+
"size_too_large",
|
|
2380
|
+
"size_too_small",
|
|
2381
|
+
"color",
|
|
2382
|
+
"style",
|
|
2383
|
+
"not_as_described",
|
|
2384
|
+
"wrong_item",
|
|
2385
|
+
"unknown",
|
|
1316
2386
|
"other"
|
|
1317
2387
|
];
|
|
1318
|
-
var returnReasonSchema =
|
|
2388
|
+
var returnReasonSchema = z7.enum(RETURN_REASON_CODES);
|
|
1319
2389
|
var RETURN_REJECTION_REASON_CODES = [
|
|
1320
2390
|
"window_expired",
|
|
1321
2391
|
"used_or_damaged",
|
|
1322
2392
|
"non_returnable",
|
|
1323
2393
|
"other"
|
|
1324
2394
|
];
|
|
1325
|
-
var returnRejectionReasonCodeSchema =
|
|
2395
|
+
var returnRejectionReasonCodeSchema = z7.enum(
|
|
1326
2396
|
RETURN_REJECTION_REASON_CODES
|
|
1327
2397
|
);
|
|
1328
|
-
var
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
2398
|
+
var RETURN_CANCELLATION_REASON_CODES = [
|
|
2399
|
+
"items_not_received",
|
|
2400
|
+
"customer_withdrew",
|
|
2401
|
+
"merchant_decision",
|
|
2402
|
+
"other"
|
|
2403
|
+
];
|
|
2404
|
+
var returnCancellationReasonCodeSchema = z7.enum(
|
|
2405
|
+
RETURN_CANCELLATION_REASON_CODES
|
|
2406
|
+
);
|
|
2407
|
+
var restockActionSchema = z7.enum(["return_to_stock", "discard"]);
|
|
2408
|
+
var returnWithRefundItemSchema = z7.object({
|
|
2409
|
+
orderItem: z7.union([z7.string().min(1), z7.number()]).transform(String),
|
|
2410
|
+
quantity: z7.number().int().positive("quantity must be a positive integer"),
|
|
1332
2411
|
restockAction: restockActionSchema.default("return_to_stock"),
|
|
1333
|
-
restockingFee:
|
|
2412
|
+
restockingFee: z7.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
|
|
1334
2413
|
}).strict();
|
|
1335
|
-
var returnWithRefundSchema =
|
|
1336
|
-
orderNumber:
|
|
2414
|
+
var returnWithRefundSchema = z7.object({
|
|
2415
|
+
orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
|
|
1337
2416
|
reason: returnReasonSchema.optional().describe("Return reason (optional)"),
|
|
1338
|
-
reasonDetail:
|
|
1339
|
-
returnItems:
|
|
1340
|
-
refundAmount:
|
|
1341
|
-
returnShippingFee:
|
|
2417
|
+
reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
|
|
2418
|
+
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)"),
|
|
2419
|
+
refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
|
|
2420
|
+
returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe(
|
|
1342
2421
|
"Return shipping fee charged to the customer (ADR 0005 \xA7Gap 1)"
|
|
1343
2422
|
),
|
|
1344
|
-
initialShippingRefundAmount:
|
|
1345
|
-
initialShippingRefundOverrideNote:
|
|
2423
|
+
initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
|
|
2424
|
+
initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
|
|
1346
2425
|
"Operator audit note required when overriding policy suggestion"
|
|
1347
2426
|
),
|
|
1348
|
-
pgPaymentId:
|
|
1349
|
-
paymentKey:
|
|
1350
|
-
refundReceiptUrl:
|
|
2427
|
+
pgPaymentId: z7.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
|
|
2428
|
+
paymentKey: z7.string().min(1).optional().describe("Provider payment key for verified refund"),
|
|
2429
|
+
refundReceiptUrl: z7.string().optional().describe("Refund receipt URL (optional)")
|
|
1351
2430
|
}).strict();
|
|
1352
|
-
var createReturnSchema =
|
|
1353
|
-
orderNumber:
|
|
2431
|
+
var createReturnSchema = z7.object({
|
|
2432
|
+
orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
|
|
1354
2433
|
reason: returnReasonSchema.optional().describe("Return reason (optional)"),
|
|
1355
|
-
reasonDetail:
|
|
1356
|
-
returnItems:
|
|
1357
|
-
refundAmount:
|
|
2434
|
+
reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
|
|
2435
|
+
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)"),
|
|
2436
|
+
refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe(
|
|
1358
2437
|
"Line refund amount before initial shipping refund (required, min 0)"
|
|
1359
2438
|
),
|
|
1360
|
-
returnShippingFee:
|
|
1361
|
-
initialShippingRefundAmount:
|
|
1362
|
-
initialShippingRefundOverrideNote:
|
|
2439
|
+
returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
|
|
2440
|
+
initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
|
|
2441
|
+
initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
|
|
1363
2442
|
"Operator audit note required when overriding policy suggestion"
|
|
1364
2443
|
)
|
|
1365
2444
|
}).strict();
|
|
2445
|
+
var updateReturnSchema = z7.object({
|
|
2446
|
+
returnId: z7.union([z7.string(), z7.number()]).transform(String),
|
|
2447
|
+
// Only non-financial transitions are accepted here. `completed` is included
|
|
2448
|
+
// so the server can return the canonical `server_derived` error instead of
|
|
2449
|
+
// a generic validation failure.
|
|
2450
|
+
status: z7.enum([
|
|
2451
|
+
"processing",
|
|
2452
|
+
"approved",
|
|
2453
|
+
"rejected",
|
|
2454
|
+
"canceled",
|
|
2455
|
+
"completed"
|
|
2456
|
+
]),
|
|
2457
|
+
reasonCode: returnRejectionReasonCodeSchema.optional(),
|
|
2458
|
+
cancellationReasonCode: returnCancellationReasonCodeSchema.optional(),
|
|
2459
|
+
cancellationReasonDetail: z7.string().trim().min(1).max(2e3).optional()
|
|
2460
|
+
}).strict();
|
|
1366
2461
|
var CANCEL_REASON_CODES = [
|
|
1367
2462
|
"customer",
|
|
1368
2463
|
"inventory",
|
|
@@ -1371,245 +2466,159 @@ var CANCEL_REASON_CODES = [
|
|
|
1371
2466
|
"staff",
|
|
1372
2467
|
"other"
|
|
1373
2468
|
];
|
|
1374
|
-
var cancelReasonCodeSchema =
|
|
2469
|
+
var cancelReasonCodeSchema = z7.enum(CANCEL_REASON_CODES);
|
|
1375
2470
|
var FORCE_RESOLUTION_REASON_CODES = [
|
|
1376
2471
|
"staff_error",
|
|
1377
2472
|
"wrong_fulfillment",
|
|
1378
2473
|
"other"
|
|
1379
2474
|
];
|
|
1380
|
-
var forceResolutionReasonCodeSchema =
|
|
2475
|
+
var forceResolutionReasonCodeSchema = z7.enum(
|
|
1381
2476
|
FORCE_RESOLUTION_REASON_CODES
|
|
1382
2477
|
);
|
|
1383
|
-
var idempotencyKeySchema =
|
|
2478
|
+
var idempotencyKeySchema = z7.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
|
|
1384
2479
|
/^[\x21-\x7E]+$/,
|
|
1385
2480
|
"idempotencyKey must contain only printable header-safe characters"
|
|
1386
2481
|
);
|
|
1387
|
-
var cancelOrderSchema =
|
|
1388
|
-
orderNumber:
|
|
2482
|
+
var cancelOrderSchema = z7.object({
|
|
2483
|
+
orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number to cancel"),
|
|
1389
2484
|
reasonCode: cancelReasonCodeSchema.default("other").describe("Operator-selected cancel reason code"),
|
|
1390
|
-
reasonDetail:
|
|
2485
|
+
reasonDetail: z7.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
|
|
1391
2486
|
}).strict();
|
|
1392
2487
|
var cancelOrderResponseBaseSchema = {
|
|
1393
|
-
orderId:
|
|
2488
|
+
orderId: z7.string().min(1)
|
|
1394
2489
|
};
|
|
1395
2490
|
var unpaidLocalCancelCommittedResponseFields = {
|
|
1396
|
-
refundedAmount:
|
|
1397
|
-
providerRefunded:
|
|
2491
|
+
refundedAmount: z7.literal(0),
|
|
2492
|
+
providerRefunded: z7.literal(false)
|
|
1398
2493
|
};
|
|
1399
2494
|
var paidLocalCancelCommittedResponseFields = {
|
|
1400
|
-
transactionId:
|
|
1401
|
-
refundedAmount:
|
|
1402
|
-
providerRefunded:
|
|
1403
|
-
refundPending:
|
|
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)
|
|
2495
|
+
transactionId: z7.string().min(1),
|
|
2496
|
+
refundedAmount: z7.literal(0),
|
|
2497
|
+
providerRefunded: z7.literal(false),
|
|
2498
|
+
refundPending: z7.literal(true)
|
|
1410
2499
|
};
|
|
1411
2500
|
var alreadyCanceledResponseFields = {
|
|
1412
|
-
refundedAmount:
|
|
1413
|
-
providerRefunded:
|
|
2501
|
+
refundedAmount: z7.number().int().nonnegative(),
|
|
2502
|
+
providerRefunded: z7.literal(false)
|
|
1414
2503
|
};
|
|
1415
2504
|
var alreadyCanceledRefundPendingResponseFields = {
|
|
1416
|
-
transactionId:
|
|
1417
|
-
refundedAmount:
|
|
1418
|
-
providerRefunded:
|
|
1419
|
-
refundPending:
|
|
2505
|
+
transactionId: z7.string().min(1),
|
|
2506
|
+
refundedAmount: z7.number().int().nonnegative(),
|
|
2507
|
+
providerRefunded: z7.literal(false),
|
|
2508
|
+
refundPending: z7.literal(true)
|
|
1420
2509
|
};
|
|
1421
|
-
var
|
|
1422
|
-
|
|
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({
|
|
2510
|
+
var cancelOrderResponseSchema = z7.union([
|
|
2511
|
+
z7.object({
|
|
1434
2512
|
...cancelOrderResponseBaseSchema,
|
|
1435
2513
|
...paidLocalCancelCommittedResponseFields,
|
|
1436
|
-
status:
|
|
1437
|
-
cancelCommitted:
|
|
2514
|
+
status: z7.literal("canceled"),
|
|
2515
|
+
cancelCommitted: z7.literal(true)
|
|
1438
2516
|
}).strict(),
|
|
1439
|
-
|
|
2517
|
+
z7.object({
|
|
1440
2518
|
...cancelOrderResponseBaseSchema,
|
|
1441
2519
|
...unpaidLocalCancelCommittedResponseFields,
|
|
1442
|
-
status:
|
|
1443
|
-
cancelCommitted:
|
|
2520
|
+
status: z7.literal("canceled"),
|
|
2521
|
+
cancelCommitted: z7.literal(true)
|
|
1444
2522
|
}).strict(),
|
|
1445
|
-
|
|
2523
|
+
z7.object({
|
|
1446
2524
|
...cancelOrderResponseBaseSchema,
|
|
1447
2525
|
...alreadyCanceledResponseFields,
|
|
1448
|
-
status:
|
|
1449
|
-
cancelCommitted:
|
|
1450
|
-
alreadyCanceled:
|
|
2526
|
+
status: z7.literal("canceled"),
|
|
2527
|
+
cancelCommitted: z7.literal(false),
|
|
2528
|
+
alreadyCanceled: z7.literal(true)
|
|
1451
2529
|
}).strict(),
|
|
1452
|
-
|
|
2530
|
+
z7.object({
|
|
1453
2531
|
...cancelOrderResponseBaseSchema,
|
|
1454
2532
|
...alreadyCanceledRefundPendingResponseFields,
|
|
1455
|
-
status:
|
|
1456
|
-
cancelCommitted:
|
|
1457
|
-
alreadyCanceled:
|
|
1458
|
-
}).strict(),
|
|
1459
|
-
z4.object({
|
|
1460
|
-
...cancelOrderResponseBaseSchema,
|
|
1461
|
-
...legacyProviderRefundResponseFields,
|
|
1462
|
-
status: cancelOrderReconciliationStatusSchema,
|
|
1463
|
-
cancelCommitted: z4.literal(false),
|
|
1464
|
-
reconciliationRequired: z4.literal(true)
|
|
2533
|
+
status: z7.literal("canceled"),
|
|
2534
|
+
cancelCommitted: z7.literal(false),
|
|
2535
|
+
alreadyCanceled: z7.literal(true)
|
|
1465
2536
|
}).strict()
|
|
1466
2537
|
]);
|
|
1467
|
-
var resolveCancelRefundOutcomeSchema =
|
|
1468
|
-
var resolveCancelRefundSchema =
|
|
1469
|
-
orderNumber:
|
|
2538
|
+
var resolveCancelRefundOutcomeSchema = z7.enum(["succeeded", "failed"]);
|
|
2539
|
+
var resolveCancelRefundSchema = z7.object({
|
|
2540
|
+
orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
|
|
1470
2541
|
idempotencyKey: idempotencyKeySchema.describe(
|
|
1471
2542
|
"Stable key for this PG refund result report"
|
|
1472
2543
|
),
|
|
1473
2544
|
outcome: resolveCancelRefundOutcomeSchema.describe(
|
|
1474
2545
|
"PG refund result reported by the storefront or BFF"
|
|
1475
2546
|
),
|
|
1476
|
-
refundedAmount:
|
|
1477
|
-
pgProvider:
|
|
2547
|
+
refundedAmount: z7.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
|
|
2548
|
+
pgProvider: z7.string().trim().min(1, "pgProvider is required").regex(
|
|
1478
2549
|
/^[a-z0-9][a-z0-9_-]*$/,
|
|
1479
2550
|
"pgProvider must be a lowercase provider slug"
|
|
1480
2551
|
),
|
|
1481
|
-
pgRefundId:
|
|
2552
|
+
pgRefundId: z7.string().trim().min(1).optional()
|
|
1482
2553
|
}).strict().superRefine((value, ctx) => {
|
|
1483
2554
|
if (value.outcome === "succeeded" && value.refundedAmount <= 0) {
|
|
1484
2555
|
ctx.addIssue({
|
|
1485
|
-
code:
|
|
2556
|
+
code: z7.ZodIssueCode.custom,
|
|
1486
2557
|
path: ["refundedAmount"],
|
|
1487
2558
|
message: "refundedAmount must be positive when outcome is succeeded"
|
|
1488
2559
|
});
|
|
1489
2560
|
}
|
|
1490
2561
|
if (value.outcome === "succeeded" && !value.pgRefundId) {
|
|
1491
2562
|
ctx.addIssue({
|
|
1492
|
-
code:
|
|
2563
|
+
code: z7.ZodIssueCode.custom,
|
|
1493
2564
|
path: ["pgRefundId"],
|
|
1494
2565
|
message: "pgRefundId is required when outcome is succeeded"
|
|
1495
2566
|
});
|
|
1496
2567
|
}
|
|
1497
2568
|
if (value.outcome === "failed" && value.refundedAmount !== 0) {
|
|
1498
2569
|
ctx.addIssue({
|
|
1499
|
-
code:
|
|
2570
|
+
code: z7.ZodIssueCode.custom,
|
|
1500
2571
|
path: ["refundedAmount"],
|
|
1501
2572
|
message: "refundedAmount must be 0 when outcome is failed"
|
|
1502
2573
|
});
|
|
1503
2574
|
}
|
|
1504
2575
|
});
|
|
1505
|
-
var resolveCancelRefundResponseSchema =
|
|
1506
|
-
|
|
1507
|
-
orderId:
|
|
1508
|
-
transactionId:
|
|
1509
|
-
refundTransactionId:
|
|
1510
|
-
refundedAmount:
|
|
1511
|
-
refundStatus:
|
|
1512
|
-
transactionStatus:
|
|
2576
|
+
var resolveCancelRefundResponseSchema = z7.union([
|
|
2577
|
+
z7.object({
|
|
2578
|
+
orderId: z7.string().min(1),
|
|
2579
|
+
transactionId: z7.string().min(1),
|
|
2580
|
+
refundTransactionId: z7.string().min(1),
|
|
2581
|
+
refundedAmount: z7.number().int().positive(),
|
|
2582
|
+
refundStatus: z7.literal("succeeded"),
|
|
2583
|
+
transactionStatus: z7.enum(["paid", "refunded"])
|
|
1513
2584
|
}).strict(),
|
|
1514
|
-
|
|
1515
|
-
orderId:
|
|
1516
|
-
transactionId:
|
|
1517
|
-
refundTransactionId:
|
|
1518
|
-
refundedAmount:
|
|
1519
|
-
refundStatus:
|
|
1520
|
-
transactionStatus:
|
|
2585
|
+
z7.object({
|
|
2586
|
+
orderId: z7.string().min(1),
|
|
2587
|
+
transactionId: z7.string().min(1),
|
|
2588
|
+
refundTransactionId: z7.string().min(1),
|
|
2589
|
+
refundedAmount: z7.literal(0),
|
|
2590
|
+
refundStatus: z7.literal("failed"),
|
|
2591
|
+
transactionStatus: z7.literal("paid")
|
|
1521
2592
|
}).strict()
|
|
1522
2593
|
]);
|
|
1523
2594
|
|
|
1524
2595
|
// src/commands/order.ts
|
|
1525
|
-
import { z as
|
|
1526
|
-
var idSchema =
|
|
1527
|
-
var
|
|
1528
|
-
|
|
1529
|
-
|
|
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({
|
|
2596
|
+
import { z as z8 } from "zod";
|
|
2597
|
+
var idSchema = z8.union([z8.string().min(1), z8.number()]).transform(String);
|
|
2598
|
+
var orderStatusSchema = z8.enum(["confirmed"]);
|
|
2599
|
+
var fulfillmentStatusSchema = z8.enum(["shipped", "delivered", "failed"]);
|
|
2600
|
+
var updateFulfillmentSchema = z8.object({
|
|
1535
2601
|
fulfillmentId: idSchema,
|
|
1536
2602
|
status: fulfillmentStatusSchema.optional(),
|
|
1537
|
-
carrier:
|
|
1538
|
-
trackingNumber:
|
|
2603
|
+
carrier: z8.string().min(1).optional(),
|
|
2604
|
+
trackingNumber: z8.string().min(1).optional()
|
|
1539
2605
|
}).refine(
|
|
1540
2606
|
(value) => value.status !== void 0 || value.carrier !== void 0 || value.trackingNumber !== void 0,
|
|
1541
2607
|
{
|
|
1542
2608
|
message: "status, carrier, or trackingNumber is required"
|
|
1543
2609
|
}
|
|
1544
2610
|
);
|
|
1545
|
-
var fulfillmentItemsSchema =
|
|
1546
|
-
|
|
2611
|
+
var fulfillmentItemsSchema = z8.array(
|
|
2612
|
+
z8.object({
|
|
1547
2613
|
orderItem: idSchema,
|
|
1548
|
-
quantity:
|
|
2614
|
+
quantity: z8.number().int().positive("quantity must be a positive integer")
|
|
1549
2615
|
}).strict()
|
|
1550
2616
|
).min(1, "At least one fulfillment item is required").max(100, "Maximum 100 items per fulfillment");
|
|
1551
2617
|
function registerOrderCommands(program2, getClient2, getFormat2) {
|
|
1552
2618
|
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
2619
|
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
2620
|
try {
|
|
1612
|
-
const status = parseWithSchema(opts.status, "status",
|
|
2621
|
+
const status = parseWithSchema(opts.status, "status", orderStatusSchema);
|
|
1613
2622
|
const data = { orderNumber, status };
|
|
1614
2623
|
if (opts.dryRun) {
|
|
1615
2624
|
printResult(
|
|
@@ -1653,40 +2662,6 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
|
|
|
1653
2662
|
exitWithError(e);
|
|
1654
2663
|
}
|
|
1655
2664
|
});
|
|
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
2665
|
order.command("prepare <orderNumber>").description("Move paid order fulfillment work to preparation").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
|
|
1691
2666
|
try {
|
|
1692
2667
|
const data = { orderNumber };
|
|
@@ -1770,8 +2745,8 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
|
|
|
1770
2745
|
}
|
|
1771
2746
|
|
|
1772
2747
|
// src/commands/return.ts
|
|
1773
|
-
import { z as
|
|
1774
|
-
var returnStatusSchema =
|
|
2748
|
+
import { z as z9 } from "zod";
|
|
2749
|
+
var returnStatusSchema = z9.enum([
|
|
1775
2750
|
"processing",
|
|
1776
2751
|
"approved",
|
|
1777
2752
|
"rejected",
|
|
@@ -1900,146 +2875,227 @@ function registerReturnCommands(program2, getClient2, getFormat2) {
|
|
|
1900
2875
|
});
|
|
1901
2876
|
}
|
|
1902
2877
|
|
|
2878
|
+
// src/lib/sensitive-json-input.ts
|
|
2879
|
+
import { createReadStream } from "fs";
|
|
2880
|
+
import { stdin as processStdin } from "process";
|
|
2881
|
+
var SENSITIVE_JSON_MAX_BYTES = 1024 * 1024;
|
|
2882
|
+
function invalidInput(message) {
|
|
2883
|
+
return {
|
|
2884
|
+
type: "validation",
|
|
2885
|
+
code: "invalid_argument",
|
|
2886
|
+
field: "data",
|
|
2887
|
+
message,
|
|
2888
|
+
detail: { message, field: "data" }
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
function isInvalidInput(error) {
|
|
2892
|
+
if (!error || typeof error !== "object") return false;
|
|
2893
|
+
const value = error;
|
|
2894
|
+
return value.type === "validation" && value.code === "invalid_argument";
|
|
2895
|
+
}
|
|
2896
|
+
async function readBoundedUtf8(source) {
|
|
2897
|
+
const chunks = [];
|
|
2898
|
+
let totalBytes = 0;
|
|
2899
|
+
for await (const chunk of source) {
|
|
2900
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2901
|
+
totalBytes += buffer.byteLength;
|
|
2902
|
+
if (totalBytes > SENSITIVE_JSON_MAX_BYTES) {
|
|
2903
|
+
throw invalidInput(
|
|
2904
|
+
`Sensitive commerce data must be ${SENSITIVE_JSON_MAX_BYTES} bytes or fewer.`
|
|
2905
|
+
);
|
|
2906
|
+
}
|
|
2907
|
+
chunks.push(buffer);
|
|
2908
|
+
}
|
|
2909
|
+
return Buffer.concat(chunks, totalBytes).toString("utf8");
|
|
2910
|
+
}
|
|
2911
|
+
async function readSensitiveJsonObject(opts, sources = {}) {
|
|
2912
|
+
const sourceCount = [opts.dataStdin, opts.dataFile].filter(Boolean).length;
|
|
2913
|
+
if (sourceCount !== 1) {
|
|
2914
|
+
throw invalidInput(
|
|
2915
|
+
"Exactly one of --data-stdin or --data-file is required for sensitive commerce data."
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
let raw;
|
|
2919
|
+
const input = sources.stdin ?? processStdin;
|
|
2920
|
+
try {
|
|
2921
|
+
if (opts.dataStdin) {
|
|
2922
|
+
if (input.isTTY === true) {
|
|
2923
|
+
throw invalidInput(
|
|
2924
|
+
"Sensitive commerce data cannot be read from an interactive terminal. Pipe JSON to --data-stdin or use --data-file."
|
|
2925
|
+
);
|
|
2926
|
+
}
|
|
2927
|
+
raw = await readBoundedUtf8(input);
|
|
2928
|
+
} else {
|
|
2929
|
+
const file = (sources.openFile ?? createReadStream)(opts.dataFile);
|
|
2930
|
+
try {
|
|
2931
|
+
raw = await readBoundedUtf8(file);
|
|
2932
|
+
} finally {
|
|
2933
|
+
file.destroy?.();
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
} catch (error) {
|
|
2937
|
+
if (isInvalidInput(error)) throw error;
|
|
2938
|
+
throw invalidInput("Sensitive commerce data could not be read.");
|
|
2939
|
+
}
|
|
2940
|
+
let parsed;
|
|
2941
|
+
try {
|
|
2942
|
+
parsed = JSON.parse(raw);
|
|
2943
|
+
} catch {
|
|
2944
|
+
throw invalidInput("Sensitive commerce data must be valid JSON.");
|
|
2945
|
+
}
|
|
2946
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2947
|
+
throw invalidInput("Sensitive commerce data must be a JSON object.");
|
|
2948
|
+
}
|
|
2949
|
+
return parsed;
|
|
2950
|
+
}
|
|
2951
|
+
function sensitiveDryRunResult(action) {
|
|
2952
|
+
return { dryRun: true, valid: true, action };
|
|
2953
|
+
}
|
|
2954
|
+
|
|
1903
2955
|
// src/commands/cart.ts
|
|
1904
2956
|
function registerCartCommands(program2, getClient2, getFormat2) {
|
|
1905
2957
|
const cart = program2.command("cart").description("Cart management");
|
|
1906
|
-
cart.command("create").description("Create a cart and print its cartToken
|
|
2958
|
+
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
2959
|
try {
|
|
2960
|
+
const input = opts.dataStdin || opts.dataFile ? await readSensitiveJsonObject(opts) : {};
|
|
2961
|
+
const data = parseWithSchema(
|
|
2962
|
+
input,
|
|
2963
|
+
"cart create",
|
|
2964
|
+
createCartParamsSchema
|
|
2965
|
+
);
|
|
1908
2966
|
if (opts.dryRun) {
|
|
1909
|
-
printResult(
|
|
1910
|
-
{ dryRun: true, valid: true, action: "cart create", data: {} },
|
|
1911
|
-
getFormat2()
|
|
1912
|
-
);
|
|
2967
|
+
printResult(sensitiveDryRunResult("cart create"), getFormat2());
|
|
1913
2968
|
return;
|
|
1914
2969
|
}
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
} catch (e) {
|
|
1919
|
-
exitWithError(e);
|
|
2970
|
+
printResult(await getClient2().commerce.carts.create(data), getFormat2());
|
|
2971
|
+
} catch (error) {
|
|
2972
|
+
exitWithError(error);
|
|
1920
2973
|
}
|
|
1921
2974
|
});
|
|
1922
|
-
cart.command("
|
|
1923
|
-
"--quantity <n>",
|
|
1924
|
-
"Quantity",
|
|
1925
|
-
(v) => parseInt(v, 10)
|
|
1926
|
-
).option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
|
|
2975
|
+
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
2976
|
try {
|
|
1928
|
-
const data =
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2977
|
+
const data = parseWithSchema(
|
|
2978
|
+
await readSensitiveJsonObject(opts),
|
|
2979
|
+
"cart retrieve",
|
|
2980
|
+
retrieveCartParamsSchema
|
|
2981
|
+
);
|
|
2982
|
+
printResult(
|
|
2983
|
+
await getClient2().commerce.carts.retrieve(data),
|
|
2984
|
+
getFormat2()
|
|
2985
|
+
);
|
|
2986
|
+
} catch (error) {
|
|
2987
|
+
exitWithError(error);
|
|
2988
|
+
}
|
|
2989
|
+
});
|
|
2990
|
+
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) => {
|
|
2991
|
+
try {
|
|
2992
|
+
const data = parseWithSchema(
|
|
2993
|
+
await readSensitiveJsonObject(opts),
|
|
2994
|
+
"cart add",
|
|
2995
|
+
addItemParamsSchema
|
|
2996
|
+
);
|
|
1935
2997
|
if (opts.dryRun) {
|
|
1936
|
-
printResult(
|
|
1937
|
-
{ dryRun: true, valid: true, action: "cart add", data },
|
|
1938
|
-
getFormat2()
|
|
1939
|
-
);
|
|
2998
|
+
printResult(sensitiveDryRunResult("cart add"), getFormat2());
|
|
1940
2999
|
return;
|
|
1941
3000
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
} catch (e) {
|
|
1946
|
-
exitWithError(e);
|
|
3001
|
+
printResult(await getClient2().commerce.carts.addItem(data), getFormat2());
|
|
3002
|
+
} catch (error) {
|
|
3003
|
+
exitWithError(error);
|
|
1947
3004
|
}
|
|
1948
3005
|
});
|
|
1949
|
-
cart.command("update
|
|
1950
|
-
"--quantity <n>",
|
|
1951
|
-
"New quantity",
|
|
1952
|
-
(v) => parseInt(v, 10)
|
|
1953
|
-
).option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
|
|
3006
|
+
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
3007
|
try {
|
|
1955
|
-
const data =
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
3008
|
+
const data = parseWithSchema(
|
|
3009
|
+
await readSensitiveJsonObject(opts),
|
|
3010
|
+
"cart update",
|
|
3011
|
+
updateItemParamsSchema
|
|
3012
|
+
);
|
|
1960
3013
|
if (opts.dryRun) {
|
|
1961
|
-
printResult(
|
|
1962
|
-
{ dryRun: true, valid: true, action: "cart update", data },
|
|
1963
|
-
getFormat2()
|
|
1964
|
-
);
|
|
3014
|
+
printResult(sensitiveDryRunResult("cart update"), getFormat2());
|
|
1965
3015
|
return;
|
|
1966
3016
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
3017
|
+
printResult(
|
|
3018
|
+
await getClient2().commerce.carts.updateItem(data),
|
|
3019
|
+
getFormat2()
|
|
3020
|
+
);
|
|
3021
|
+
} catch (error) {
|
|
3022
|
+
exitWithError(error);
|
|
1972
3023
|
}
|
|
1973
3024
|
});
|
|
1974
|
-
cart.command("remove
|
|
3025
|
+
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
3026
|
try {
|
|
1976
|
-
const data =
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
3027
|
+
const data = parseWithSchema(
|
|
3028
|
+
await readSensitiveJsonObject(opts),
|
|
3029
|
+
"cart remove",
|
|
3030
|
+
removeItemParamsSchema
|
|
3031
|
+
);
|
|
3032
|
+
if (opts.dryRun) {
|
|
3033
|
+
printResult(sensitiveDryRunResult("cart remove"), getFormat2());
|
|
1982
3034
|
return;
|
|
1983
3035
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
3036
|
+
printResult(
|
|
3037
|
+
await getClient2().commerce.carts.removeItem(data),
|
|
3038
|
+
getFormat2()
|
|
3039
|
+
);
|
|
3040
|
+
} catch (error) {
|
|
3041
|
+
exitWithError(error);
|
|
1989
3042
|
}
|
|
1990
3043
|
});
|
|
1991
|
-
cart.command("clear
|
|
3044
|
+
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
3045
|
try {
|
|
3046
|
+
const data = parseWithSchema(
|
|
3047
|
+
await readSensitiveJsonObject(opts),
|
|
3048
|
+
"cart clear",
|
|
3049
|
+
clearCartParamsSchema
|
|
3050
|
+
);
|
|
1993
3051
|
if (opts.dryRun) {
|
|
1994
|
-
printResult(
|
|
1995
|
-
{ dryRun: true, valid: true, action: "cart clear", data: { cartToken } },
|
|
1996
|
-
getFormat2()
|
|
1997
|
-
);
|
|
3052
|
+
printResult(sensitiveDryRunResult("cart clear"), getFormat2());
|
|
1998
3053
|
return;
|
|
1999
3054
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
} catch (e) {
|
|
2004
|
-
exitWithError(e);
|
|
3055
|
+
printResult(await getClient2().commerce.carts.clear(data), getFormat2());
|
|
3056
|
+
} catch (error) {
|
|
3057
|
+
exitWithError(error);
|
|
2005
3058
|
}
|
|
2006
3059
|
});
|
|
2007
|
-
cart.command("
|
|
3060
|
+
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
3061
|
try {
|
|
2009
|
-
const data =
|
|
3062
|
+
const data = parseWithSchema(
|
|
3063
|
+
await readSensitiveJsonObject(opts),
|
|
3064
|
+
"cart discounts",
|
|
3065
|
+
updateDiscountCodesParamsSchema
|
|
3066
|
+
);
|
|
2010
3067
|
if (opts.dryRun) {
|
|
2011
|
-
printResult(
|
|
2012
|
-
{ dryRun: true, valid: true, action: "cart apply-discount", data },
|
|
2013
|
-
getFormat2()
|
|
2014
|
-
);
|
|
3068
|
+
printResult(sensitiveDryRunResult("cart discounts"), getFormat2());
|
|
2015
3069
|
return;
|
|
2016
3070
|
}
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
3071
|
+
printResult(
|
|
3072
|
+
await getClient2().commerce.carts.updateDiscountCodes(data),
|
|
3073
|
+
getFormat2()
|
|
3074
|
+
);
|
|
3075
|
+
} catch (error) {
|
|
3076
|
+
exitWithError(error);
|
|
2022
3077
|
}
|
|
2023
3078
|
});
|
|
2024
|
-
cart.command("
|
|
3079
|
+
cart.command("attributes").description("Replace all public cart attributes; pass [] to clear").option("--data-stdin", "Read capability and attributes JSON from stdin").option(
|
|
3080
|
+
"--data-file <path>",
|
|
3081
|
+
"Read capability and attributes JSON from file"
|
|
3082
|
+
).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
|
|
2025
3083
|
try {
|
|
3084
|
+
const data = parseWithSchema(
|
|
3085
|
+
await readSensitiveJsonObject(opts),
|
|
3086
|
+
"cart attributes",
|
|
3087
|
+
updateCartAttributesParamsSchema
|
|
3088
|
+
);
|
|
2026
3089
|
if (opts.dryRun) {
|
|
2027
|
-
printResult(
|
|
2028
|
-
{
|
|
2029
|
-
dryRun: true,
|
|
2030
|
-
valid: true,
|
|
2031
|
-
action: "cart remove-discount",
|
|
2032
|
-
data: { cartToken }
|
|
2033
|
-
},
|
|
2034
|
-
getFormat2()
|
|
2035
|
-
);
|
|
3090
|
+
printResult(sensitiveDryRunResult("cart attributes"), getFormat2());
|
|
2036
3091
|
return;
|
|
2037
3092
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
3093
|
+
printResult(
|
|
3094
|
+
await getClient2().commerce.carts.updateAttributes(data),
|
|
3095
|
+
getFormat2()
|
|
3096
|
+
);
|
|
3097
|
+
} catch (error) {
|
|
3098
|
+
exitWithError(error);
|
|
2043
3099
|
}
|
|
2044
3100
|
});
|
|
2045
3101
|
}
|
|
@@ -2063,124 +3119,196 @@ function registerStockCommands(program2, getClient2, getFormat2) {
|
|
|
2063
3119
|
}
|
|
2064
3120
|
|
|
2065
3121
|
// src/commands/transaction.ts
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
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) => {
|
|
3122
|
+
function registerCommerceSessionCommands(program2, getClient2, getFormat2) {
|
|
3123
|
+
const checkout = program2.command("checkout").description("Checkout management");
|
|
3124
|
+
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
3125
|
try {
|
|
2098
3126
|
const data = parseWithSchema(
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
3127
|
+
await readSensitiveJsonObject(opts),
|
|
3128
|
+
"checkout",
|
|
3129
|
+
CreateServerCheckoutParamsSchema
|
|
3130
|
+
);
|
|
3131
|
+
if (opts.dryRun) {
|
|
3132
|
+
printResult(sensitiveDryRunResult("checkout create"), getFormat2());
|
|
3133
|
+
return;
|
|
3134
|
+
}
|
|
3135
|
+
printResult(
|
|
3136
|
+
await getClient2().commerce.checkouts.create(data),
|
|
3137
|
+
getFormat2()
|
|
3138
|
+
);
|
|
3139
|
+
} catch (error) {
|
|
3140
|
+
exitWithError(error);
|
|
3141
|
+
}
|
|
3142
|
+
});
|
|
3143
|
+
checkout.command("retrieve <checkoutId>").description("Retrieve a Checkout by ID with server credentials").action(async (checkoutId) => {
|
|
3144
|
+
try {
|
|
3145
|
+
printResult(
|
|
3146
|
+
await getClient2().commerce.checkouts.retrieve({ checkoutId }),
|
|
3147
|
+
getFormat2()
|
|
3148
|
+
);
|
|
3149
|
+
} catch (error) {
|
|
3150
|
+
exitWithError(error);
|
|
3151
|
+
}
|
|
3152
|
+
});
|
|
3153
|
+
checkout.command("query").description("Query lightweight Checkouts with server credentials").option("--params <json>", "Checkout query parameters (JSON)", "{}").action(async (opts) => {
|
|
3154
|
+
try {
|
|
3155
|
+
const data = parseWithSchema(
|
|
3156
|
+
parseJson(opts.params, "params"),
|
|
3157
|
+
"checkout query",
|
|
3158
|
+
CanonicalCheckoutQuerySchema
|
|
3159
|
+
);
|
|
3160
|
+
printResult(
|
|
3161
|
+
await getClient2().commerce.checkouts.query(data),
|
|
3162
|
+
getFormat2()
|
|
3163
|
+
);
|
|
3164
|
+
} catch (error) {
|
|
3165
|
+
exitWithError(error);
|
|
3166
|
+
}
|
|
3167
|
+
});
|
|
3168
|
+
const sessions = program2.command("payment-session").description("PaymentSession management");
|
|
3169
|
+
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) => {
|
|
3170
|
+
try {
|
|
3171
|
+
const data = parseWithSchema(
|
|
3172
|
+
await readSensitiveJsonObject(opts),
|
|
3173
|
+
"payment-session",
|
|
3174
|
+
CreatePaymentSessionParamsSchema
|
|
2109
3175
|
);
|
|
2110
3176
|
if (opts.dryRun) {
|
|
2111
3177
|
printResult(
|
|
2112
|
-
|
|
3178
|
+
sensitiveDryRunResult("payment-session create"),
|
|
2113
3179
|
getFormat2()
|
|
2114
3180
|
);
|
|
2115
3181
|
return;
|
|
2116
3182
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
3183
|
+
printResult(
|
|
3184
|
+
await getClient2().commerce.paymentSessions.create(data),
|
|
3185
|
+
getFormat2()
|
|
3186
|
+
);
|
|
3187
|
+
} catch (error) {
|
|
3188
|
+
exitWithError(error);
|
|
3189
|
+
}
|
|
3190
|
+
});
|
|
3191
|
+
sessions.command("retrieve <paymentSessionId>").description("Retrieve a PaymentSession").action(async (paymentSessionId) => {
|
|
3192
|
+
try {
|
|
3193
|
+
printResult(
|
|
3194
|
+
await getClient2().commerce.paymentSessions.retrieve({
|
|
3195
|
+
paymentSessionId
|
|
3196
|
+
}),
|
|
3197
|
+
getFormat2()
|
|
3198
|
+
);
|
|
3199
|
+
} catch (error) {
|
|
3200
|
+
exitWithError(error);
|
|
2122
3201
|
}
|
|
2123
3202
|
});
|
|
2124
|
-
|
|
2125
|
-
"--
|
|
2126
|
-
"
|
|
2127
|
-
).
|
|
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) => {
|
|
3203
|
+
sessions.command("cancel").description("Cancel a PaymentSession and release its payment slot").option("--data-stdin", "Read session ID and idempotency key from stdin").option(
|
|
3204
|
+
"--data-file <path>",
|
|
3205
|
+
"Read session ID and idempotency key from file"
|
|
3206
|
+
).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
|
|
2134
3207
|
try {
|
|
2135
3208
|
const data = parseWithSchema(
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
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
|
|
3209
|
+
await readSensitiveJsonObject(opts),
|
|
3210
|
+
"payment-session cancel",
|
|
3211
|
+
CancelServerPaymentSessionParamsSchema
|
|
2153
3212
|
);
|
|
2154
|
-
const sdkParams = {
|
|
2155
|
-
...data,
|
|
2156
|
-
...opts.idempotencyKey !== void 0 ? { idempotencyKey: parseIdempotencyKey(opts.idempotencyKey) } : {}
|
|
2157
|
-
};
|
|
2158
3213
|
if (opts.dryRun) {
|
|
2159
3214
|
printResult(
|
|
2160
|
-
|
|
2161
|
-
dryRun: true,
|
|
2162
|
-
valid: true,
|
|
2163
|
-
action: "transaction confirm-payment",
|
|
2164
|
-
data: sdkParams
|
|
2165
|
-
},
|
|
3215
|
+
sensitiveDryRunResult("payment-session cancel"),
|
|
2166
3216
|
getFormat2()
|
|
2167
3217
|
);
|
|
2168
3218
|
return;
|
|
2169
3219
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
3220
|
+
printResult(
|
|
3221
|
+
await getClient2().commerce.paymentSessions.cancel(data),
|
|
3222
|
+
getFormat2()
|
|
2173
3223
|
);
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
3224
|
+
} catch (error) {
|
|
3225
|
+
exitWithError(error);
|
|
3226
|
+
}
|
|
3227
|
+
});
|
|
3228
|
+
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) => {
|
|
3229
|
+
try {
|
|
3230
|
+
const data = parseWithSchema(
|
|
3231
|
+
await readSensitiveJsonObject(opts),
|
|
3232
|
+
"payment-session confirm-toss",
|
|
3233
|
+
ConfirmTossPaymentSessionParamsSchema
|
|
3234
|
+
);
|
|
3235
|
+
if (opts.dryRun) {
|
|
3236
|
+
printResult(
|
|
3237
|
+
sensitiveDryRunResult("payment-session confirm-toss"),
|
|
3238
|
+
getFormat2()
|
|
3239
|
+
);
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
printResult(
|
|
3243
|
+
await getClient2().commerce.paymentSessions.confirm(data),
|
|
3244
|
+
getFormat2()
|
|
3245
|
+
);
|
|
3246
|
+
} catch (error) {
|
|
3247
|
+
exitWithError(error);
|
|
2177
3248
|
}
|
|
2178
3249
|
});
|
|
2179
3250
|
}
|
|
2180
3251
|
|
|
3252
|
+
// src/lib/product-detail-options.ts
|
|
3253
|
+
function invalidOption(message, field, value) {
|
|
3254
|
+
const error = new Error(message);
|
|
3255
|
+
Object.assign(error, {
|
|
3256
|
+
type: "validation",
|
|
3257
|
+
code: "invalid_argument",
|
|
3258
|
+
field,
|
|
3259
|
+
detail: { message, value, field }
|
|
3260
|
+
});
|
|
3261
|
+
return error;
|
|
3262
|
+
}
|
|
3263
|
+
function parseLocale(value) {
|
|
3264
|
+
if (value == null) return void 0;
|
|
3265
|
+
if (value === "ko" || value === "en" || value === "all") return value;
|
|
3266
|
+
throw invalidOption("--locale must be one of: ko, en, all", "locale", value);
|
|
3267
|
+
}
|
|
3268
|
+
function parseFallbackLocale(value) {
|
|
3269
|
+
if (value == null) return void 0;
|
|
3270
|
+
if (value === "ko" || value === "en") return value;
|
|
3271
|
+
if (value === "none") return false;
|
|
3272
|
+
throw invalidOption(
|
|
3273
|
+
"--fallback-locale must be one of: ko, en, none",
|
|
3274
|
+
"fallbackLocale",
|
|
3275
|
+
value
|
|
3276
|
+
);
|
|
3277
|
+
}
|
|
3278
|
+
function productDetailParamsFromOptions(options) {
|
|
3279
|
+
const hasId = typeof options.id === "string" && options.id.length > 0;
|
|
3280
|
+
const hasSlug = typeof options.slug === "string" && options.slug.length > 0;
|
|
3281
|
+
if (hasId === hasSlug) {
|
|
3282
|
+
throw invalidOption(
|
|
3283
|
+
"Exactly one of --id or --slug is required.",
|
|
3284
|
+
"product",
|
|
3285
|
+
{ id: options.id, slug: options.slug }
|
|
3286
|
+
);
|
|
3287
|
+
}
|
|
3288
|
+
const locale = parseLocale(options.locale);
|
|
3289
|
+
const fallbackLocale = parseFallbackLocale(options.fallbackLocale);
|
|
3290
|
+
const identifier = hasId ? { id: options.id } : { slug: options.slug };
|
|
3291
|
+
return {
|
|
3292
|
+
...identifier,
|
|
3293
|
+
...options.market !== void 0 ? { market: options.market } : {},
|
|
3294
|
+
...locale !== void 0 ? { locale } : {},
|
|
3295
|
+
...fallbackLocale !== void 0 ? { fallbackLocale } : {}
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
|
|
2181
3299
|
// src/commands/product.ts
|
|
2182
3300
|
function registerProductCommands(program2, getClient2, getFormat2) {
|
|
2183
3301
|
const product = program2.command("product").description("Product management");
|
|
3302
|
+
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) => {
|
|
3303
|
+
try {
|
|
3304
|
+
const params = productDetailParamsFromOptions(opts);
|
|
3305
|
+
const client = getClient2();
|
|
3306
|
+
const result = await client.commerce.product.detail(params);
|
|
3307
|
+
printResult(result, getFormat2());
|
|
3308
|
+
} catch (e) {
|
|
3309
|
+
exitWithError(e);
|
|
3310
|
+
}
|
|
3311
|
+
});
|
|
2184
3312
|
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
3313
|
try {
|
|
2186
3314
|
const data = parseWithSchema(
|
|
@@ -2234,12 +3362,14 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
|
|
|
2234
3362
|
"--order-amount <n>",
|
|
2235
3363
|
"Order amount",
|
|
2236
3364
|
(v) => parseInt(v, 10)
|
|
2237
|
-
).option("--shipping-policy-id <id>", "Shipping policy ID").option("--postal-code <code>", "Destination postal code").action(async (opts) => {
|
|
3365
|
+
).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
3366
|
try {
|
|
2239
3367
|
const client = getClient2();
|
|
2240
3368
|
const result = await client.commerce.shipping.calculate({
|
|
2241
3369
|
orderAmount: opts.orderAmount,
|
|
2242
3370
|
...opts.shippingPolicyId ? { shippingPolicyId: opts.shippingPolicyId } : {},
|
|
3371
|
+
...opts.countryCode ? { countryCode: opts.countryCode } : {},
|
|
3372
|
+
...opts.regionCode ? { regionCode: opts.regionCode } : {},
|
|
2243
3373
|
...opts.postalCode ? { postalCode: opts.postalCode } : {}
|
|
2244
3374
|
});
|
|
2245
3375
|
printResult(result, getFormat2());
|
|
@@ -2250,7 +3380,7 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
|
|
|
2250
3380
|
}
|
|
2251
3381
|
|
|
2252
3382
|
// src/commands/auth.ts
|
|
2253
|
-
import { randomBytes } from "crypto";
|
|
3383
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
2254
3384
|
import { createServer } from "http";
|
|
2255
3385
|
import { execFile, exec } from "child_process";
|
|
2256
3386
|
import { platform } from "os";
|
|
@@ -2425,7 +3555,7 @@ Logged in successfully!`));
|
|
|
2425
3555
|
}
|
|
2426
3556
|
function registerAuthCommands(program2) {
|
|
2427
3557
|
program2.command("login").description("Login via browser and store credentials").action(async () => {
|
|
2428
|
-
const state =
|
|
3558
|
+
const state = randomBytes2(32).toString("hex");
|
|
2429
3559
|
try {
|
|
2430
3560
|
const { port } = await startAuthServer({
|
|
2431
3561
|
state,
|
|
@@ -2514,7 +3644,7 @@ ${loginUrl}`));
|
|
|
2514
3644
|
);
|
|
2515
3645
|
process.exit(3);
|
|
2516
3646
|
}
|
|
2517
|
-
const state =
|
|
3647
|
+
const state = randomBytes2(32).toString("hex");
|
|
2518
3648
|
const isLocal = !!opts.local;
|
|
2519
3649
|
try {
|
|
2520
3650
|
const { port } = await startAuthServer({
|
|
@@ -2613,9 +3743,15 @@ function registerFeatureCommands(program2, getClient2, getFormat2) {
|
|
|
2613
3743
|
}
|
|
2614
3744
|
|
|
2615
3745
|
// src/commands/agent.ts
|
|
3746
|
+
import { SERVER_COLLECTIONS as SERVER_COLLECTIONS4 } from "@01.software/sdk";
|
|
3747
|
+
|
|
3748
|
+
// src/lib/agent-plan-allowlist.ts
|
|
2616
3749
|
import { SERVER_COLLECTIONS as SERVER_COLLECTIONS3 } from "@01.software/sdk";
|
|
2617
3750
|
|
|
2618
3751
|
// src/lib/agent-output.ts
|
|
3752
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
3753
|
+
import fs from "fs";
|
|
3754
|
+
import path from "path";
|
|
2619
3755
|
var PLAN_AGENT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
2620
3756
|
"PLAN_EXPIRED",
|
|
2621
3757
|
"PLAN_MISMATCH",
|
|
@@ -2632,9 +3768,95 @@ function agentPlanError(code, message) {
|
|
|
2632
3768
|
});
|
|
2633
3769
|
return error;
|
|
2634
3770
|
}
|
|
3771
|
+
var AGENT_OUTPUT_FILE_MODE = 384;
|
|
3772
|
+
var isWin32 = process.platform === "win32";
|
|
2635
3773
|
function stringifyAgentJson(data, options = {}) {
|
|
2636
3774
|
return options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
2637
3775
|
}
|
|
3776
|
+
function invalidOutputTarget(message, outputFile) {
|
|
3777
|
+
const error = new Error(message);
|
|
3778
|
+
Object.assign(error, {
|
|
3779
|
+
type: "validation",
|
|
3780
|
+
code: "invalid_argument",
|
|
3781
|
+
field: "outputFile",
|
|
3782
|
+
detail: { message, field: "outputFile", value: outputFile }
|
|
3783
|
+
});
|
|
3784
|
+
return error;
|
|
3785
|
+
}
|
|
3786
|
+
function assertSafeAgentOutputTarget(outputFile) {
|
|
3787
|
+
let stat;
|
|
3788
|
+
try {
|
|
3789
|
+
stat = fs.lstatSync(outputFile);
|
|
3790
|
+
} catch (error) {
|
|
3791
|
+
if (error.code === "ENOENT") return;
|
|
3792
|
+
throw error;
|
|
3793
|
+
}
|
|
3794
|
+
if (stat.isSymbolicLink()) {
|
|
3795
|
+
throw invalidOutputTarget(
|
|
3796
|
+
`Refusing to write Agent output through a symlink: ${outputFile}.`,
|
|
3797
|
+
outputFile
|
|
3798
|
+
);
|
|
3799
|
+
}
|
|
3800
|
+
if (!stat.isFile()) {
|
|
3801
|
+
throw invalidOutputTarget(
|
|
3802
|
+
`Refusing to write Agent output to a non-regular file: ${outputFile}.`,
|
|
3803
|
+
outputFile
|
|
3804
|
+
);
|
|
3805
|
+
}
|
|
3806
|
+
if (!isWin32 && stat.nlink > 1) {
|
|
3807
|
+
throw invalidOutputTarget(
|
|
3808
|
+
`Refusing to write Agent output to a file with multiple hard links: ${outputFile}.`,
|
|
3809
|
+
outputFile
|
|
3810
|
+
);
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
function writeAgentOutputFile(requestedPath, content) {
|
|
3814
|
+
const outputFile = path.resolve(requestedPath);
|
|
3815
|
+
assertSafeAgentOutputTarget(outputFile);
|
|
3816
|
+
const tempFile = path.join(
|
|
3817
|
+
path.dirname(outputFile),
|
|
3818
|
+
`.${path.basename(outputFile)}.${randomBytes3(12).toString("hex")}.tmp`
|
|
3819
|
+
);
|
|
3820
|
+
let tempFd;
|
|
3821
|
+
try {
|
|
3822
|
+
tempFd = fs.openSync(tempFile, "wx", AGENT_OUTPUT_FILE_MODE);
|
|
3823
|
+
if (!isWin32) {
|
|
3824
|
+
fs.fchmodSync(tempFd, AGENT_OUTPUT_FILE_MODE);
|
|
3825
|
+
}
|
|
3826
|
+
fs.writeFileSync(tempFd, content, { encoding: "utf8" });
|
|
3827
|
+
fs.closeSync(tempFd);
|
|
3828
|
+
tempFd = void 0;
|
|
3829
|
+
fs.renameSync(tempFile, outputFile);
|
|
3830
|
+
} catch (error) {
|
|
3831
|
+
if (tempFd !== void 0) {
|
|
3832
|
+
try {
|
|
3833
|
+
fs.closeSync(tempFd);
|
|
3834
|
+
} catch {
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
try {
|
|
3838
|
+
fs.unlinkSync(tempFile);
|
|
3839
|
+
} catch {
|
|
3840
|
+
}
|
|
3841
|
+
throw error;
|
|
3842
|
+
}
|
|
3843
|
+
return {
|
|
3844
|
+
outputFile,
|
|
3845
|
+
bytes: Buffer.byteLength(content, "utf8")
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3848
|
+
function resolveAgentPrintOptions(command) {
|
|
3849
|
+
const options = command.optsWithGlobals();
|
|
3850
|
+
return {
|
|
3851
|
+
pretty: Boolean(options.pretty),
|
|
3852
|
+
outputFile: options.outputFile
|
|
3853
|
+
};
|
|
3854
|
+
}
|
|
3855
|
+
function validateAgentOutputTarget(options) {
|
|
3856
|
+
if (options.outputFile !== void 0) {
|
|
3857
|
+
assertSafeAgentOutputTarget(path.resolve(options.outputFile));
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
2638
3860
|
function asRecord(value) {
|
|
2639
3861
|
return value && typeof value === "object" ? value : null;
|
|
2640
3862
|
}
|
|
@@ -2660,6 +3882,20 @@ function isRawNotFound(error) {
|
|
|
2660
3882
|
const raw = asRecord(error);
|
|
2661
3883
|
return raw?.status === 404 || raw?.code === "not_found" || raw?.name === "GoneError";
|
|
2662
3884
|
}
|
|
3885
|
+
function isSdkUsageLimitError(error) {
|
|
3886
|
+
const raw = asRecord(error);
|
|
3887
|
+
return raw?.name === "UsageLimitError" || raw?.code === "USAGE_LIMIT_ERROR";
|
|
3888
|
+
}
|
|
3889
|
+
function agentErrorEnvelope(code, message, retryable, retryAfter) {
|
|
3890
|
+
return {
|
|
3891
|
+
error: {
|
|
3892
|
+
code,
|
|
3893
|
+
message,
|
|
3894
|
+
retryable,
|
|
3895
|
+
...typeof retryAfter === "number" ? { retryAfter } : {}
|
|
3896
|
+
}
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
2663
3899
|
function agentErrorExitCode(code) {
|
|
2664
3900
|
switch (code) {
|
|
2665
3901
|
case "INVALID_INPUT":
|
|
@@ -2683,22 +3919,30 @@ function classifyAgentError(error) {
|
|
|
2683
3919
|
const detailRecord = detail && typeof detail === "object" ? detail : null;
|
|
2684
3920
|
const agentCode = raw?.agentCode ?? detailRecord?.agentCode;
|
|
2685
3921
|
if (typeof agentCode === "string" && PLAN_AGENT_ERROR_CODES.has(agentCode)) {
|
|
2686
|
-
return
|
|
3922
|
+
return agentErrorEnvelope(agentCode, message, false);
|
|
2687
3923
|
}
|
|
2688
3924
|
const adminError = classifyError(error);
|
|
2689
3925
|
if (isRawNotFound(error) || adminError.code === "not_found") {
|
|
2690
|
-
return
|
|
3926
|
+
return agentErrorEnvelope("NOT_FOUND", message, false);
|
|
2691
3927
|
}
|
|
2692
3928
|
if (adminError.type === "permission") {
|
|
2693
|
-
return
|
|
3929
|
+
return agentErrorEnvelope("AUTH_FAILED", message, false);
|
|
2694
3930
|
}
|
|
2695
3931
|
if (adminError.type === "validation" && adminError.code !== "unknown") {
|
|
2696
|
-
return
|
|
3932
|
+
return agentErrorEnvelope("INVALID_INPUT", message, false);
|
|
2697
3933
|
}
|
|
2698
|
-
|
|
3934
|
+
const retryable = !isSdkUsageLimitError(error) && (adminError.type === "network" || adminError.type === "degraded");
|
|
3935
|
+
const retryAfter = retryable && adminError.type === "degraded" && adminError.code === "rate_limited" ? adminError.retryAfter : void 0;
|
|
3936
|
+
return agentErrorEnvelope("INTERNAL", message, retryable, retryAfter);
|
|
2699
3937
|
}
|
|
2700
3938
|
function printAgentSuccess(data, options = {}) {
|
|
2701
|
-
|
|
3939
|
+
const output = stringifyAgentJson(data, options);
|
|
3940
|
+
if (options.outputFile !== void 0) {
|
|
3941
|
+
const receipt = writeAgentOutputFile(options.outputFile, output);
|
|
3942
|
+
console.log(JSON.stringify(receipt));
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
console.log(output);
|
|
2702
3946
|
}
|
|
2703
3947
|
function printAgentError(error, options = {}) {
|
|
2704
3948
|
const envelope = classifyAgentError(error);
|
|
@@ -2711,25 +3955,77 @@ function exitWithAgentError(error, options = {}) {
|
|
|
2711
3955
|
}
|
|
2712
3956
|
|
|
2713
3957
|
// src/lib/agent-plan-allowlist.ts
|
|
2714
|
-
var
|
|
2715
|
-
|
|
2716
|
-
"article-categories",
|
|
2717
|
-
"article-tags",
|
|
2718
|
-
|
|
2719
|
-
"link-categories",
|
|
2720
|
-
"link-tags"
|
|
2721
|
-
]
|
|
2722
|
-
|
|
3958
|
+
var AGENT_PLAN_CAPABILITIES = {
|
|
3959
|
+
articles: ["create", "update", "delete"],
|
|
3960
|
+
"article-categories": ["create", "update", "delete"],
|
|
3961
|
+
"article-tags": ["create", "update", "delete"],
|
|
3962
|
+
links: ["create", "update", "delete"],
|
|
3963
|
+
"link-categories": ["create", "update", "delete"],
|
|
3964
|
+
"link-tags": ["create", "update", "delete"],
|
|
3965
|
+
documents: ["create", "update"],
|
|
3966
|
+
"document-categories": ["create", "update"],
|
|
3967
|
+
"document-types": ["create", "update"],
|
|
3968
|
+
canvases: ["create", "update", "delete"],
|
|
3969
|
+
"canvas-categories": ["create", "update", "delete"],
|
|
3970
|
+
"canvas-tags": ["create", "update", "delete"]
|
|
3971
|
+
};
|
|
3972
|
+
var SERVER_COLLECTION_SET = new Set(SERVER_COLLECTIONS3);
|
|
3973
|
+
var AGENT_PLAN_OPERATIONS = /* @__PURE__ */ new Set([
|
|
3974
|
+
"create",
|
|
3975
|
+
"update",
|
|
3976
|
+
"delete"
|
|
3977
|
+
]);
|
|
3978
|
+
function assertAgentPlanCapabilityMatrix(matrix) {
|
|
3979
|
+
for (const [collection, operations] of Object.entries(matrix)) {
|
|
3980
|
+
if (!SERVER_COLLECTION_SET.has(collection)) {
|
|
3981
|
+
throw new Error(
|
|
3982
|
+
`Agent plan capability collection "${collection}" is not a ServerCollection.`
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
if (operations.length === 0) {
|
|
3986
|
+
throw new Error(
|
|
3987
|
+
`Agent plan capability collection "${collection}" has no operations.`
|
|
3988
|
+
);
|
|
3989
|
+
}
|
|
3990
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3991
|
+
for (const operation of operations) {
|
|
3992
|
+
if (!AGENT_PLAN_OPERATIONS.has(operation)) {
|
|
3993
|
+
throw new Error(
|
|
3994
|
+
`Agent plan capability operation "${operation}" is invalid for "${collection}".`
|
|
3995
|
+
);
|
|
3996
|
+
}
|
|
3997
|
+
if (seen.has(operation)) {
|
|
3998
|
+
throw new Error(
|
|
3999
|
+
`Agent plan capability operation "${operation}" is duplicated for "${collection}".`
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
4002
|
+
seen.add(operation);
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
assertAgentPlanCapabilityMatrix(AGENT_PLAN_CAPABILITIES);
|
|
2723
4007
|
function isAgentPlanCollection(collection) {
|
|
2724
|
-
return
|
|
4008
|
+
return Object.prototype.hasOwnProperty.call(
|
|
4009
|
+
AGENT_PLAN_CAPABILITIES,
|
|
4010
|
+
collection
|
|
4011
|
+
);
|
|
4012
|
+
}
|
|
4013
|
+
function getAgentPlanOperations(collection) {
|
|
4014
|
+
return isAgentPlanCollection(collection) ? AGENT_PLAN_CAPABILITIES[collection] : [];
|
|
2725
4015
|
}
|
|
2726
|
-
function
|
|
2727
|
-
if (isAgentPlanCollection(collection))
|
|
4016
|
+
function isAgentPlanCapability(collection, operation) {
|
|
4017
|
+
if (!isAgentPlanCollection(collection)) return false;
|
|
4018
|
+
return AGENT_PLAN_CAPABILITIES[collection].includes(
|
|
4019
|
+
operation
|
|
4020
|
+
);
|
|
4021
|
+
}
|
|
4022
|
+
function assertAgentPlanCapability(collection, operation) {
|
|
4023
|
+
if (isAgentPlanCapability(collection, operation)) {
|
|
2728
4024
|
return collection;
|
|
2729
4025
|
}
|
|
2730
4026
|
throw agentPlanError(
|
|
2731
4027
|
"UNSUPPORTED_OPERATION",
|
|
2732
|
-
`
|
|
4028
|
+
`Operation "${operation}" is not supported for collection "${collection}" in agent plan mutations.`
|
|
2733
4029
|
);
|
|
2734
4030
|
}
|
|
2735
4031
|
|
|
@@ -2832,6 +4128,12 @@ function parseStoredAgentPlan(raw, planId) {
|
|
|
2832
4128
|
if (operation !== "create" && operation !== "update" && operation !== "delete") {
|
|
2833
4129
|
throw agentPlanError("PLAN_MISMATCH", "Stored plan has invalid operation.");
|
|
2834
4130
|
}
|
|
4131
|
+
if (!isAgentPlanCapability(collection, operation)) {
|
|
4132
|
+
throw agentPlanError(
|
|
4133
|
+
"PLAN_MISMATCH",
|
|
4134
|
+
"Stored plan has unsupported collection operation."
|
|
4135
|
+
);
|
|
4136
|
+
}
|
|
2835
4137
|
const payload = record.payload == null ? void 0 : typeof record.payload === "object" && !Array.isArray(record.payload) ? record.payload : (() => {
|
|
2836
4138
|
throw agentPlanError("PLAN_MISMATCH", "Stored plan payload is invalid.");
|
|
2837
4139
|
})();
|
|
@@ -2856,6 +4158,12 @@ function parseStoredAgentPlan(raw, planId) {
|
|
|
2856
4158
|
};
|
|
2857
4159
|
}
|
|
2858
4160
|
function assertStoredPlanHashMatches(plan) {
|
|
4161
|
+
if (!isAgentPlanCapability(plan.collection, plan.operation)) {
|
|
4162
|
+
throw agentPlanError(
|
|
4163
|
+
"PLAN_MISMATCH",
|
|
4164
|
+
"Stored plan has unsupported collection operation."
|
|
4165
|
+
);
|
|
4166
|
+
}
|
|
2859
4167
|
const expected = hashAgentPlanEnvelope({
|
|
2860
4168
|
operation: plan.operation,
|
|
2861
4169
|
collection: plan.collection,
|
|
@@ -3076,10 +4384,10 @@ function isSecretFieldName(key) {
|
|
|
3076
4384
|
const snake = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
3077
4385
|
return SECRET_KEY.test(snake);
|
|
3078
4386
|
}
|
|
3079
|
-
function assertNoSecretFields(value,
|
|
4387
|
+
function assertNoSecretFields(value, path2 = "") {
|
|
3080
4388
|
if (Array.isArray(value)) {
|
|
3081
4389
|
value.forEach((item, index) => {
|
|
3082
|
-
assertNoSecretFields(item, `${
|
|
4390
|
+
assertNoSecretFields(item, `${path2}[${index}]`);
|
|
3083
4391
|
});
|
|
3084
4392
|
return;
|
|
3085
4393
|
}
|
|
@@ -3087,7 +4395,7 @@ function assertNoSecretFields(value, path = "") {
|
|
|
3087
4395
|
return;
|
|
3088
4396
|
}
|
|
3089
4397
|
for (const [key, child] of Object.entries(value)) {
|
|
3090
|
-
const fieldPath =
|
|
4398
|
+
const fieldPath = path2 ? `${path2}.${key}` : key;
|
|
3091
4399
|
if (isSecretFieldName(key)) {
|
|
3092
4400
|
throw agentPlanError(
|
|
3093
4401
|
"INVALID_INPUT",
|
|
@@ -3129,7 +4437,7 @@ function buildConfirmCommand(planToken, planHash) {
|
|
|
3129
4437
|
}
|
|
3130
4438
|
|
|
3131
4439
|
// src/commands/agent-plan.ts
|
|
3132
|
-
function
|
|
4440
|
+
function invalidInput2(message, field) {
|
|
3133
4441
|
const error = new Error(message);
|
|
3134
4442
|
Object.assign(error, {
|
|
3135
4443
|
type: "validation",
|
|
@@ -3143,7 +4451,7 @@ function handleCommanderError(error) {
|
|
|
3143
4451
|
if (error.code === "commander.helpDisplayed") {
|
|
3144
4452
|
process.exit(error.exitCode);
|
|
3145
4453
|
}
|
|
3146
|
-
exitWithAgentError(
|
|
4454
|
+
exitWithAgentError(invalidInput2(error.message, "command"));
|
|
3147
4455
|
}
|
|
3148
4456
|
function configureAgentParser(command) {
|
|
3149
4457
|
command.configureOutput({
|
|
@@ -3161,9 +4469,12 @@ async function captureBaseUpdatedAt(client, collection, documentId) {
|
|
|
3161
4469
|
return readDocumentUpdatedAt(doc);
|
|
3162
4470
|
}
|
|
3163
4471
|
async function runPlanAction(input) {
|
|
3164
|
-
const collection =
|
|
4472
|
+
const collection = assertAgentPlanCapability(
|
|
4473
|
+
input.collection,
|
|
4474
|
+
input.operation
|
|
4475
|
+
);
|
|
3165
4476
|
if (input.operation !== "create" && !input.documentId) {
|
|
3166
|
-
throw
|
|
4477
|
+
throw invalidInput2("Missing required argument: id", "id");
|
|
3167
4478
|
}
|
|
3168
4479
|
let payload;
|
|
3169
4480
|
if (input.operation === "create" || input.operation === "update") {
|
|
@@ -3225,7 +4536,7 @@ async function runPlanAction(input) {
|
|
|
3225
4536
|
documentId: stored.documentId,
|
|
3226
4537
|
confirmCommand: buildConfirmCommand(planToken, stored.planHash)
|
|
3227
4538
|
},
|
|
3228
|
-
|
|
4539
|
+
input.outputOptions
|
|
3229
4540
|
);
|
|
3230
4541
|
}
|
|
3231
4542
|
function registerAgentPlanCommands(agent, getClient2) {
|
|
@@ -3233,63 +4544,83 @@ function registerAgentPlanCommands(agent, getClient2) {
|
|
|
3233
4544
|
agent.command("plan").description("Plan a mutation without executing")
|
|
3234
4545
|
);
|
|
3235
4546
|
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").
|
|
3237
|
-
|
|
4547
|
+
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(
|
|
4548
|
+
"--output-file <path>",
|
|
4549
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4550
|
+
).action(
|
|
4551
|
+
async (collection, opts, command) => {
|
|
4552
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
3238
4553
|
try {
|
|
4554
|
+
validateAgentOutputTarget(outputOptions);
|
|
3239
4555
|
await runPlanAction({
|
|
3240
4556
|
operation: "create",
|
|
3241
4557
|
collection,
|
|
3242
4558
|
dataStdin: opts.dataStdin,
|
|
3243
4559
|
dataFile: opts.dataFile,
|
|
3244
|
-
|
|
4560
|
+
outputOptions,
|
|
3245
4561
|
getClient: getClient2
|
|
3246
4562
|
});
|
|
3247
4563
|
} catch (error) {
|
|
3248
|
-
exitWithAgentError(error,
|
|
4564
|
+
exitWithAgentError(error, outputOptions);
|
|
3249
4565
|
}
|
|
3250
4566
|
}
|
|
3251
4567
|
)
|
|
3252
4568
|
);
|
|
3253
4569
|
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").
|
|
3255
|
-
|
|
4570
|
+
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(
|
|
4571
|
+
"--output-file <path>",
|
|
4572
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4573
|
+
).action(
|
|
4574
|
+
async (collection, id, opts, command) => {
|
|
4575
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
3256
4576
|
try {
|
|
4577
|
+
validateAgentOutputTarget(outputOptions);
|
|
3257
4578
|
await runPlanAction({
|
|
3258
4579
|
operation: "update",
|
|
3259
4580
|
collection,
|
|
3260
4581
|
documentId: id,
|
|
3261
4582
|
dataStdin: opts.dataStdin,
|
|
3262
4583
|
dataFile: opts.dataFile,
|
|
3263
|
-
|
|
4584
|
+
outputOptions,
|
|
3264
4585
|
getClient: getClient2
|
|
3265
4586
|
});
|
|
3266
4587
|
} catch (error) {
|
|
3267
|
-
exitWithAgentError(error,
|
|
4588
|
+
exitWithAgentError(error, outputOptions);
|
|
3268
4589
|
}
|
|
3269
4590
|
}
|
|
3270
4591
|
)
|
|
3271
4592
|
);
|
|
3272
4593
|
configureAgentParser(
|
|
3273
|
-
plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented JSON").
|
|
3274
|
-
|
|
4594
|
+
plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented success JSON").option(
|
|
4595
|
+
"--output-file <path>",
|
|
4596
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4597
|
+
).action(
|
|
4598
|
+
async (collection, id, opts, command) => {
|
|
4599
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
3275
4600
|
try {
|
|
4601
|
+
validateAgentOutputTarget(outputOptions);
|
|
3276
4602
|
await runPlanAction({
|
|
3277
4603
|
operation: "delete",
|
|
3278
4604
|
collection,
|
|
3279
4605
|
documentId: id,
|
|
3280
|
-
|
|
4606
|
+
outputOptions,
|
|
3281
4607
|
getClient: getClient2
|
|
3282
4608
|
});
|
|
3283
4609
|
} catch (error) {
|
|
3284
|
-
exitWithAgentError(error,
|
|
4610
|
+
exitWithAgentError(error, outputOptions);
|
|
3285
4611
|
}
|
|
3286
4612
|
}
|
|
3287
4613
|
)
|
|
3288
4614
|
);
|
|
3289
4615
|
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").
|
|
3291
|
-
|
|
4616
|
+
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(
|
|
4617
|
+
"--output-file <path>",
|
|
4618
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4619
|
+
).action(
|
|
4620
|
+
async (planTokenRaw, opts, command) => {
|
|
4621
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
3292
4622
|
try {
|
|
4623
|
+
validateAgentOutputTarget(outputOptions);
|
|
3293
4624
|
const token = decodePlanToken(planTokenRaw);
|
|
3294
4625
|
if (token.planHash !== opts.hash) {
|
|
3295
4626
|
throw agentPlanError(
|
|
@@ -3335,10 +4666,10 @@ function registerAgentPlanCommands(agent, getClient2) {
|
|
|
3335
4666
|
id: doc && typeof doc === "object" && "id" in doc ? String(doc.id) : plan2.documentId,
|
|
3336
4667
|
doc
|
|
3337
4668
|
},
|
|
3338
|
-
|
|
4669
|
+
outputOptions
|
|
3339
4670
|
);
|
|
3340
4671
|
} catch (error) {
|
|
3341
|
-
exitWithAgentError(error,
|
|
4672
|
+
exitWithAgentError(error, outputOptions);
|
|
3342
4673
|
}
|
|
3343
4674
|
}
|
|
3344
4675
|
)
|
|
@@ -3354,7 +4685,7 @@ function buildStableAgentFields() {
|
|
|
3354
4685
|
updatedAt: { type: "string", queryable: true }
|
|
3355
4686
|
};
|
|
3356
4687
|
}
|
|
3357
|
-
function
|
|
4688
|
+
function invalidInput3(message, field, value) {
|
|
3358
4689
|
const error = new Error(message);
|
|
3359
4690
|
Object.assign(error, {
|
|
3360
4691
|
type: "validation",
|
|
@@ -3367,13 +4698,13 @@ function invalidInput2(message, field, value) {
|
|
|
3367
4698
|
function parsePositiveInteger(value, field) {
|
|
3368
4699
|
if (value == null) return void 0;
|
|
3369
4700
|
if (/^[1-9]\d*$/.test(value)) return Number(value);
|
|
3370
|
-
throw
|
|
4701
|
+
throw invalidInput3(`--${field} must be a positive integer`, field, value);
|
|
3371
4702
|
}
|
|
3372
4703
|
function handleCommanderError2(error) {
|
|
3373
4704
|
if (error.code === "commander.helpDisplayed") {
|
|
3374
4705
|
process.exit(error.exitCode);
|
|
3375
4706
|
}
|
|
3376
|
-
exitWithAgentError(
|
|
4707
|
+
exitWithAgentError(invalidInput3(error.message, "command"));
|
|
3377
4708
|
}
|
|
3378
4709
|
function configureAgentParser2(command) {
|
|
3379
4710
|
command.configureOutput({
|
|
@@ -3394,23 +4725,27 @@ function parseWhere(value) {
|
|
|
3394
4725
|
return parsed;
|
|
3395
4726
|
}
|
|
3396
4727
|
} catch {
|
|
3397
|
-
throw
|
|
4728
|
+
throw invalidInput3("--where must be a JSON object", "where", value);
|
|
3398
4729
|
}
|
|
3399
|
-
throw
|
|
4730
|
+
throw invalidInput3("--where must be a JSON object", "where", value);
|
|
3400
4731
|
}
|
|
3401
4732
|
var AGENT_READ_OPERATIONS = [
|
|
3402
4733
|
"query",
|
|
3403
4734
|
"get"
|
|
3404
4735
|
];
|
|
3405
|
-
var
|
|
3406
|
-
"
|
|
3407
|
-
"plan:update",
|
|
3408
|
-
"plan:delete"
|
|
4736
|
+
var AGENT_PRODUCT_OPERATIONS = [
|
|
4737
|
+
"detail"
|
|
3409
4738
|
];
|
|
3410
4739
|
function buildAgentManifest() {
|
|
3411
4740
|
const collections = {};
|
|
3412
|
-
for (const collection of
|
|
3413
|
-
const operations =
|
|
4741
|
+
for (const collection of SERVER_COLLECTIONS4) {
|
|
4742
|
+
const operations = [
|
|
4743
|
+
...AGENT_READ_OPERATIONS,
|
|
4744
|
+
...collection === "products" ? AGENT_PRODUCT_OPERATIONS : [],
|
|
4745
|
+
...getAgentPlanOperations(collection).map(
|
|
4746
|
+
(operation) => `plan:${operation}`
|
|
4747
|
+
)
|
|
4748
|
+
];
|
|
3414
4749
|
collections[collection] = {
|
|
3415
4750
|
operations,
|
|
3416
4751
|
fields: buildStableAgentFields()
|
|
@@ -3418,6 +4753,10 @@ function buildAgentManifest() {
|
|
|
3418
4753
|
}
|
|
3419
4754
|
return {
|
|
3420
4755
|
version: AGENT_PROTOCOL_VERSION,
|
|
4756
|
+
discovery: {
|
|
4757
|
+
context: { counts: true },
|
|
4758
|
+
schema: { mode: "effective" }
|
|
4759
|
+
},
|
|
3421
4760
|
collections
|
|
3422
4761
|
};
|
|
3423
4762
|
}
|
|
@@ -3426,27 +4765,75 @@ function validateAgentCollection(collection) {
|
|
|
3426
4765
|
return validateCollection(collection);
|
|
3427
4766
|
} catch (error) {
|
|
3428
4767
|
const message = error instanceof Error ? error.message : `Unknown collection "${collection}".`;
|
|
3429
|
-
throw
|
|
4768
|
+
throw invalidInput3(message, "collection", collection);
|
|
3430
4769
|
}
|
|
3431
4770
|
}
|
|
3432
4771
|
function registerAgentCommands(program2, getClient2) {
|
|
3433
4772
|
const agent = configureAgentParser2(
|
|
3434
|
-
program2.command("agent").description("Machine-stable CLI namespace for coding agents").
|
|
4773
|
+
program2.command("agent").description("Machine-stable CLI namespace for coding agents").option("--pretty", "Print 2-space indented success JSON").option(
|
|
4774
|
+
"--output-file <path>",
|
|
4775
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4776
|
+
).addHelpText(
|
|
3435
4777
|
"after",
|
|
3436
4778
|
"\nContract: docs/agent-cli-contract.md\nJSON only on stdout; no TTY effects."
|
|
3437
4779
|
)
|
|
3438
4780
|
);
|
|
3439
4781
|
configureAgentParser2(
|
|
3440
|
-
agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented JSON").
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
4782
|
+
agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented success JSON").option(
|
|
4783
|
+
"--output-file <path>",
|
|
4784
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4785
|
+
).action((_opts, command) => {
|
|
4786
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
4787
|
+
try {
|
|
4788
|
+
validateAgentOutputTarget(outputOptions);
|
|
4789
|
+
printAgentSuccess(buildAgentManifest(), outputOptions);
|
|
4790
|
+
} catch (error) {
|
|
4791
|
+
exitWithAgentError(error, outputOptions);
|
|
4792
|
+
}
|
|
4793
|
+
})
|
|
4794
|
+
);
|
|
4795
|
+
configureAgentParser2(
|
|
4796
|
+
agent.command("context").description("Get the active tenant context").option("--counts", "Include collection document counts").option("--pretty", "Print 2-space indented success JSON").option(
|
|
4797
|
+
"--output-file <path>",
|
|
4798
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4799
|
+
).action(async (opts, command) => {
|
|
4800
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
4801
|
+
try {
|
|
4802
|
+
validateAgentOutputTarget(outputOptions);
|
|
4803
|
+
const client = getClient2();
|
|
4804
|
+
const result = opts.counts ? await client.tenant.context({ includeCounts: true }) : await client.tenant.context();
|
|
4805
|
+
printAgentSuccess(result, outputOptions);
|
|
4806
|
+
} catch (error) {
|
|
4807
|
+
exitWithAgentError(error, outputOptions);
|
|
4808
|
+
}
|
|
4809
|
+
})
|
|
4810
|
+
);
|
|
4811
|
+
configureAgentParser2(
|
|
4812
|
+
agent.command("schema <collection>").description("Get the effective field schema for a collection").option("--pretty", "Print 2-space indented success JSON").option(
|
|
4813
|
+
"--output-file <path>",
|
|
4814
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4815
|
+
).action(async (collection, _opts, command) => {
|
|
4816
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
4817
|
+
try {
|
|
4818
|
+
validateAgentOutputTarget(outputOptions);
|
|
4819
|
+
const col = validateAgentCollection(collection);
|
|
4820
|
+
const client = getClient2();
|
|
4821
|
+
const result = await client.tenant.collectionSchema(col);
|
|
4822
|
+
printAgentSuccess(result, outputOptions);
|
|
4823
|
+
} catch (error) {
|
|
4824
|
+
exitWithAgentError(error, outputOptions);
|
|
4825
|
+
}
|
|
3444
4826
|
})
|
|
3445
4827
|
);
|
|
3446
4828
|
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").
|
|
3448
|
-
|
|
4829
|
+
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(
|
|
4830
|
+
"--output-file <path>",
|
|
4831
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4832
|
+
).action(
|
|
4833
|
+
async (collection, opts, command) => {
|
|
4834
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
3449
4835
|
try {
|
|
4836
|
+
validateAgentOutputTarget(outputOptions);
|
|
3450
4837
|
const col = validateAgentCollection(collection);
|
|
3451
4838
|
const options = {};
|
|
3452
4839
|
const where = parseWhere(opts.where);
|
|
@@ -3459,28 +4846,55 @@ function registerAgentCommands(program2, getClient2) {
|
|
|
3459
4846
|
if (opts.select) options.select = parseSelect(opts.select);
|
|
3460
4847
|
const client = getClient2();
|
|
3461
4848
|
const result = await client.collections.from(col).find(options);
|
|
3462
|
-
printAgentSuccess(result,
|
|
4849
|
+
printAgentSuccess(result, outputOptions);
|
|
3463
4850
|
} catch (error) {
|
|
3464
|
-
exitWithAgentError(error,
|
|
4851
|
+
exitWithAgentError(error, outputOptions);
|
|
3465
4852
|
}
|
|
3466
4853
|
}
|
|
3467
4854
|
)
|
|
3468
4855
|
);
|
|
3469
4856
|
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").
|
|
3471
|
-
|
|
4857
|
+
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(
|
|
4858
|
+
"--output-file <path>",
|
|
4859
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4860
|
+
).action(
|
|
4861
|
+
async (collection, id, opts, command) => {
|
|
4862
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
3472
4863
|
try {
|
|
4864
|
+
validateAgentOutputTarget(outputOptions);
|
|
3473
4865
|
const col = validateAgentCollection(collection);
|
|
3474
4866
|
if (!id) {
|
|
3475
|
-
throw
|
|
4867
|
+
throw invalidInput3("Missing required argument: id", "id");
|
|
3476
4868
|
}
|
|
3477
4869
|
const options = {};
|
|
3478
4870
|
if (opts.select) options.select = parseSelect(opts.select);
|
|
3479
4871
|
const client = getClient2();
|
|
3480
4872
|
const result = await client.collections.from(col).findById(id, options);
|
|
3481
|
-
printAgentSuccess(result,
|
|
4873
|
+
printAgentSuccess(result, outputOptions);
|
|
4874
|
+
} catch (error) {
|
|
4875
|
+
exitWithAgentError(error, outputOptions);
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
)
|
|
4879
|
+
);
|
|
4880
|
+
const product = configureAgentParser2(
|
|
4881
|
+
agent.command("product").description("Shaped product reads")
|
|
4882
|
+
);
|
|
4883
|
+
configureAgentParser2(
|
|
4884
|
+
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(
|
|
4885
|
+
"--output-file <path>",
|
|
4886
|
+
"Write success JSON to a secure file and print a receipt"
|
|
4887
|
+
).action(
|
|
4888
|
+
async (opts, command) => {
|
|
4889
|
+
const outputOptions = resolveAgentPrintOptions(command);
|
|
4890
|
+
try {
|
|
4891
|
+
validateAgentOutputTarget(outputOptions);
|
|
4892
|
+
const params = productDetailParamsFromOptions(opts);
|
|
4893
|
+
const client = getClient2();
|
|
4894
|
+
const result = await client.commerce.product.detail(params);
|
|
4895
|
+
printAgentSuccess(result, outputOptions);
|
|
3482
4896
|
} catch (error) {
|
|
3483
|
-
exitWithAgentError(error,
|
|
4897
|
+
exitWithAgentError(error, outputOptions);
|
|
3484
4898
|
}
|
|
3485
4899
|
}
|
|
3486
4900
|
)
|
|
@@ -3510,7 +4924,7 @@ registerOrderCommands(program, getClient, getFormat);
|
|
|
3510
4924
|
registerReturnCommands(program, getClient, getFormat);
|
|
3511
4925
|
registerCartCommands(program, getClient, getFormat);
|
|
3512
4926
|
registerStockCommands(program, getClient, getFormat);
|
|
3513
|
-
|
|
4927
|
+
registerCommerceSessionCommands(program, getClient, getFormat);
|
|
3514
4928
|
registerProductCommands(program, getClient, getFormat);
|
|
3515
4929
|
registerDiscountCommands(program, getClient, getFormat);
|
|
3516
4930
|
registerShippingCommands(program, getClient, getFormat);
|