@cookiemunch/cli 0.1.0 → 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/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-77A2NLOH.js";
4
+ } from "./chunk-R5WPMHRO.js";
5
5
 
6
6
  // src/bin.ts
7
7
  import { readFile, writeFile } from "fs/promises";
@@ -6,7 +6,8 @@ function mergeLikeServer(base, patch) {
6
6
  const out = { ...base };
7
7
  for (const [k, v] of Object.entries(patch)) {
8
8
  const b = base[k];
9
- out[k] = isPlainObject(v) && isPlainObject(b) ? mergeLikeServer(b, v) : v;
9
+ if (v === null) delete out[k];
10
+ else out[k] = isPlainObject(v) ? mergeLikeServer(isPlainObject(b) ? b : {}, v) : v;
10
11
  }
11
12
  return out;
12
13
  }
@@ -59,14 +60,16 @@ Usage:
59
60
  Show what pushing <file> would change. Nothing is sent.
60
61
  cookiemunch config push --site <cbid> <file> [--replace]
61
62
  Apply <file>. Default: merged over the stored config (PATCH) \u2014 fields the file
62
- leaves out keep their value. --replace sends it whole (PUT): a top-level field
63
- the file leaves out reverts to its default.
63
+ leaves out keep their value, and a field set to null is removed (it takes its
64
+ default). --replace sends it whole (PUT): a top-level field the file leaves out
65
+ reverts to its default.
64
66
  cookiemunch preview --site <cbid> [--url <page>] [--region <eu|de|us-ca\u2026>] [--lang <de\u2026>]
65
67
  [--ttl <seconds>] [--open]
66
68
  Print (and with --open, open) a link showing the banner as a visitor in that
67
69
  region and language sees it. Works in headless browsers; nothing is recorded.
68
- cookiemunch executors test <id> --email <email> [--operation <lookup|erase|export\u2026>]
70
+ cookiemunch executors test <id> --email <email> [--operation <locate|export|erase>]
69
71
  Try a connected system against one person without filing a request.
72
+ --operation defaults to locate (lookup is accepted as another name for it).
70
73
 
71
74
  Environment:
72
75
  COOKIEMUNCH_API_KEY an API key (fck_\u2026). Required.
