@cerkel/cli 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +497 -2
  2. package/dist/api-url.js +42 -0
  3. package/dist/api.js +48 -0
  4. package/dist/bin.js +157 -0
  5. package/dist/chunk.js +31 -0
  6. package/dist/cli.js +497 -0
  7. package/dist/client.js +125 -0
  8. package/dist/command.js +2 -0
  9. package/dist/commands/args.js +107 -0
  10. package/dist/commands/auth.js +255 -0
  11. package/dist/commands/call.js +67 -0
  12. package/dist/commands/collection.js +121 -0
  13. package/dist/commands/content.js +281 -0
  14. package/dist/commands/customer.js +126 -0
  15. package/dist/commands/dev.js +261 -0
  16. package/dist/commands/draft.js +101 -0
  17. package/dist/commands/file.js +53 -0
  18. package/dist/commands/index.js +43 -0
  19. package/dist/commands/inventory.js +103 -0
  20. package/dist/commands/offer.js +92 -0
  21. package/dist/commands/order-args.js +37 -0
  22. package/dist/commands/order.js +219 -0
  23. package/dist/commands/price.js +55 -0
  24. package/dist/commands/product.js +265 -0
  25. package/dist/commands/promo.js +140 -0
  26. package/dist/commands/redirect.js +94 -0
  27. package/dist/commands/report.js +93 -0
  28. package/dist/commands/section.js +233 -0
  29. package/dist/commands/store.js +23 -0
  30. package/dist/commands/theme-tools.js +432 -0
  31. package/dist/commands/theme.js +388 -0
  32. package/dist/commands/whatsapp.js +39 -0
  33. package/dist/commands/zone.js +90 -0
  34. package/dist/commerce.js +138 -0
  35. package/dist/content-files.js +246 -0
  36. package/dist/context.js +5 -0
  37. package/dist/credentials.js +201 -0
  38. package/dist/diff.js +146 -0
  39. package/dist/env.js +24 -0
  40. package/dist/errors.js +73 -0
  41. package/dist/format.js +13 -0
  42. package/dist/layout.js +122 -0
  43. package/dist/logins.js +49 -0
  44. package/dist/raizn-alias.js +6 -0
  45. package/dist/redact.js +27 -0
  46. package/dist/session.js +266 -0
  47. package/dist/state.js +153 -0
  48. package/dist/sync.js +121 -0
  49. package/dist/zip.js +88 -0
  50. package/package.json +37 -3
