@ratio-app/cli 0.1.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/README.md +504 -0
- package/bin/run.mjs +5 -0
- package/dist/commands/__test__/throw.d.ts +25 -0
- package/dist/commands/__test__/throw.js +238 -0
- package/dist/commands/__test__/throw.js.map +1 -0
- package/dist/commands/app/create.d.ts +55 -0
- package/dist/commands/app/create.js +609 -0
- package/dist/commands/app/create.js.map +1 -0
- package/dist/commands/app/deploy.d.ts +14 -0
- package/dist/commands/app/deploy.js +179 -0
- package/dist/commands/app/deploy.js.map +1 -0
- package/dist/commands/app/dev.d.ts +28 -0
- package/dist/commands/app/dev.js +701 -0
- package/dist/commands/app/dev.js.map +1 -0
- package/dist/commands/app/generate.d.ts +14 -0
- package/dist/commands/app/generate.js +179 -0
- package/dist/commands/app/generate.js.map +1 -0
- package/dist/commands/app/info.d.ts +14 -0
- package/dist/commands/app/info.js +179 -0
- package/dist/commands/app/info.js.map +1 -0
- package/dist/commands/app/link.d.ts +118 -0
- package/dist/commands/app/link.js +1120 -0
- package/dist/commands/app/link.js.map +1 -0
- package/dist/commands/auth/login.d.ts +43 -0
- package/dist/commands/auth/login.js +987 -0
- package/dist/commands/auth/login.js.map +1 -0
- package/dist/commands/auth/logout.d.ts +15 -0
- package/dist/commands/auth/logout.js +486 -0
- package/dist/commands/auth/logout.js.map +1 -0
- package/dist/commands/auth/whoami.d.ts +16 -0
- package/dist/commands/auth/whoami.js +531 -0
- package/dist/commands/auth/whoami.js.map +1 -0
- package/dist/commands/dev/listen.d.ts +84 -0
- package/dist/commands/dev/listen.js +1187 -0
- package/dist/commands/dev/listen.js.map +1 -0
- package/dist/commands/dev/trigger.d.ts +88 -0
- package/dist/commands/dev/trigger.js +729 -0
- package/dist/commands/dev/trigger.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +617 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth/authorize-code-exchange.d.ts +50 -0
- package/dist/lib/auth/authorize-code-exchange.js +114 -0
- package/dist/lib/auth/authorize-code-exchange.js.map +1 -0
- package/dist/lib/auth/authorize-url.d.ts +42 -0
- package/dist/lib/auth/authorize-url.js +35 -0
- package/dist/lib/auth/authorize-url.js.map +1 -0
- package/dist/lib/auth/browser.d.ts +16 -0
- package/dist/lib/auth/browser.js +48 -0
- package/dist/lib/auth/browser.js.map +1 -0
- package/dist/lib/auth/config.d.ts +41 -0
- package/dist/lib/auth/config.js +21 -0
- package/dist/lib/auth/config.js.map +1 -0
- package/dist/lib/auth/http-refresher.d.ts +30 -0
- package/dist/lib/auth/http-refresher.js +103 -0
- package/dist/lib/auth/http-refresher.js.map +1 -0
- package/dist/lib/auth/identity.d.ts +26 -0
- package/dist/lib/auth/identity.js +10 -0
- package/dist/lib/auth/identity.js.map +1 -0
- package/dist/lib/auth/index.d.ts +13 -0
- package/dist/lib/auth/index.js +527 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/auth/loopback-server.d.ts +65 -0
- package/dist/lib/auth/loopback-server.js +245 -0
- package/dist/lib/auth/loopback-server.js.map +1 -0
- package/dist/lib/auth/manual-prompt.d.ts +17 -0
- package/dist/lib/auth/manual-prompt.js +20 -0
- package/dist/lib/auth/manual-prompt.js.map +1 -0
- package/dist/lib/auth/pkce.d.ts +24 -0
- package/dist/lib/auth/pkce.js +15 -0
- package/dist/lib/auth/pkce.js.map +1 -0
- package/dist/lib/auth/state.d.ts +20 -0
- package/dist/lib/auth/state.js +19 -0
- package/dist/lib/auth/state.js.map +1 -0
- package/dist/lib/auth/verbose-trace.d.ts +25 -0
- package/dist/lib/auth/verbose-trace.js +17 -0
- package/dist/lib/auth/verbose-trace.js.map +1 -0
- package/dist/lib/credentials/clock.d.ts +18 -0
- package/dist/lib/credentials/clock.js +10 -0
- package/dist/lib/credentials/clock.js.map +1 -0
- package/dist/lib/credentials/index.d.ts +5 -0
- package/dist/lib/credentials/index.js +340 -0
- package/dist/lib/credentials/index.js.map +1 -0
- package/dist/lib/credentials/path.d.ts +26 -0
- package/dist/lib/credentials/path.js +32 -0
- package/dist/lib/credentials/path.js.map +1 -0
- package/dist/lib/credentials/refresher.d.ts +26 -0
- package/dist/lib/credentials/refresher.js +34 -0
- package/dist/lib/credentials/refresher.js.map +1 -0
- package/dist/lib/credentials/schema.d.ts +138 -0
- package/dist/lib/credentials/schema.js +85 -0
- package/dist/lib/credentials/schema.js.map +1 -0
- package/dist/lib/credentials/store.d.ts +102 -0
- package/dist/lib/credentials/store.js +337 -0
- package/dist/lib/credentials/store.js.map +1 -0
- package/dist/lib/credentials/types.d.ts +65 -0
- package/dist/lib/credentials/types.js +1 -0
- package/dist/lib/credentials/types.js.map +1 -0
- package/dist/lib/dev-store-gate.d.ts +66 -0
- package/dist/lib/dev-store-gate.js +15 -0
- package/dist/lib/dev-store-gate.js.map +1 -0
- package/dist/lib/errors.d.ts +60 -0
- package/dist/lib/errors.js +101 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/manifest/index.d.ts +2 -0
- package/dist/lib/manifest/index.js +144 -0
- package/dist/lib/manifest/index.js.map +1 -0
- package/dist/lib/manifest/read-manifest.d.ts +32 -0
- package/dist/lib/manifest/read-manifest.js +87 -0
- package/dist/lib/manifest/read-manifest.js.map +1 -0
- package/dist/lib/manifest/write-client-id.d.ts +33 -0
- package/dist/lib/manifest/write-client-id.js +94 -0
- package/dist/lib/manifest/write-client-id.js.map +1 -0
- package/dist/lib/messages.d.ts +15 -0
- package/dist/lib/messages.js +16 -0
- package/dist/lib/messages.js.map +1 -0
- package/dist/lib/orchestrator/child-runner.d.ts +140 -0
- package/dist/lib/orchestrator/child-runner.js +471 -0
- package/dist/lib/orchestrator/child-runner.js.map +1 -0
- package/dist/lib/preview/index.d.ts +11 -0
- package/dist/lib/preview/index.js +17 -0
- package/dist/lib/preview/index.js.map +1 -0
- package/dist/lib/preview/types.d.ts +16 -0
- package/dist/lib/preview/types.js +11 -0
- package/dist/lib/preview/types.js.map +1 -0
- package/dist/lib/ratio-command.d.ts +52 -0
- package/dist/lib/ratio-command.js +141 -0
- package/dist/lib/ratio-command.js.map +1 -0
- package/dist/lib/relay/index.d.ts +7 -0
- package/dist/lib/relay/index.js +362 -0
- package/dist/lib/relay/index.js.map +1 -0
- package/dist/lib/relay/relay-client.d.ts +91 -0
- package/dist/lib/relay/relay-client.js +362 -0
- package/dist/lib/relay/relay-client.js.map +1 -0
- package/dist/lib/relay/types.d.ts +71 -0
- package/dist/lib/relay/types.js +1 -0
- package/dist/lib/relay/types.js.map +1 -0
- package/dist/lib/render-error.d.ts +35 -0
- package/dist/lib/render-error.js +115 -0
- package/dist/lib/render-error.js.map +1 -0
- package/dist/lib/verbose-flag.d.ts +12 -0
- package/dist/lib/verbose-flag.js +17 -0
- package/dist/lib/verbose-flag.js.map +1 -0
- package/dist/render-CrHGqTPH.d.ts +115 -0
- package/dist/templates/.gitkeep +0 -0
- package/dist/templates/minimal/.env.example.tmpl +4 -0
- package/dist/templates/minimal/README.md.tmpl +26 -0
- package/dist/templates/minimal/gitignore +5 -0
- package/dist/templates/minimal/manifest.json +5 -0
- package/dist/templates/minimal/package.json.tmpl +25 -0
- package/dist/templates/minimal/ratio.config.jsonc.tmpl +17 -0
- package/dist/templates/minimal/src/index.ts.tmpl +26 -0
- package/dist/templates/minimal/src/install.ts.tmpl +37 -0
- package/dist/templates/minimal/src/webhooks.ts.tmpl +57 -0
- package/dist/templates/minimal/tsconfig.json +17 -0
- package/dist/templates/serverless/README.md.tmpl +28 -0
- package/dist/templates/serverless/gitignore +6 -0
- package/dist/templates/serverless/manifest.json +5 -0
- package/dist/templates/serverless/package.json.tmpl +22 -0
- package/dist/templates/serverless/ratio.config.jsonc.tmpl +17 -0
- package/dist/templates/serverless/src/index.ts.tmpl +20 -0
- package/dist/templates/serverless/src/install.ts.tmpl +20 -0
- package/dist/templates/serverless/src/webhooks.ts.tmpl +36 -0
- package/dist/templates/serverless/tsconfig.json +18 -0
- package/dist/templates/serverless/wrangler.toml.tmpl +9 -0
- package/dist/templates/with-admin-ui/.env.example.tmpl +4 -0
- package/dist/templates/with-admin-ui/README.md.tmpl +28 -0
- package/dist/templates/with-admin-ui/gitignore +6 -0
- package/dist/templates/with-admin-ui/manifest.json +5 -0
- package/dist/templates/with-admin-ui/package.json.tmpl +31 -0
- package/dist/templates/with-admin-ui/ratio.config.jsonc.tmpl +17 -0
- package/dist/templates/with-admin-ui/src/admin/app/globals.css +19 -0
- package/dist/templates/with-admin-ui/src/admin/app/layout.tsx.tmpl +13 -0
- package/dist/templates/with-admin-ui/src/admin/app/page.tsx.tmpl +8 -0
- package/dist/templates/with-admin-ui/src/admin/next-env.d.ts.tmpl +5 -0
- package/dist/templates/with-admin-ui/src/admin/next.config.mjs +3 -0
- package/dist/templates/with-admin-ui/src/admin/tsconfig.json +20 -0
- package/dist/templates/with-admin-ui/src/index.ts.tmpl +26 -0
- package/dist/templates/with-admin-ui/src/install.ts.tmpl +37 -0
- package/dist/templates/with-admin-ui/src/webhooks.ts.tmpl +57 -0
- package/dist/templates/with-admin-ui/tsconfig.json +17 -0
- package/package.json +78 -0
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
// src/commands/dev/trigger.ts
|
|
2
|
+
import { Args, Flags as Flags2 } from "@oclif/core";
|
|
3
|
+
import { z as z2 } from "zod";
|
|
4
|
+
|
|
5
|
+
// src/lib/auth/config.ts
|
|
6
|
+
var DEFAULT_SERVER_URL = "https://gkx.ratio.win";
|
|
7
|
+
function resolveServerUrl(opts) {
|
|
8
|
+
const env = opts?.env ?? process.env;
|
|
9
|
+
if (typeof env["RATIO_SERVER_URL"] === "string" && env["RATIO_SERVER_URL"].length > 0) {
|
|
10
|
+
return env["RATIO_SERVER_URL"];
|
|
11
|
+
}
|
|
12
|
+
if (typeof opts?.credentials?.server === "string" && opts.credentials.server.length > 0) {
|
|
13
|
+
return opts.credentials.server;
|
|
14
|
+
}
|
|
15
|
+
return DEFAULT_SERVER_URL;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/lib/credentials/clock.ts
|
|
19
|
+
var SystemClock = class {
|
|
20
|
+
now() {
|
|
21
|
+
return Date.now();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/lib/credentials/path.ts
|
|
26
|
+
import os from "os";
|
|
27
|
+
import path from "path";
|
|
28
|
+
function snapshotPathEnv() {
|
|
29
|
+
const snapshot = {};
|
|
30
|
+
const xdgConfigHome = process.env["XDG_CONFIG_HOME"];
|
|
31
|
+
if (xdgConfigHome !== void 0) {
|
|
32
|
+
snapshot.XDG_CONFIG_HOME = xdgConfigHome;
|
|
33
|
+
}
|
|
34
|
+
const appData = process.env["APPDATA"];
|
|
35
|
+
if (appData !== void 0) {
|
|
36
|
+
snapshot.APPDATA = appData;
|
|
37
|
+
}
|
|
38
|
+
return snapshot;
|
|
39
|
+
}
|
|
40
|
+
function defaultCredentialsPath(platform, homedir, env) {
|
|
41
|
+
const resolvedPlatform = platform ?? process.platform;
|
|
42
|
+
const resolvedHome = homedir ?? os.homedir();
|
|
43
|
+
const resolvedEnv = env ?? snapshotPathEnv();
|
|
44
|
+
if (resolvedPlatform === "win32") {
|
|
45
|
+
const appData = resolvedEnv.APPDATA;
|
|
46
|
+
const base2 = appData !== void 0 && appData !== "" ? appData : path.win32.join(resolvedHome, "AppData", "Roaming");
|
|
47
|
+
return path.win32.join(base2, "ratio", "credentials");
|
|
48
|
+
}
|
|
49
|
+
const xdgConfigHome = resolvedEnv.XDG_CONFIG_HOME;
|
|
50
|
+
const base = typeof xdgConfigHome === "string" && xdgConfigHome !== "" ? xdgConfigHome : path.posix.join(resolvedHome, ".config");
|
|
51
|
+
return path.posix.join(base, "ratio", "credentials");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/lib/errors.ts
|
|
55
|
+
var RatioCLIError = class extends Error {
|
|
56
|
+
code;
|
|
57
|
+
exitCode;
|
|
58
|
+
hint;
|
|
59
|
+
docsUrl;
|
|
60
|
+
constructor(opts) {
|
|
61
|
+
super(opts.message);
|
|
62
|
+
this.name = new.target.name;
|
|
63
|
+
this.code = opts.code;
|
|
64
|
+
this.exitCode = opts.exitCode;
|
|
65
|
+
if (opts.hint !== void 0) this.hint = opts.hint;
|
|
66
|
+
if (opts.docsUrl !== void 0) this.docsUrl = opts.docsUrl;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var NotLoggedInError = class extends RatioCLIError {
|
|
70
|
+
constructor(message, hint = "Run 'ratio auth login' to sign in.") {
|
|
71
|
+
super({ code: "NOT_LOGGED_IN", exitCode: 3, message, hint });
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var TokenExpiredError = class extends RatioCLIError {
|
|
75
|
+
constructor(message, hint = "Run 'ratio auth login' to refresh your session.") {
|
|
76
|
+
super({ code: "TOKEN_EXPIRED", exitCode: 3, message, hint });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var NetworkError = class extends RatioCLIError {
|
|
80
|
+
constructor(message, hint = "Check your network and retry.") {
|
|
81
|
+
super({ code: "NETWORK_ERROR", exitCode: 5, message, hint });
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var ValidationError = class extends RatioCLIError {
|
|
85
|
+
constructor(message, hint) {
|
|
86
|
+
const opts = {
|
|
87
|
+
code: "VALIDATION_ERROR",
|
|
88
|
+
exitCode: 8,
|
|
89
|
+
message
|
|
90
|
+
};
|
|
91
|
+
if (hint !== void 0) opts.hint = hint;
|
|
92
|
+
super(opts);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
function isRatioCLIError(err) {
|
|
96
|
+
return err instanceof RatioCLIError;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/lib/credentials/refresher.ts
|
|
100
|
+
var NotImplementedRefresher = class {
|
|
101
|
+
async refresh(refreshToken) {
|
|
102
|
+
void refreshToken;
|
|
103
|
+
throw new NotLoggedInError(
|
|
104
|
+
"Token refresh is not available yet in this CLI version."
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/lib/credentials/store.ts
|
|
110
|
+
import crypto from "crypto";
|
|
111
|
+
import fs from "fs";
|
|
112
|
+
import { dirname } from "path";
|
|
113
|
+
|
|
114
|
+
// src/lib/credentials/schema.ts
|
|
115
|
+
import { z } from "zod";
|
|
116
|
+
var identitySchema = z.object({
|
|
117
|
+
developerId: z.string(),
|
|
118
|
+
email: z.string(),
|
|
119
|
+
// Token-derived fields captured at login. Optional (additive) so files
|
|
120
|
+
// written before these fields existed still validate as version 1.
|
|
121
|
+
merchantId: z.string().optional(),
|
|
122
|
+
scopes: z.array(z.string()).optional()
|
|
123
|
+
}).strict();
|
|
124
|
+
var profileSchema = z.object({
|
|
125
|
+
accessToken: z.string(),
|
|
126
|
+
refreshToken: z.string(),
|
|
127
|
+
expiresAt: z.string().datetime({ offset: true }),
|
|
128
|
+
identity: identitySchema,
|
|
129
|
+
obtainedAt: z.string().datetime({ offset: true })
|
|
130
|
+
}).strict();
|
|
131
|
+
var developerSessionSchema = z.object({
|
|
132
|
+
jwt: z.string(),
|
|
133
|
+
developerId: z.string(),
|
|
134
|
+
email: z.string(),
|
|
135
|
+
expiresAt: z.string().datetime({ offset: true }),
|
|
136
|
+
obtainedAt: z.string().datetime({ offset: true })
|
|
137
|
+
}).strict();
|
|
138
|
+
var credentialsSchema = z.object({
|
|
139
|
+
version: z.literal(1),
|
|
140
|
+
server: z.string(),
|
|
141
|
+
clientId: z.string(),
|
|
142
|
+
profiles: z.record(z.string(), profileSchema),
|
|
143
|
+
// Optional cached developer session. Additive so files written before
|
|
144
|
+
// this field existed still validate as version 1.
|
|
145
|
+
developer: developerSessionSchema.optional()
|
|
146
|
+
}).strict();
|
|
147
|
+
function parseCredentials(input) {
|
|
148
|
+
if (typeof input === "object" && input !== null && "version" in input) {
|
|
149
|
+
const version = input.version;
|
|
150
|
+
if (version !== 1) {
|
|
151
|
+
const label = typeof version === "number" ? String(version) : "unknown";
|
|
152
|
+
throw new ValidationError(
|
|
153
|
+
`credentials file version ${label} is unsupported by this CLI \u2014 upgrade the CLI or sign in again`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const result = credentialsSchema.safeParse(input);
|
|
158
|
+
if (!result.success) {
|
|
159
|
+
throw new ValidationError(
|
|
160
|
+
"credentials file schema mismatch \u2014 expected v1 shape"
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return result.data;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/lib/credentials/store.ts
|
|
167
|
+
var REFRESH_WINDOW_MS = 3e5;
|
|
168
|
+
var ENV_TOKEN_VAR = "RATIO_API_TOKEN";
|
|
169
|
+
var ENV_DEVELOPER_TOKEN_VAR = "RATIO_DEVELOPER_TOKEN";
|
|
170
|
+
function hasErrnoCode(err, code) {
|
|
171
|
+
return err instanceof Error && err.code === code;
|
|
172
|
+
}
|
|
173
|
+
var FileCredentialStore = class {
|
|
174
|
+
filePath;
|
|
175
|
+
clock;
|
|
176
|
+
refresher;
|
|
177
|
+
envReader;
|
|
178
|
+
platform;
|
|
179
|
+
constructor(opts) {
|
|
180
|
+
this.filePath = opts?.filePath ?? defaultCredentialsPath();
|
|
181
|
+
this.clock = opts?.clock ?? new SystemClock();
|
|
182
|
+
this.refresher = opts?.refresher ?? new NotImplementedRefresher();
|
|
183
|
+
this.envReader = opts?.envReader ?? (() => process.env);
|
|
184
|
+
this.platform = opts?.platform ?? process.platform;
|
|
185
|
+
}
|
|
186
|
+
async load() {
|
|
187
|
+
this.reapplyFileModeIfDrifted();
|
|
188
|
+
let raw;
|
|
189
|
+
try {
|
|
190
|
+
raw = fs.readFileSync(this.filePath, "utf8");
|
|
191
|
+
} catch (err) {
|
|
192
|
+
if (hasErrnoCode(err, "ENOENT")) {
|
|
193
|
+
throw new NotLoggedInError("You are not signed in.");
|
|
194
|
+
}
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
let parsed;
|
|
198
|
+
try {
|
|
199
|
+
parsed = JSON.parse(raw);
|
|
200
|
+
} catch {
|
|
201
|
+
throw new ValidationError(
|
|
202
|
+
'credentials file is not valid JSON \u2014 run "ratio auth login" to reset'
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return parseCredentials(parsed);
|
|
206
|
+
}
|
|
207
|
+
async save(creds) {
|
|
208
|
+
const dir = dirname(this.filePath);
|
|
209
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
210
|
+
if (this.platform !== "win32") {
|
|
211
|
+
fs.chmodSync(dir, 448);
|
|
212
|
+
}
|
|
213
|
+
const tmpPath = `${this.filePath}.${process.pid}.${crypto.randomBytes(8).toString("hex")}.tmp`;
|
|
214
|
+
try {
|
|
215
|
+
fs.writeFileSync(tmpPath, JSON.stringify(creds, null, 2), {
|
|
216
|
+
mode: 384
|
|
217
|
+
});
|
|
218
|
+
fs.renameSync(tmpPath, this.filePath);
|
|
219
|
+
} catch (err) {
|
|
220
|
+
try {
|
|
221
|
+
fs.unlinkSync(tmpPath);
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
226
|
+
if (this.platform !== "win32") {
|
|
227
|
+
fs.chmodSync(this.filePath, 384);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async clear() {
|
|
231
|
+
try {
|
|
232
|
+
fs.unlinkSync(this.filePath);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
if (hasErrnoCode(err, "ENOENT")) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async getValidAccessToken(profileName) {
|
|
241
|
+
const envToken = this.envReader()[ENV_TOKEN_VAR];
|
|
242
|
+
if (typeof envToken === "string" && envToken.length > 0) {
|
|
243
|
+
return envToken;
|
|
244
|
+
}
|
|
245
|
+
const creds = await this.load();
|
|
246
|
+
const name = profileName ?? "default";
|
|
247
|
+
const profile = creds.profiles[name];
|
|
248
|
+
if (profile === void 0) {
|
|
249
|
+
throw new NotLoggedInError("You are not signed in.");
|
|
250
|
+
}
|
|
251
|
+
const msUntilExpiry = new Date(profile.expiresAt).getTime() - this.clock.now();
|
|
252
|
+
if (msUntilExpiry < REFRESH_WINDOW_MS) {
|
|
253
|
+
return this.refreshAndPersist(creds, name, profile);
|
|
254
|
+
}
|
|
255
|
+
return profile.accessToken;
|
|
256
|
+
}
|
|
257
|
+
async getValidDeveloperToken() {
|
|
258
|
+
const envToken = this.envReader()[ENV_DEVELOPER_TOKEN_VAR];
|
|
259
|
+
if (typeof envToken === "string" && envToken.length > 0) {
|
|
260
|
+
return envToken;
|
|
261
|
+
}
|
|
262
|
+
let creds;
|
|
263
|
+
try {
|
|
264
|
+
creds = await this.load();
|
|
265
|
+
} catch (err) {
|
|
266
|
+
if (err instanceof NotLoggedInError) {
|
|
267
|
+
throw new NotLoggedInError("Developer signin required.");
|
|
268
|
+
}
|
|
269
|
+
throw err;
|
|
270
|
+
}
|
|
271
|
+
const session = creds.developer;
|
|
272
|
+
if (session === void 0) {
|
|
273
|
+
throw new NotLoggedInError("Developer signin required.");
|
|
274
|
+
}
|
|
275
|
+
const msUntilExpiry = new Date(session.expiresAt).getTime() - this.clock.now();
|
|
276
|
+
if (msUntilExpiry < REFRESH_WINDOW_MS) {
|
|
277
|
+
throw new NotLoggedInError("Developer signin required.");
|
|
278
|
+
}
|
|
279
|
+
return session.jwt;
|
|
280
|
+
}
|
|
281
|
+
async saveDeveloperSession(session) {
|
|
282
|
+
let creds;
|
|
283
|
+
try {
|
|
284
|
+
creds = await this.load();
|
|
285
|
+
} catch (err) {
|
|
286
|
+
if (err instanceof NotLoggedInError) {
|
|
287
|
+
creds = { version: 1, server: "", clientId: "", profiles: {} };
|
|
288
|
+
} else {
|
|
289
|
+
throw err;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const updated = {
|
|
293
|
+
...creds,
|
|
294
|
+
developer: session
|
|
295
|
+
};
|
|
296
|
+
await this.save(updated);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Exchange the profile's refresh token for a fresh token set, merge it
|
|
300
|
+
* into the loaded credentials (preserving every other profile), persist
|
|
301
|
+
* atomically, and return the new access token. A refresher failure is
|
|
302
|
+
* wrapped in TokenExpiredError; the underlying message is deliberately
|
|
303
|
+
* discarded so a secret can never leak through the error surface.
|
|
304
|
+
*/
|
|
305
|
+
async refreshAndPersist(creds, name, profile) {
|
|
306
|
+
let refreshed;
|
|
307
|
+
try {
|
|
308
|
+
refreshed = await this.refresher.refresh(profile.refreshToken);
|
|
309
|
+
} catch {
|
|
310
|
+
throw new TokenExpiredError(
|
|
311
|
+
"Your session has expired and could not be refreshed."
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const updated = {
|
|
315
|
+
...creds,
|
|
316
|
+
profiles: {
|
|
317
|
+
...creds.profiles,
|
|
318
|
+
[name]: {
|
|
319
|
+
...profile,
|
|
320
|
+
accessToken: refreshed.accessToken,
|
|
321
|
+
refreshToken: refreshed.refreshToken,
|
|
322
|
+
expiresAt: refreshed.expiresAt
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
await this.save(updated);
|
|
327
|
+
return refreshed.accessToken;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* POSIX-only startup permission check: if the file exists with a mode
|
|
331
|
+
* other than 0600, re-apply 0600 and emit a warning to stderr. Never
|
|
332
|
+
* runs on Windows (stat modes there do not map to POSIX bits and chmod
|
|
333
|
+
* must not be called). Missing file is NOT an error here — load()
|
|
334
|
+
* reports it as NotLoggedInError.
|
|
335
|
+
*/
|
|
336
|
+
reapplyFileModeIfDrifted() {
|
|
337
|
+
if (this.platform === "win32") {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
let stat;
|
|
341
|
+
try {
|
|
342
|
+
stat = fs.statSync(this.filePath);
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (hasErrnoCode(err, "ENOENT")) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
throw err;
|
|
348
|
+
}
|
|
349
|
+
const mode = stat.mode & 511;
|
|
350
|
+
if (mode !== 384) {
|
|
351
|
+
fs.chmodSync(this.filePath, 384);
|
|
352
|
+
process.stderr.write(
|
|
353
|
+
`[warn] credentials file permissions were 0o${mode.toString(8)}; corrected to 0600.
|
|
354
|
+
`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// src/lib/ratio-command.ts
|
|
361
|
+
import { Command, Errors } from "@oclif/core";
|
|
362
|
+
|
|
363
|
+
// src/lib/render-error.ts
|
|
364
|
+
function isOclifParserError(err) {
|
|
365
|
+
return err instanceof Error && "parse" in err && err.parse !== null && err.parse !== void 0;
|
|
366
|
+
}
|
|
367
|
+
function buildErrorEnvelope(code, message, hint, docsUrl) {
|
|
368
|
+
const obj = { ok: false, code, message };
|
|
369
|
+
if (hint !== void 0) obj["hint"] = hint;
|
|
370
|
+
if (docsUrl !== void 0) obj["docsUrl"] = docsUrl;
|
|
371
|
+
return JSON.stringify(obj) + "\n";
|
|
372
|
+
}
|
|
373
|
+
function renderError(input) {
|
|
374
|
+
const { err, verbose, json } = input;
|
|
375
|
+
if (isRatioCLIError(err)) {
|
|
376
|
+
const ratioErr = err;
|
|
377
|
+
if (ratioErr.exitCode === 0) {
|
|
378
|
+
if (json) {
|
|
379
|
+
return {
|
|
380
|
+
stdout: JSON.stringify({ ok: true, data: null }) + "\n",
|
|
381
|
+
stderr: "",
|
|
382
|
+
exitCode: 0
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
stdout: ratioErr.message + "\n",
|
|
387
|
+
stderr: "",
|
|
388
|
+
exitCode: 0
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
let stderrLines = `${ratioErr.code}: ${ratioErr.message}
|
|
392
|
+
`;
|
|
393
|
+
if (ratioErr.hint !== void 0) stderrLines += `${ratioErr.hint}
|
|
394
|
+
`;
|
|
395
|
+
if (ratioErr.docsUrl !== void 0) stderrLines += `${ratioErr.docsUrl}
|
|
396
|
+
`;
|
|
397
|
+
if (json) {
|
|
398
|
+
return {
|
|
399
|
+
stdout: buildErrorEnvelope(ratioErr.code, ratioErr.message, ratioErr.hint, ratioErr.docsUrl),
|
|
400
|
+
stderr: verbose && ratioErr.stack ? ratioErr.stack + "\n" : "",
|
|
401
|
+
exitCode: ratioErr.exitCode
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (verbose && ratioErr.stack) {
|
|
405
|
+
stderrLines += ratioErr.stack + "\n";
|
|
406
|
+
}
|
|
407
|
+
return { stdout: "", stderr: stderrLines, exitCode: ratioErr.exitCode };
|
|
408
|
+
}
|
|
409
|
+
if (isOclifParserError(err)) {
|
|
410
|
+
const parserErr = err;
|
|
411
|
+
const msg = parserErr.message || "Unknown parse error";
|
|
412
|
+
const coercedErr = new ValidationError(msg);
|
|
413
|
+
let stderrText2 = `${coercedErr.code}: ${msg}
|
|
414
|
+
`;
|
|
415
|
+
if (json) {
|
|
416
|
+
return {
|
|
417
|
+
stdout: buildErrorEnvelope(coercedErr.code, msg),
|
|
418
|
+
stderr: verbose && parserErr.stack ? parserErr.stack + "\n" : "",
|
|
419
|
+
exitCode: 8
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (verbose && parserErr.stack) {
|
|
423
|
+
stderrText2 += parserErr.stack + "\n";
|
|
424
|
+
}
|
|
425
|
+
return { stdout: "", stderr: stderrText2, exitCode: 8 };
|
|
426
|
+
}
|
|
427
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
428
|
+
const stack = err instanceof Error ? err.stack : void 0;
|
|
429
|
+
const stderrPrefix = `INTERNAL: ${message}
|
|
430
|
+
`;
|
|
431
|
+
if (json) {
|
|
432
|
+
return {
|
|
433
|
+
stdout: buildErrorEnvelope("INTERNAL", message),
|
|
434
|
+
stderr: verbose && stack ? stack + "\n" : "",
|
|
435
|
+
exitCode: 1
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
let stderrText = stderrPrefix;
|
|
439
|
+
if (verbose && stack) {
|
|
440
|
+
stderrText += stack + "\n";
|
|
441
|
+
}
|
|
442
|
+
return { stdout: "", stderr: stderrText, exitCode: 1 };
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// src/lib/ratio-command.ts
|
|
446
|
+
var RatioCommand = class extends Command {
|
|
447
|
+
_getRenderFlags() {
|
|
448
|
+
const argv = this.argv;
|
|
449
|
+
return {
|
|
450
|
+
verbose: argv.includes("--verbose"),
|
|
451
|
+
json: argv.includes("--json")
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
async catch(err) {
|
|
455
|
+
const { verbose, json } = this._getRenderFlags();
|
|
456
|
+
const output = renderError({ err, verbose, json });
|
|
457
|
+
if (output.stdout) process.stdout.write(output.stdout);
|
|
458
|
+
if (output.stderr) process.stderr.write(output.stderr);
|
|
459
|
+
if (output.exitCode === 0) {
|
|
460
|
+
throw new Errors.ExitError(0);
|
|
461
|
+
}
|
|
462
|
+
err.skipOclifErrorHandling = true;
|
|
463
|
+
err.oclif = { exit: output.exitCode };
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// src/lib/verbose-flag.ts
|
|
469
|
+
import { Flags } from "@oclif/core";
|
|
470
|
+
var verboseFlag = {
|
|
471
|
+
verbose: Flags.boolean({
|
|
472
|
+
description: "Show diagnostic detail: HTTP status/timing lines for auth requests (stderr) and stack traces for internal errors."
|
|
473
|
+
})
|
|
474
|
+
};
|
|
475
|
+
var jsonFlag = {
|
|
476
|
+
json: Flags.boolean({
|
|
477
|
+
description: "Emit machine-readable JSON on stdout."
|
|
478
|
+
})
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/commands/dev/trigger.ts
|
|
482
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
483
|
+
var SCENARIO_OPTIONS = ["cod", "prepaid", "cancelled"];
|
|
484
|
+
var triggerResponseSchema = z2.object({
|
|
485
|
+
"deliveryId": z2.string().min(1)
|
|
486
|
+
});
|
|
487
|
+
var scenarioEntrySchema = z2.object({
|
|
488
|
+
"topic": z2.string(),
|
|
489
|
+
"aliases": z2.array(z2.string()),
|
|
490
|
+
"scenarios": z2.array(z2.string()),
|
|
491
|
+
"sampleShape": z2.unknown()
|
|
492
|
+
});
|
|
493
|
+
var scenariosResponseSchema = z2.array(scenarioEntrySchema);
|
|
494
|
+
function errorForStatus(status) {
|
|
495
|
+
if (status === 400) {
|
|
496
|
+
return new ValidationError(
|
|
497
|
+
"The server rejected the request.",
|
|
498
|
+
"Unknown topic; run --list to see options."
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
if (status === 401) {
|
|
502
|
+
return new NotLoggedInError("The server rejected your session token.");
|
|
503
|
+
}
|
|
504
|
+
if (status === 404) {
|
|
505
|
+
return new ValidationError(
|
|
506
|
+
"App not found.",
|
|
507
|
+
"App not found or not accessible with this identity; verify --app-id."
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
if (status === 429) {
|
|
511
|
+
return new NetworkError(
|
|
512
|
+
"Too many requests.",
|
|
513
|
+
"You are being rate-limited; wait a moment and retry."
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
return new NetworkError(
|
|
517
|
+
`The server had a problem processing your request (status: ${status}); try again.`
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
async function requestJson(url, init) {
|
|
521
|
+
const controller = new AbortController();
|
|
522
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
523
|
+
let response;
|
|
524
|
+
try {
|
|
525
|
+
response = await fetch(url, { ...init, signal: controller.signal });
|
|
526
|
+
} catch {
|
|
527
|
+
throw new NetworkError("Could not reach the Ratio platform.");
|
|
528
|
+
} finally {
|
|
529
|
+
clearTimeout(timer);
|
|
530
|
+
}
|
|
531
|
+
if (!response.ok) {
|
|
532
|
+
await response.arrayBuffer().catch(() => void 0);
|
|
533
|
+
throw errorForStatus(response.status);
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
return await response.json();
|
|
537
|
+
} catch {
|
|
538
|
+
throw new NetworkError("The server response was not the expected shape.");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function postTrigger(serverUrl, token, body) {
|
|
542
|
+
const base = serverUrl.replace(/\/+$/, "");
|
|
543
|
+
const raw = await requestJson(`${base}/dev/trigger`, {
|
|
544
|
+
method: "POST",
|
|
545
|
+
headers: {
|
|
546
|
+
Authorization: `Bearer ${token}`,
|
|
547
|
+
"content-type": "application/json"
|
|
548
|
+
},
|
|
549
|
+
body: JSON.stringify({
|
|
550
|
+
"topic": body.topic,
|
|
551
|
+
"scenario": body.scenario,
|
|
552
|
+
"appId": body.appId
|
|
553
|
+
})
|
|
554
|
+
});
|
|
555
|
+
const parsed = triggerResponseSchema.safeParse(raw);
|
|
556
|
+
if (!parsed.success) {
|
|
557
|
+
throw new NetworkError("The server response was not the expected shape.");
|
|
558
|
+
}
|
|
559
|
+
return parsed.data;
|
|
560
|
+
}
|
|
561
|
+
async function getScenarios(serverUrl, token) {
|
|
562
|
+
const base = serverUrl.replace(/\/+$/, "");
|
|
563
|
+
const raw = await requestJson(`${base}/dev/scenarios`, {
|
|
564
|
+
method: "GET",
|
|
565
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
566
|
+
});
|
|
567
|
+
const parsed = scenariosResponseSchema.safeParse(raw);
|
|
568
|
+
if (!parsed.success) {
|
|
569
|
+
throw new NetworkError("The server response was not the expected shape.");
|
|
570
|
+
}
|
|
571
|
+
return parsed.data;
|
|
572
|
+
}
|
|
573
|
+
function summarizeSampleShape(sample) {
|
|
574
|
+
if (typeof sample !== "object" || sample === null || Array.isArray(sample)) {
|
|
575
|
+
return "-";
|
|
576
|
+
}
|
|
577
|
+
const keys = Object.keys(sample);
|
|
578
|
+
return keys.length > 0 ? keys.join(", ") : "-";
|
|
579
|
+
}
|
|
580
|
+
function renderScenarioTable(entries) {
|
|
581
|
+
if (entries.length === 0) {
|
|
582
|
+
return "No scenarios are available yet.\n";
|
|
583
|
+
}
|
|
584
|
+
const header = "TOPIC";
|
|
585
|
+
const topicWidth = Math.max(
|
|
586
|
+
header.length,
|
|
587
|
+
...entries.map((entry) => entry.topic.length)
|
|
588
|
+
);
|
|
589
|
+
const lines = [
|
|
590
|
+
`${header.padEnd(topicWidth)} SCENARIOS SAMPLE FIELDS`
|
|
591
|
+
];
|
|
592
|
+
for (const entry of entries) {
|
|
593
|
+
const scenarioCol = entry.scenarios.join(", ").padEnd(23);
|
|
594
|
+
lines.push(
|
|
595
|
+
`${entry.topic.padEnd(topicWidth)} ${scenarioCol} ${summarizeSampleShape(entry.sampleShape)}`
|
|
596
|
+
);
|
|
597
|
+
if (entry.aliases.length > 0) {
|
|
598
|
+
lines.push(
|
|
599
|
+
`${"".padEnd(topicWidth)} Also accepted: ${entry.aliases.join(", ")}`
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return `${lines.join("\n")}
|
|
604
|
+
`;
|
|
605
|
+
}
|
|
606
|
+
var DevTrigger = class _DevTrigger extends RatioCommand {
|
|
607
|
+
static description = "Fire a synthetic Ratio webhook event for local testing.";
|
|
608
|
+
static examples = [
|
|
609
|
+
"<%= config.bin %> dev trigger --list",
|
|
610
|
+
"<%= config.bin %> dev trigger orders.created --scenario=cod --app-id=4f9d2c31-8a6b-4f2e-9c1d-3e5a7b9d0f12",
|
|
611
|
+
"<%= config.bin %> dev trigger --list --json"
|
|
612
|
+
];
|
|
613
|
+
static args = {
|
|
614
|
+
topic: Args.string({
|
|
615
|
+
name: "topic",
|
|
616
|
+
required: false,
|
|
617
|
+
description: "Event topic, e.g. orders.created."
|
|
618
|
+
})
|
|
619
|
+
};
|
|
620
|
+
static flags = {
|
|
621
|
+
...verboseFlag,
|
|
622
|
+
...jsonFlag,
|
|
623
|
+
scenario: Flags2.string({
|
|
624
|
+
options: [...SCENARIO_OPTIONS],
|
|
625
|
+
description: "Payload scenario."
|
|
626
|
+
}),
|
|
627
|
+
list: Flags2.boolean({
|
|
628
|
+
description: "List available topics and scenarios."
|
|
629
|
+
}),
|
|
630
|
+
"app-id": Flags2.string({
|
|
631
|
+
description: "App id whose synthetic delivery to trigger (UUID). Required when a topic is given."
|
|
632
|
+
})
|
|
633
|
+
};
|
|
634
|
+
// ── Seams: overridable in tests; defaults hit the real process. ──────────
|
|
635
|
+
writeOut = (chunk) => {
|
|
636
|
+
process.stdout.write(chunk);
|
|
637
|
+
};
|
|
638
|
+
writeErr = (chunk) => {
|
|
639
|
+
process.stderr.write(chunk);
|
|
640
|
+
};
|
|
641
|
+
makeCredentialStore = () => new FileCredentialStore();
|
|
642
|
+
async run() {
|
|
643
|
+
const { args, flags } = await this.parse(_DevTrigger);
|
|
644
|
+
const jsonMode = flags.json === true;
|
|
645
|
+
const verbose = flags.verbose === true;
|
|
646
|
+
const trace = (message) => {
|
|
647
|
+
if (verbose) {
|
|
648
|
+
this.writeErr(`${message}
|
|
649
|
+
`);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
if (flags.list === true) {
|
|
653
|
+
await this.runList(jsonMode, trace);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const topic = args.topic;
|
|
657
|
+
if (topic === void 0) {
|
|
658
|
+
throw new ValidationError(
|
|
659
|
+
"Pass a topic or use --list to see options.",
|
|
660
|
+
"Example: ratio dev trigger orders.created --scenario=cod --app-id=<uuid>"
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
const scenario = flags.scenario;
|
|
664
|
+
if (scenario === void 0) {
|
|
665
|
+
throw new ValidationError(
|
|
666
|
+
"A topic requires a --scenario.",
|
|
667
|
+
"Pass --scenario=cod|prepaid|cancelled or run --list to see options."
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
const appId = flags["app-id"];
|
|
671
|
+
if (appId === void 0 || appId === "") {
|
|
672
|
+
throw new ValidationError(
|
|
673
|
+
"A topic requires an --app-id.",
|
|
674
|
+
"Pass --app-id=<uuid>."
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
const store = this.makeCredentialStore();
|
|
678
|
+
const token = await store.getValidAccessToken();
|
|
679
|
+
const serverUrl = resolveServerUrl();
|
|
680
|
+
trace(`server: ${serverUrl}`);
|
|
681
|
+
trace(`trigger: topic=${topic} scenario=${scenario}`);
|
|
682
|
+
const started = Date.now();
|
|
683
|
+
const result = await postTrigger(serverUrl, token, {
|
|
684
|
+
topic,
|
|
685
|
+
scenario,
|
|
686
|
+
appId
|
|
687
|
+
});
|
|
688
|
+
trace(`trigger accepted in ${Date.now() - started}ms`);
|
|
689
|
+
if (jsonMode) {
|
|
690
|
+
this.writeOut(
|
|
691
|
+
`${JSON.stringify({ ok: true, data: { "deliveryId": result.deliveryId } })}
|
|
692
|
+
`
|
|
693
|
+
);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
this.writeOut(
|
|
697
|
+
`Triggered ${topic} scenario=${scenario} deliveryId=${result.deliveryId}
|
|
698
|
+
`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
/** Handle `--list`: fetch the catalog and render table or JSON envelope. */
|
|
702
|
+
async runList(jsonMode, trace) {
|
|
703
|
+
const store = this.makeCredentialStore();
|
|
704
|
+
const token = await store.getValidAccessToken();
|
|
705
|
+
const serverUrl = resolveServerUrl();
|
|
706
|
+
trace(`server: ${serverUrl}`);
|
|
707
|
+
const started = Date.now();
|
|
708
|
+
const entries = await getScenarios(serverUrl, token);
|
|
709
|
+
trace(
|
|
710
|
+
`scenarios fetched in ${Date.now() - started}ms (${entries.length} topics)`
|
|
711
|
+
);
|
|
712
|
+
if (jsonMode) {
|
|
713
|
+
this.writeOut(`${JSON.stringify({ ok: true, data: entries })}
|
|
714
|
+
`);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
this.writeOut(renderScenarioTable(entries));
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
export {
|
|
721
|
+
REQUEST_TIMEOUT_MS,
|
|
722
|
+
SCENARIO_OPTIONS,
|
|
723
|
+
DevTrigger as default,
|
|
724
|
+
getScenarios,
|
|
725
|
+
postTrigger,
|
|
726
|
+
renderScenarioTable,
|
|
727
|
+
summarizeSampleShape
|
|
728
|
+
};
|
|
729
|
+
//# sourceMappingURL=trigger.js.map
|