@@ -227,7 +230,8 @@ async function executorsCommand(sub, p, io, base, key) {
227
230
  if (!id) throw new UsageError("executors test <id> needs the connection id");
228
231
  const email = str(p.flags, "email");
229
232
  if (!email) throw new UsageError("--email is required");
230
- const operation = str(p.flags, "operation") ?? "lookup";
233
+ const asked = str(p.flags, "operation") ?? "locate";
234
+ const operation = asked === "lookup" ? "locate" : asked;
231
235
  try {
232
236
  const r = await rawRequest(io, base, key, "POST", `/dsar/executors/${encodeURIComponent(id)}/test`, { email, operation });
233
237
  io.stdout(`${JSON.stringify(r, null, 2)}
@@ -295,4 +299,4 @@ export {
295
299
  parseArgs,
296
300
  run
297
301
  };
298
- //# sourceMappingURL=chunk-77A2NLOH.js.map
302
+ //# sourceMappingURL=chunk-R5WPMHRO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/diff.ts","../src/cli.ts"],"sourcesContent":["/**\n * What a config push would change, computed on this side of the wire.\n *\n * `mergeLikeServer` mirrors the server's `PATCH /v1/sites/{cbid}/config` semantics exactly\n * (JSON Merge Patch, RFC 7386 — packages/server/src/site-config-write.ts mergePatch): the\n * patch wins, nested plain objects merge, arrays and primitives replace wholesale, and a\n * `null` removes the key. A diff computed against any other merge would describe a change\n * the server is not going to make.\n */\n\nexport type Json = null | boolean | number | string | Json[] | { [k: string]: Json };\n\nexport interface Change {\n /** Dotted path; array indices in brackets: `banner.content.title`, `geoRules[0].mode`. */\n path: string;\n kind: 'added' | 'removed' | 'changed';\n from?: Json;\n to?: Json;\n}\n\nexport function isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nexport function mergeLikeServer(base: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = { ...base };\n for (const [k, v] of Object.entries(patch)) {\n const b = base[k];\n if (v === null) delete out[k];\n else out[k] = isPlainObject(v) ? mergeLikeServer(isPlainObject(b) ? b : {}, v) : v;\n }\n return out;\n}\n\nfunction join(path: string, key: string): string {\n return path ? `${path}.${key}` : key;\n}\n\n/** Every leaf that differs between `before` and `after`, in a stable order. */\nexport function diffJson(before: unknown, after: unknown, path = ''): Change[] {\n if (isPlainObject(before) && isPlainObject(after)) {\n const keys = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();\n return keys.flatMap((k) => {\n const p = join(path, k);\n if (!(k in after)) return [{ path: p, kind: 'removed' as const, from: before[k] as Json }];\n if (!(k in before)) return [{ path: p, kind: 'added' as const, to: after[k] as Json }];\n return diffJson(before[k], after[k], p);\n });\n }\n if (Array.isArray(before) && Array.isArray(after)) {\n const out: Change[] = [];\n const n = Math.max(before.length, after.length);\n for (let i = 0; i < n; i++) {\n const p = `${path}[${i}]`;\n if (i >= after.length) out.push({ path: p, kind: 'removed', from: before[i] as Json });\n else if (i >= before.length) out.push({ path: p, kind: 'added', to: after[i] as Json });\n else out.push(...diffJson(before[i], after[i], p));\n }\n return out;\n }\n if (JSON.stringify(before) === JSON.stringify(after)) return [];\n return [{ path: path || '(root)', kind: 'changed', from: before as Json, to: after as Json }];\n}\n\nfunction show(v: Json | undefined): string {\n const s = JSON.stringify(v);\n return s === undefined ? 'undefined' : s.length > 120 ? `${s.slice(0, 117)}...` : s;\n}\n\n/** A unified-diff-ish rendering: one line per change. */\nexport function formatChanges(changes: Change[]): string {\n return changes\n .map((c) =>\n c.kind === 'added'\n ? `+ ${c.path}: ${show(c.to)}`\n : c.kind === 'removed'\n ? `- ${c.path}: ${show(c.from)}`\n : `~ ${c.path}: ${show(c.from)} -> ${show(c.to)}`,\n )\n .join('\\n');\n}\n","/**\n * `cookiemunch` — the Developer API from a terminal or a CI job.\n *\n * cookiemunch config pull --site <cbid> [--out file]\n * cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]\n * cookiemunch config push --site <cbid> <file> [--replace]\n * cookiemunch preview --site <cbid> [--url <url>] [--region eu] [--lang de] [--ttl 900] [--open]\n * cookiemunch executors test <id> --email <email> [--operation locate]\n *\n * Everything goes through the typed SDK (@cookiemunch/sdk) except where the SDK has no\n * method yet; those calls go through `rawRequest` with the same key and base URL. All I/O\n * is injected, so the whole CLI is testable with a mocked fetch.\n */\n\nimport { createCookieMunch, CookieMunchApiError, type CookieMunchClient } from '@cookiemunch/sdk';\nimport { diffJson, formatChanges, isPlainObject, mergeLikeServer } from './diff.js';\n\nexport const DEFAULT_BASE_URL = 'https://api.cookiemunch.net';\n\nexport interface CliIo {\n env: Record<string, string | undefined>;\n fetch: typeof fetch;\n stdout: (text: string) => void;\n stderr: (text: string) => void;\n readFile: (path: string) => Promise<string>;\n writeFile: (path: string, data: string) => Promise<void>;\n /** Open a URL in the user's browser. */\n openUrl: (url: string) => Promise<void>;\n}\n\nexport const USAGE = `cookiemunch — Cookie Munch from the command line\n\nUsage:\n cookiemunch config pull --site <cbid> [--out <file>]\n Write the site's config as JSON (to stdout, or to <file>).\n cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]\n Show what pushing <file> would change. Nothing is sent.\n cookiemunch config push --site <cbid> <file> [--replace]\n Apply <file>. Default: merged over the stored config (PATCH) — fields the file\n leaves out keep their value, and a field set to null is removed (it takes its\n default). --replace sends it whole (PUT): a top-level field the file leaves out\n reverts to its default.\n cookiemunch preview --site <cbid> [--url <page>] [--region <eu|de|us-ca…>] [--lang <de…>]\n [--ttl <seconds>] [--open]\n Print (and with --open, open) a link showing the banner as a visitor in that\n region and language sees it. Works in headless browsers; nothing is recorded.\n cookiemunch executors test <id> --email <email> [--operation <locate|export|erase>]\n Try a connected system against one person without filing a request.\n --operation defaults to locate (lookup is accepted as another name for it).\n\nEnvironment:\n COOKIEMUNCH_API_KEY an API key (fck_…). Required.\n COOKIEMUNCH_BASE_URL the API origin. Default ${DEFAULT_BASE_URL}.\n COOKIEMUNCH_SITE default for --site.\n`;\n\n/** A usage mistake: exit code 2. */\nclass UsageError extends Error {}\n\ninterface Parsed {\n positional: string[];\n flags: Record<string, string | true>;\n}\n\nconst BOOLEAN_FLAGS = new Set(['replace', 'exit-code', 'open', 'help', 'json']);\n\nexport function parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n const flags: Record<string, string | true> = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a === '-h') {\n flags.help = true;\n } else if (a.startsWith('--')) {\n const eq = a.indexOf('=');\n const name = eq > 0 ? a.slice(2, eq) : a.slice(2);\n if (eq > 0) flags[name] = a.slice(eq + 1);\n else if (BOOLEAN_FLAGS.has(name)) flags[name] = true;\n else {\n const v = argv[i + 1];\n if (v === undefined || v.startsWith('--')) throw new UsageError(`--${name} needs a value`);\n flags[name] = v;\n i++;\n }\n } else {\n positional.push(a);\n }\n }\n return { positional, flags };\n}\n\nfunction str(flags: Parsed['flags'], name: string): string | undefined {\n const v = flags[name];\n return typeof v === 'string' ? v : undefined;\n}\n\nfunction requireSite(p: Parsed, io: CliIo): string {\n const site = str(p.flags, 'site') ?? io.env.COOKIEMUNCH_SITE;\n if (!site) throw new UsageError('--site <cbid> is required (or set COOKIEMUNCH_SITE)');\n return site;\n}\n\nasync function readConfigFile(io: CliIo, path: string | undefined): Promise<Record<string, unknown>> {\n if (!path) throw new UsageError('a config file is required');\n let text: string;\n try {\n text = await io.readFile(path);\n } catch (e) {\n throw new UsageError(`cannot read ${path}: ${(e as Error).message}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (e) {\n throw new UsageError(`${path} is not valid JSON: ${(e as Error).message}`);\n }\n if (!isPlainObject(parsed)) throw new UsageError(`${path} must hold a JSON object`);\n return parsed;\n}\n\n/** What the stored config would become, per push mode. */\nfunction proposed(current: Record<string, unknown>, file: Record<string, unknown>, replace: boolean, cbid: string) {\n return replace ? { ...file, cbid } : mergeLikeServer(current, file);\n}\n\nasync function rawRequest(io: CliIo, base: string, key: string, method: string, path: string, body?: unknown): Promise<unknown> {\n const res = await io.fetch(`${base.replace(/\\/+$/, '')}/v1${path}`, {\n method,\n headers: { Authorization: `Bearer ${key}`, ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}) },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n const text = await res.text();\n let data: unknown = text;\n try {\n data = text ? JSON.parse(text) : undefined;\n } catch {\n /* keep the text */\n }\n if (!res.ok) {\n const err = isPlainObject(data) ? data : {};\n throw new CookieMunchApiError(\n res.status,\n typeof err.error === 'string' ? err.error : `request failed with status ${res.status}`,\n typeof err.code === 'string' ? err.code : undefined,\n );\n }\n return data;\n}\n\nasync function configCommand(sub: string | undefined, p: Parsed, io: CliIo, client: CookieMunchClient): Promise<number> {\n const cbid = requireSite(p, io);\n if (sub === 'pull') {\n const cfg = await client.sites.getConfig(cbid);\n const text = `${JSON.stringify(cfg, null, 2)}\\n`;\n const out = str(p.flags, 'out');\n if (out) {\n await io.writeFile(out, text);\n io.stderr(`Wrote ${cbid}'s config to ${out}\\n`);\n } else {\n io.stdout(text);\n }\n return 0;\n }\n if (sub === 'diff' || sub === 'push') {\n const file = await readConfigFile(io, p.positional[2]);\n const replace = p.flags.replace === true;\n const current = (await client.sites.getConfig(cbid)) as unknown as Record<string, unknown>;\n // Computed here: the server has no dry-run for config writes yet, and the only safe\n // way to ask \"what would this change\" of an API that might ignore a dryRun flag is\n // not to send the write at all.\n const changes = diffJson(current, proposed(current, file, replace, cbid));\n if (changes.length === 0) {\n io.stderr(`No changes: ${cbid}'s config already matches.\\n`);\n return 0;\n }\n io.stdout(`${formatChanges(changes)}\\n`);\n if (sub === 'diff') {\n io.stderr(`${changes.length} change${changes.length === 1 ? '' : 's'} (nothing sent). Apply with: cookiemunch config push --site ${cbid} ${p.positional[2]}${replace ? ' --replace' : ''}\\n`);\n return p.flags['exit-code'] === true ? 1 : 0;\n }\n if (replace) await client.sites.putConfig(cbid, { ...file, cbid } as never);\n else await client.sites.patchConfig(cbid, file as never);\n io.stderr(`Applied ${changes.length} change${changes.length === 1 ? '' : 's'} to ${cbid}.\\n`);\n return 0;\n }\n throw new UsageError(`unknown config command: ${sub ?? '(none)'} — use pull, diff or push`);\n}\n\nasync function previewCommand(p: Parsed, io: CliIo, client: CookieMunchClient): Promise<number> {\n const cbid = requireSite(p, io);\n const ttl = str(p.flags, 'ttl');\n const ttlSeconds = ttl === undefined ? undefined : Number(ttl);\n if (ttlSeconds !== undefined && (!Number.isInteger(ttlSeconds) || ttlSeconds < 1 || ttlSeconds > 3600)) {\n throw new UsageError('--ttl must be a whole number of seconds from 1 to 3600');\n }\n const r = await client.sites.previewToken(cbid, {\n ...(str(p.flags, 'region') ? { region: str(p.flags, 'region')! } : {}),\n ...(str(p.flags, 'lang') ? { lang: str(p.flags, 'lang')! } : {}),\n ...(str(p.flags, 'url') ? { url: str(p.flags, 'url')! } : {}),\n ...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n });\n if (p.flags.json === true) {\n io.stdout(`${JSON.stringify(r, null, 2)}\\n`);\n } else if (r.previewUrl) {\n io.stdout(`${r.previewUrl}\\n`);\n } else {\n // No page to put it on (the site has no domain and no --url): hand over the token.\n io.stdout(`${r.token}\\n`);\n io.stderr('Add ?cm_preview=<token>&cm_region=..&cm_lang=.. to any page of the site.\\n');\n }\n io.stderr(`Valid until ${new Date(r.expiresAt).toISOString()}.\\n`);\n if (p.flags.open === true && r.previewUrl) await io.openUrl(r.previewUrl);\n return 0;\n}\n\nasync function executorsCommand(sub: string | undefined, p: Parsed, io: CliIo, base: string, key: string): Promise<number> {\n if (sub !== 'test') throw new UsageError(`unknown executors command: ${sub ?? '(none)'} — use test`);\n const id = p.positional[2];\n if (!id) throw new UsageError('executors test <id> needs the connection id');\n const email = str(p.flags, 'email');\n if (!email) throw new UsageError('--email is required');\n // The API's operations are locate, export and erase. `lookup` was this CLI's documented\n // default and the API refused it (400), so it stays accepted as another name for locate.\n const asked = str(p.flags, 'operation') ?? 'locate';\n const operation = asked === 'lookup' ? 'locate' : asked;\n // The typed SDK has no method for this yet, so it is a direct call with the same key.\n try {\n const r = await rawRequest(io, base, key, 'POST', `/dsar/executors/${encodeURIComponent(id)}/test`, { email, operation });\n io.stdout(`${JSON.stringify(r, null, 2)}\\n`);\n return 0;\n } catch (e) {\n // The API's generic route miss is a bare `not found`; an unknown connection says so.\n if (e instanceof CookieMunchApiError && e.status === 404 && e.message === 'not found') {\n io.stderr('This server does not offer connection tests yet (POST /v1/dsar/executors/{id}/test).\\n');\n return 1;\n }\n throw e;\n }\n}\n\n/** Run the CLI. Returns the process exit code: 0 ok, 1 failed, 2 usage. */\nexport async function run(argv: string[], io: CliIo): Promise<number> {\n let p: Parsed;\n try {\n p = parseArgs(argv);\n } catch (e) {\n io.stderr(`${(e as Error).message}\\n\\n${USAGE}`);\n return 2;\n }\n const [cmd, sub] = p.positional;\n if (!cmd || p.flags.help === true || cmd === 'help') {\n io.stdout(USAGE);\n return cmd || p.flags.help === true ? 0 : 2;\n }\n const key = io.env.COOKIEMUNCH_API_KEY;\n if (!key) {\n io.stderr('COOKIEMUNCH_API_KEY is not set. Create a key under Developers in the dashboard.\\n');\n return 2;\n }\n const base = io.env.COOKIEMUNCH_BASE_URL || DEFAULT_BASE_URL;\n const client = createCookieMunch({ apiKey: key, baseUrl: base, fetch: io.fetch });\n try {\n if (cmd === 'config') return await configCommand(sub, p, io, client);\n if (cmd === 'preview') return await previewCommand(p, io, client);\n if (cmd === 'executors') return await executorsCommand(sub, p, io, base, key);\n throw new UsageError(`unknown command: ${cmd}`);\n } catch (e) {\n if (e instanceof UsageError) {\n io.stderr(`${e.message}\\n\\n${USAGE}`);\n return 2;\n }\n if (e instanceof CookieMunchApiError) {\n io.stderr(`API error ${e.status}${e.code ? ` (${e.code})` : ''}: ${e.message}\\n`);\n return 1;\n }\n io.stderr(`${(e as Error).message ?? String(e)}\\n`);\n return 1;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]
6
6
  * cookiemunch config push --site <cbid> <file> [--replace]
7
7
  * cookiemunch preview --site <cbid> [--url <url>] [--region eu] [--lang de] [--ttl 900] [--open]
8
- * cookiemunch executors test <id> --email <email> [--operation lookup]
8
+ * cookiemunch executors test <id> --email <email> [--operation locate]
9
9
  *
10
10
  * Everything goes through the typed SDK (@cookiemunch/sdk) except where the SDK has no
11
11
  * method yet; those calls go through `rawRequest` with the same key and base URL. All I/O
@@ -22,7 +22,7 @@ interface CliIo {
22
22
  /** Open a URL in the user's browser. */
23
23
  openUrl: (url: string) => Promise<void>;
24
24
  }
25
- declare const USAGE = "cookiemunch \u2014 Cookie Munch from the command line\n\nUsage:\n cookiemunch config pull --site <cbid> [--out <file>]\n Write the site's config as JSON (to stdout, or to <file>).\n cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]\n Show what pushing <file> would change. Nothing is sent.\n cookiemunch config push --site <cbid> <file> [--replace]\n Apply <file>. Default: merged over the stored config (PATCH) \u2014 fields the file\n leaves out keep their value. --replace sends it whole (PUT): a top-level field\n the file leaves out reverts to its default.\n cookiemunch preview --site <cbid> [--url <page>] [--region <eu|de|us-ca\u2026>] [--lang <de\u2026>]\n [--ttl <seconds>] [--open]\n Print (and with --open, open) a link showing the banner as a visitor in that\n region and language sees it. Works in headless browsers; nothing is recorded.\n cookiemunch executors test <id> --email <email> [--operation <lookup|erase|export\u2026>]\n Try a connected system against one person without filing a request.\n\nEnvironment:\n COOKIEMUNCH_API_KEY an API key (fck_\u2026). Required.\n COOKIEMUNCH_BASE_URL the API origin. Default https://api.cookiemunch.net.\n COOKIEMUNCH_SITE default for --site.\n";
25
+ declare const USAGE = "cookiemunch \u2014 Cookie Munch from the command line\n\nUsage:\n cookiemunch config pull --site <cbid> [--out <file>]\n Write the site's config as JSON (to stdout, or to <file>).\n cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]\n Show what pushing <file> would change. Nothing is sent.\n cookiemunch config push --site <cbid> <file> [--replace]\n Apply <file>. Default: merged over the stored config (PATCH) \u2014 fields the file\n leaves out keep their value, and a field set to null is removed (it takes its\n default). --replace sends it whole (PUT): a top-level field the file leaves out\n reverts to its default.\n cookiemunch preview --site <cbid> [--url <page>] [--region <eu|de|us-ca\u2026>] [--lang <de\u2026>]\n [--ttl <seconds>] [--open]\n Print (and with --open, open) a link showing the banner as a visitor in that\n region and language sees it. Works in headless browsers; nothing is recorded.\n cookiemunch executors test <id> --email <email> [--operation <locate|export|erase>]\n Try a connected system against one person without filing a request.\n --operation defaults to locate (lookup is accepted as another name for it).\n\nEnvironment:\n COOKIEMUNCH_API_KEY an API key (fck_\u2026). Required.\n COOKIEMUNCH_BASE_URL the API origin. Default https://api.cookiemunch.net.\n COOKIEMUNCH_SITE default for --site.\n";
26
26
  interface Parsed {
27
27
  positional: string[];
28
28
  flags: Record<string, string | true>;
@@ -35,9 +35,10 @@ declare function run(argv: string[], io: CliIo): Promise<number>;
35
35
  * What a config push would change, computed on this side of the wire.
36
36
  *
37
37
  * `mergeLikeServer` mirrors the server's `PATCH /v1/sites/{cbid}/config` semantics exactly
38
- * (packages/server/src/dev-api-sites.ts deepMerge): the patch wins, nested plain objects
39
- * merge, arrays and primitives replace wholesale. A diff computed against any other merge
40
- * would describe a change the server is not going to make.
38
+ * (JSON Merge Patch, RFC 7386 — packages/server/src/site-config-write.ts mergePatch): the
39
+ * patch wins, nested plain objects merge, arrays and primitives replace wholesale, and a
40
+ * `null` removes the key. A diff computed against any other merge would describe a change
41
+ * the server is not going to make.
41
42
  */
42
43
  type Json = null | boolean | number | string | Json[] | {
43
44
  [k: string]: Json;
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  mergeLikeServer,
7
7
  parseArgs,
8
8
  run
9
- } from "./chunk-77A2NLOH.js";
9
+ } from "./chunk-R5WPMHRO.js";
10
10
  export {
11
11
  DEFAULT_BASE_URL,
12
12
  USAGE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cookiemunch/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "cookiemunch — config as code, banner previews and connection tests from a terminal or CI, over the Cookie Munch Developer API",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,14 +23,17 @@
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
26
- "url": "git+https://github.com/alphajew420/cookiemunch.git",
27
- "directory": "packages/cli"
26
+ "url": "git+https://github.com/Cookie-Munch/cli.git"
28
27
  },
29
28
  "publishConfig": {
30
29
  "access": "public"
31
30
  },
32
31
  "dependencies": {
33
- "@cookiemunch/sdk": "0.1.0"
32
+ "@cookiemunch/sdk": "0.1.1"
33
+ },
34
+ "homepage": "https://cookiemunch.net/docs",
35
+ "bugs": {
36
+ "url": "https://github.com/Cookie-Munch/cli/issues"
34
37
  },
35
38
  "scripts": {
36
39
  "build": "tsup",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/diff.ts","../src/cli.ts"],"sourcesContent":["/**\n * What a config push would change, computed on this side of the wire.\n *\n * `mergeLikeServer` mirrors the server's `PATCH /v1/sites/{cbid}/config` semantics exactly\n * (packages/server/src/dev-api-sites.ts deepMerge): the patch wins, nested plain objects\n * merge, arrays and primitives replace wholesale. A diff computed against any other merge\n * would describe a change the server is not going to make.\n */\n\nexport type Json = null | boolean | number | string | Json[] | { [k: string]: Json };\n\nexport interface Change {\n /** Dotted path; array indices in brackets: `banner.content.title`, `geoRules[0].mode`. */\n path: string;\n kind: 'added' | 'removed' | 'changed';\n from?: Json;\n to?: Json;\n}\n\nexport function isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nexport function mergeLikeServer(base: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = { ...base };\n for (const [k, v] of Object.entries(patch)) {\n const b = base[k];\n out[k] = isPlainObject(v) && isPlainObject(b) ? mergeLikeServer(b, v) : v;\n }\n return out;\n}\n\nfunction join(path: string, key: string): string {\n return path ? `${path}.${key}` : key;\n}\n\n/** Every leaf that differs between `before` and `after`, in a stable order. */\nexport function diffJson(before: unknown, after: unknown, path = ''): Change[] {\n if (isPlainObject(before) && isPlainObject(after)) {\n const keys = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();\n return keys.flatMap((k) => {\n const p = join(path, k);\n if (!(k in after)) return [{ path: p, kind: 'removed' as const, from: before[k] as Json }];\n if (!(k in before)) return [{ path: p, kind: 'added' as const, to: after[k] as Json }];\n return diffJson(before[k], after[k], p);\n });\n }\n if (Array.isArray(before) && Array.isArray(after)) {\n const out: Change[] = [];\n const n = Math.max(before.length, after.length);\n for (let i = 0; i < n; i++) {\n const p = `${path}[${i}]`;\n if (i >= after.length) out.push({ path: p, kind: 'removed', from: before[i] as Json });\n else if (i >= before.length) out.push({ path: p, kind: 'added', to: after[i] as Json });\n else out.push(...diffJson(before[i], after[i], p));\n }\n return out;\n }\n if (JSON.stringify(before) === JSON.stringify(after)) return [];\n return [{ path: path || '(root)', kind: 'changed', from: before as Json, to: after as Json }];\n}\n\nfunction show(v: Json | undefined): string {\n const s = JSON.stringify(v);\n return s === undefined ? 'undefined' : s.length > 120 ? `${s.slice(0, 117)}...` : s;\n}\n\n/** A unified-diff-ish rendering: one line per change. */\nexport function formatChanges(changes: Change[]): string {\n return changes\n .map((c) =>\n c.kind === 'added'\n ? `+ ${c.path}: ${show(c.to)}`\n : c.kind === 'removed'\n ? `- ${c.path}: ${show(c.from)}`\n : `~ ${c.path}: ${show(c.from)} -> ${show(c.to)}`,\n )\n .join('\\n');\n}\n","/**\n * `cookiemunch` — the Developer API from a terminal or a CI job.\n *\n * cookiemunch config pull --site <cbid> [--out file]\n * cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]\n * cookiemunch config push --site <cbid> <file> [--replace]\n * cookiemunch preview --site <cbid> [--url <url>] [--region eu] [--lang de] [--ttl 900] [--open]\n * cookiemunch executors test <id> --email <email> [--operation lookup]\n *\n * Everything goes through the typed SDK (@cookiemunch/sdk) except where the SDK has no\n * method yet; those calls go through `rawRequest` with the same key and base URL. All I/O\n * is injected, so the whole CLI is testable with a mocked fetch.\n */\n\nimport { createCookieMunch, CookieMunchApiError, type CookieMunchClient } from '@cookiemunch/sdk';\nimport { diffJson, formatChanges, isPlainObject, mergeLikeServer } from './diff.js';\n\nexport const DEFAULT_BASE_URL = 'https://api.cookiemunch.net';\n\nexport interface CliIo {\n env: Record<string, string | undefined>;\n fetch: typeof fetch;\n stdout: (text: string) => void;\n stderr: (text: string) => void;\n readFile: (path: string) => Promise<string>;\n writeFile: (path: string, data: string) => Promise<void>;\n /** Open a URL in the user's browser. */\n openUrl: (url: string) => Promise<void>;\n}\n\nexport const USAGE = `cookiemunch — Cookie Munch from the command line\n\nUsage:\n cookiemunch config pull --site <cbid> [--out <file>]\n Write the site's config as JSON (to stdout, or to <file>).\n cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]\n Show what pushing <file> would change. Nothing is sent.\n cookiemunch config push --site <cbid> <file> [--replace]\n Apply <file>. Default: merged over the stored config (PATCH) — fields the file\n leaves out keep their value. --replace sends it whole (PUT): a top-level field\n the file leaves out reverts to its default.\n cookiemunch preview --site <cbid> [--url <page>] [--region <eu|de|us-ca…>] [--lang <de…>]\n [--ttl <seconds>] [--open]\n Print (and with --open, open) a link showing the banner as a visitor in that\n region and language sees it. Works in headless browsers; nothing is recorded.\n cookiemunch executors test <id> --email <email> [--operation <lookup|erase|export…>]\n Try a connected system against one person without filing a request.\n\nEnvironment:\n COOKIEMUNCH_API_KEY an API key (fck_…). Required.\n COOKIEMUNCH_BASE_URL the API origin. Default ${DEFAULT_BASE_URL}.\n COOKIEMUNCH_SITE default for --site.\n`;\n\n/** A usage mistake: exit code 2. */\nclass UsageError extends Error {}\n\ninterface Parsed {\n positional: string[];\n flags: Record<string, string | true>;\n}\n\nconst BOOLEAN_FLAGS = new Set(['replace', 'exit-code', 'open', 'help', 'json']);\n\nexport function parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n const flags: Record<string, string | true> = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a === '-h') {\n flags.help = true;\n } else if (a.startsWith('--')) {\n const eq = a.indexOf('=');\n const name = eq > 0 ? a.slice(2, eq) : a.slice(2);\n if (eq > 0) flags[name] = a.slice(eq + 1);\n else if (BOOLEAN_FLAGS.has(name)) flags[name] = true;\n else {\n const v = argv[i + 1];\n if (v === undefined || v.startsWith('--')) throw new UsageError(`--${name} needs a value`);\n flags[name] = v;\n i++;\n }\n } else {\n positional.push(a);\n }\n }\n return { positional, flags };\n}\n\nfunction str(flags: Parsed['flags'], name: string): string | undefined {\n const v = flags[name];\n return typeof v === 'string' ? v : undefined;\n}\n\nfunction requireSite(p: Parsed, io: CliIo): string {\n const site = str(p.flags, 'site') ?? io.env.COOKIEMUNCH_SITE;\n if (!site) throw new UsageError('--site <cbid> is required (or set COOKIEMUNCH_SITE)');\n return site;\n}\n\nasync function readConfigFile(io: CliIo, path: string | undefined): Promise<Record<string, unknown>> {\n if (!path) throw new UsageError('a config file is required');\n let text: string;\n try {\n text = await io.readFile(path);\n } catch (e) {\n throw new UsageError(`cannot read ${path}: ${(e as Error).message}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (e) {\n throw new UsageError(`${path} is not valid JSON: ${(e as Error).message}`);\n }\n if (!isPlainObject(parsed)) throw new UsageError(`${path} must hold a JSON object`);\n return parsed;\n}\n\n/** What the stored config would become, per push mode. */\nfunction proposed(current: Record<string, unknown>, file: Record<string, unknown>, replace: boolean, cbid: string) {\n return replace ? { ...file, cbid } : mergeLikeServer(current, file);\n}\n\nasync function rawRequest(io: CliIo, base: string, key: string, method: string, path: string, body?: unknown): Promise<unknown> {\n const res = await io.fetch(`${base.replace(/\\/+$/, '')}/v1${path}`, {\n method,\n headers: { Authorization: `Bearer ${key}`, ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}) },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n const text = await res.text();\n let data: unknown = text;\n try {\n data = text ? JSON.parse(text) : undefined;\n } catch {\n /* keep the text */\n }\n if (!res.ok) {\n const err = isPlainObject(data) ? data : {};\n throw new CookieMunchApiError(\n res.status,\n typeof err.error === 'string' ? err.error : `request failed with status ${res.status}`,\n typeof err.code === 'string' ? err.code : undefined,\n );\n }\n return data;\n}\n\nasync function configCommand(sub: string | undefined, p: Parsed, io: CliIo, client: CookieMunchClient): Promise<number> {\n const cbid = requireSite(p, io);\n if (sub === 'pull') {\n const cfg = await client.sites.getConfig(cbid);\n const text = `${JSON.stringify(cfg, null, 2)}\\n`;\n const out = str(p.flags, 'out');\n if (out) {\n await io.writeFile(out, text);\n io.stderr(`Wrote ${cbid}'s config to ${out}\\n`);\n } else {\n io.stdout(text);\n }\n return 0;\n }\n if (sub === 'diff' || sub === 'push') {\n const file = await readConfigFile(io, p.positional[2]);\n const replace = p.flags.replace === true;\n const current = (await client.sites.getConfig(cbid)) as unknown as Record<string, unknown>;\n // Computed here: the server has no dry-run for config writes yet, and the only safe\n // way to ask \"what would this change\" of an API that might ignore a dryRun flag is\n // not to send the write at all.\n const changes = diffJson(current, proposed(current, file, replace, cbid));\n if (changes.length === 0) {\n io.stderr(`No changes: ${cbid}'s config already matches.\\n`);\n return 0;\n }\n io.stdout(`${formatChanges(changes)}\\n`);\n if (sub === 'diff') {\n io.stderr(`${changes.length} change${changes.length === 1 ? '' : 's'} (nothing sent). Apply with: cookiemunch config push --site ${cbid} ${p.positional[2]}${replace ? ' --replace' : ''}\\n`);\n return p.flags['exit-code'] === true ? 1 : 0;\n }\n if (replace) await client.sites.putConfig(cbid, { ...file, cbid } as never);\n else await client.sites.patchConfig(cbid, file as never);\n io.stderr(`Applied ${changes.length} change${changes.length === 1 ? '' : 's'} to ${cbid}.\\n`);\n return 0;\n }\n throw new UsageError(`unknown config command: ${sub ?? '(none)'} — use pull, diff or push`);\n}\n\nasync function previewCommand(p: Parsed, io: CliIo, client: CookieMunchClient): Promise<number> {\n const cbid = requireSite(p, io);\n const ttl = str(p.flags, 'ttl');\n const ttlSeconds = ttl === undefined ? undefined : Number(ttl);\n if (ttlSeconds !== undefined && (!Number.isInteger(ttlSeconds) || ttlSeconds < 1 || ttlSeconds > 3600)) {\n throw new UsageError('--ttl must be a whole number of seconds from 1 to 3600');\n }\n const r = await client.sites.previewToken(cbid, {\n ...(str(p.flags, 'region') ? { region: str(p.flags, 'region')! } : {}),\n ...(str(p.flags, 'lang') ? { lang: str(p.flags, 'lang')! } : {}),\n ...(str(p.flags, 'url') ? { url: str(p.flags, 'url')! } : {}),\n ...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n });\n if (p.flags.json === true) {\n io.stdout(`${JSON.stringify(r, null, 2)}\\n`);\n } else if (r.previewUrl) {\n io.stdout(`${r.previewUrl}\\n`);\n } else {\n // No page to put it on (the site has no domain and no --url): hand over the token.\n io.stdout(`${r.token}\\n`);\n io.stderr('Add ?cm_preview=<token>&cm_region=..&cm_lang=.. to any page of the site.\\n');\n }\n io.stderr(`Valid until ${new Date(r.expiresAt).toISOString()}.\\n`);\n if (p.flags.open === true && r.previewUrl) await io.openUrl(r.previewUrl);\n return 0;\n}\n\nasync function executorsCommand(sub: string | undefined, p: Parsed, io: CliIo, base: string, key: string): Promise<number> {\n if (sub !== 'test') throw new UsageError(`unknown executors command: ${sub ?? '(none)'} — use test`);\n const id = p.positional[2];\n if (!id) throw new UsageError('executors test <id> needs the connection id');\n const email = str(p.flags, 'email');\n if (!email) throw new UsageError('--email is required');\n const operation = str(p.flags, 'operation') ?? 'lookup';\n // The typed SDK has no method for this yet, so it is a direct call with the same key.\n try {\n const r = await rawRequest(io, base, key, 'POST', `/dsar/executors/${encodeURIComponent(id)}/test`, { email, operation });\n io.stdout(`${JSON.stringify(r, null, 2)}\\n`);\n return 0;\n } catch (e) {\n // The API's generic route miss is a bare `not found`; an unknown connection says so.\n if (e instanceof CookieMunchApiError && e.status === 404 && e.message === 'not found') {\n io.stderr('This server does not offer connection tests yet (POST /v1/dsar/executors/{id}/test).\\n');\n return 1;\n }\n throw e;\n }\n}\n\n/** Run the CLI. Returns the process exit code: 0 ok, 1 failed, 2 usage. */\nexport async function run(argv: string[], io: CliIo): Promise<number> {\n let p: Parsed;\n try {\n p = parseArgs(argv);\n } catch (e) {\n io.stderr(`${(e as Error).message}\\n\\n${USAGE}`);\n return 2;\n }\n const [cmd, sub] = p.positional;\n if (!cmd || p.flags.help === true || cmd === 'help') {\n io.stdout(USAGE);\n return cmd || p.flags.help === true ? 0 : 2;\n }\n const key = io.env.COOKIEMUNCH_API_KEY;\n if (!key) {\n io.stderr('COOKIEMUNCH_API_KEY is not set. Create a key under Developers in the dashboard.\\n');\n return 2;\n }\n const base = io.env.COOKIEMUNCH_BASE_URL || DEFAULT_BASE_URL;\n const client = createCookieMunch({ apiKey: key, baseUrl: base, fetch: io.fetch });\n try {\n if (cmd === 'config') return await configCommand(sub, p, io, client);\n if (cmd === 'preview') return await previewCommand(p, io, client);\n if (cmd === 'executors') return await executorsCommand(sub, p, io, base, key);\n throw new UsageError(`unknown command: ${cmd}`);\n } catch (e) {\n if (e instanceof UsageError) {\n io.stderr(`${e.message}\\n\\n${USAGE}`);\n return 2;\n }\n if (e instanceof CookieMunchApiError) {\n io.stderr(`API error ${e.status}${e.code ? ` (${e.code})` : ''}: ${e.message}\\n`);\n return 1;\n }\n io.stderr(`${(e as Error).message ?? String(e)}\\n`);\n return 1;\n }\n}\n"],"mappings":"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","names":[]}