package/dist/bin.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { createServer } from "node:http";
4
+ import { hostname } from "node:os";
5
+ import { watch } from "node:fs";
6
+ import { readFileSync } from "node:fs";
7
+ import { createInterface } from "node:readline/promises";
8
+ import { Writable } from "node:stream";
9
+ import { main } from "./cli.js";
10
+ import { globalTimers } from "./context.js";
11
+ import { FileCredentialStore, SystemCredentialStore, credentialsFilePath, legacyCredentialsFilePath } from "./credentials.js";
12
+ function version() {
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
15
+ return pkg.version ?? "0.0.0";
16
+ }
17
+ catch {
18
+ return "0.0.0";
19
+ }
20
+ }
21
+ async function ask(question, hidden) {
22
+ // Prompts go to stderr so `cerkel … > out.txt` still shows them and never captures them.
23
+ let muted = false;
24
+ const output = new Writable({
25
+ write(chunk, _enc, cb) {
26
+ if (!muted)
27
+ process.stderr.write(chunk);
28
+ cb();
29
+ },
30
+ });
31
+ const rl = createInterface({ input: process.stdin, output, terminal: process.stdin.isTTY === true });
32
+ try {
33
+ const pending = rl.question(question);
34
+ // Mute only after the prompt itself is written, so the question shows but keystrokes do not.
35
+ muted = hidden;
36
+ return await pending;
37
+ }
38
+ finally {
39
+ rl.close();
40
+ if (hidden)
41
+ process.stderr.write("\n");
42
+ }
43
+ }
44
+ async function readStdin() {
45
+ const chunks = [];
46
+ for await (const c of process.stdin)
47
+ chunks.push(typeof c === "string" ? Buffer.from(c) : c);
48
+ return Buffer.concat(chunks).toString("utf8");
49
+ }
50
+ /** One shared line reader for the console's prompt loop (history works, Ctrl-D ends it). */
51
+ let lineReader = null;
52
+ let linesClosed = false;
53
+ function readLine(question) {
54
+ if (linesClosed)
55
+ return Promise.resolve(null);
56
+ if (!lineReader) {
57
+ lineReader = createInterface({ input: process.stdin, output: process.stderr, terminal: process.stdin.isTTY === true });
58
+ lineReader.on("close", () => { linesClosed = true; });
59
+ }
60
+ const rl = lineReader;
61
+ return new Promise((resolve) => {
62
+ const onClose = () => resolve(null);
63
+ rl.once("close", onClose);
64
+ rl.question(question).then((a) => { rl.off("close", onClose); resolve(a); }, () => resolve(null));
65
+ });
66
+ }
67
+ const io = {
68
+ out: (line) => process.stdout.write(line + "\n"),
69
+ err: (line) => process.stderr.write(line + "\n"),
70
+ isTTY: process.stdin.isTTY === true && process.stderr.isTTY === true,
71
+ prompt: (q) => ask(q, false),
72
+ promptSecret: (q) => ask(q, true),
73
+ readStdin,
74
+ readLine,
75
+ closeLines: () => { lineReader?.close(); },
76
+ };
77
+ function openUrl(url) {
78
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", '""', url.replace(/&/g, "^&")]] : ["xdg-open", [url]];
79
+ return new Promise((resolve) => {
80
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true, windowsVerbatimArguments: process.platform === "win32" });
81
+ child.on("error", () => {
82
+ process.stderr.write(`Could not open a browser; open the link above yourself.\n`);
83
+ resolve();
84
+ });
85
+ child.unref();
86
+ resolve();
87
+ });
88
+ }
89
+ const CALLBACK_PAGE = `<!doctype html><meta charset="utf-8"><title>cerkel</title>
90
+ <body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1rem;line-height:1.5">
91
+ <h1 style="font-size:1.25rem">You can close this tab</h1><p>Go back to your terminal — cerkel has what it needs.</p></body>`;
92
+ /**
93
+ * The browser half of `cerkel login` lands here: 127.0.0.1 only (never 0.0.0.0, so nothing on the
94
+ * network can reach it), a random free port, one callback, then it closes.
95
+ */
96
+ function listenLoopback() {
97
+ return new Promise((resolve, reject) => {
98
+ let deliver = null;
99
+ let got = null;
100
+ const server = createServer((req, res) => {
101
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
102
+ if (req.method !== "GET" || url.pathname !== "/callback") {
103
+ res.writeHead(404).end();
104
+ return;
105
+ }
106
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(CALLBACK_PAGE);
107
+ if (!got) {
108
+ got = url.searchParams;
109
+ deliver?.(got);
110
+ }
111
+ });
112
+ server.on("error", reject);
113
+ server.listen(0, "127.0.0.1", () => {
114
+ const addr = server.address();
115
+ const port = typeof addr === "object" && addr ? addr.port : 0;
116
+ resolve({
117
+ port,
118
+ waitForCallback: (timeoutMs) => new Promise((res, rej) => {
119
+ if (got)
120
+ return res(got);
121
+ const t = setTimeout(() => rej(new Error("timeout")), timeoutMs);
122
+ deliver = (q) => { clearTimeout(t); res(q); };
123
+ }),
124
+ close: () => { server.close(); server.closeAllConnections?.(); },
125
+ });
126
+ });
127
+ });
128
+ }
129
+ const ctx = {
130
+ cwd: process.cwd(),
131
+ env: process.env,
132
+ fetch: (input, init) => fetch(input, init),
133
+ credentials: new SystemCredentialStore(new FileCredentialStore(credentialsFilePath(), legacyCredentialsFilePath())),
134
+ io,
135
+ now: () => new Date(),
136
+ timers: globalTimers,
137
+ watch: (dir, onChange) => {
138
+ const w = watch(dir, { recursive: true }, (_event, filename) => onChange(filename ? filename.toString() : null));
139
+ w.on("error", (e) => process.stderr.write(`Watch error: ${e.message}\n`));
140
+ return w;
141
+ },
142
+ openUrl,
143
+ listenLoopback,
144
+ hostname: hostname(),
145
+ onInterrupt: (handler) => {
146
+ process.on("SIGINT", handler);
147
+ return () => process.off("SIGINT", handler);
148
+ },
149
+ version: version(),
150
+ };
151
+ main(process.argv.slice(2), ctx).then((code) => {
152
+ process.exitCode = code;
153
+ }, (e) => {
154
+ // main() already redacts and reports every expected failure; this is the last-ditch net.
155
+ process.stderr.write(`Unexpected error: ${e instanceof Error ? e.message.replace(/rzn_[A-Za-z0-9_-]{16,}/g, "***") : "unknown"}\n`);
156
+ process.exitCode = 1;
157
+ });
package/dist/chunk.js ADDED
@@ -0,0 +1,31 @@
1
+ import { writeRank } from "./layout.js";
2
+ /**
3
+ * The server refuses bodies over 1,000,000 bytes. Aim well under it so the envelope, JSON
4
+ * escaping of Arabic text and a proxy's own headers never tip a request over.
5
+ */
6
+ export const MAX_CHUNK_BYTES = 800_000;
7
+ const ENVELOPE_BYTES = 512;
8
+ /**
9
+ * Split files into requests, keeping the server's write order across requests: every section
10
+ * goes out before any settings/CSS, and those before any template. A single file larger than
11
+ * the limit travels alone and the server's 413 explains itself.
12
+ */
13
+ export function chunkFiles(files, limit = MAX_CHUNK_BYTES) {
14
+ const ordered = [...files].sort((a, b) => writeRank(a.path) - writeRank(b.path) || a.path.localeCompare(b.path));
15
+ const chunks = [];
16
+ let current = [];
17
+ let size = ENVELOPE_BYTES;
18
+ for (const f of ordered) {
19
+ const bytes = Buffer.byteLength(JSON.stringify(f), "utf8") + 1;
20
+ if (current.length > 0 && size + bytes > limit) {
21
+ chunks.push(current);
22
+ current = [];
23
+ size = ENVELOPE_BYTES;
24
+ }
25
+ current.push(f);
26
+ size += bytes;
27
+ }
28
+ if (current.length > 0)
29
+ chunks.push(current);
30
+ return chunks;
31
+ }