run402 1.43.0 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/sites.mjs CHANGED
@@ -1,15 +1,16 @@
1
- import { readFileSync } from "fs";
2
- import { dirname, resolve } from "path";
1
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
2
+ import { tmpdir } from "os";
3
+ import { dirname, join, resolve } from "path";
3
4
  import { allowanceAuthHeaders, resolveProjectId, updateProject } from "./config.mjs";
4
5
  import { resolveFilePathsInManifest } from "./manifest.mjs";
5
6
  import { getSdk } from "./sdk.mjs";
6
7
  import { reportSdkError } from "./sdk-errors.mjs";
7
8
 
8
- const HELP = `run402 sites Deploy and manage static sites
9
+ const HELP = `run402 sites - Deploy and manage static sites
9
10
 
10
11
  Usage:
11
12
  run402 sites deploy --manifest <file> [--project <id>] [--target <target>]
12
- run402 sites deploy-dir <path> --project <id> [--target <target>] [--inherit]
13
+ run402 sites deploy-dir <path> --project <id> [--target <target>]
13
14
  run402 sites status <deployment_id>
14
15
  cat manifest.json | run402 sites deploy
15
16
 
@@ -22,14 +23,13 @@ Options (deploy):
22
23
  --manifest <file> Path to manifest JSON file (or read from stdin)
23
24
  --project <id> Project ID (defaults to active project)
24
25
  --target <target> Deployment target (e.g. 'production')
25
- --inherit Copy unchanged files from the previous deployment (only upload changed files)
26
26
  --help, -h Show this help message
27
27
 
28
28
  Options (deploy-dir):
29
29
  <path> Positional: local directory to deploy
30
30
  --project <id> Project ID (defaults to active project)
31
31
  --target <target> Deployment target (e.g. 'production')
32
- --inherit Copy unchanged files from the previous deployment server-side
32
+ --quiet Suppress progress events on stderr
33
33
 
34
34
  Manifest format (JSON):
35
35
  {
@@ -40,36 +40,40 @@ Manifest format (JSON):
40
40
  }
41
41
 
42
42
  Files can use either inline "data" or a local "path":
43
- { "file": "index.html", "data": "<html>...</html>" } inline content
44
- { "file": "style.css", "path": "./dist/style.css" } read from disk
43
+ { "file": "index.html", "data": "<html>...</html>" } <- inline content
44
+ { "file": "style.css", "path": "./dist/style.css" } <- read from disk
45
45
  Paths are resolved relative to the manifest file's directory.
46
46
  Binary files (images, fonts, etc.) are auto-detected and base64-encoded.
47
47
 
48
48
  Examples:
49
49
  run402 sites deploy --manifest site.json
50
- run402 sites deploy-dir ./my-site --project prj_abc --inherit
50
+ run402 sites deploy-dir ./my-site --project prj_abc
51
51
  run402 sites status dpl_abc123
52
52
  cat site.json | run402 sites deploy
53
53
 
54
54
  Notes:
55
+ - Both deploy and deploy-dir use the v1.32 plan/commit transport: only
56
+ bytes the gateway doesn't already have are uploaded. Re-deploys of an
57
+ unchanged tree make no S3 PUTs.
55
58
  - deploy-dir walks the directory, skips .git / node_modules / .DS_Store,
56
59
  and auto-detects binary files. Symlinks are rejected.
57
- - Free with active tier requires allowance auth
60
+ - Progress events are emitted as JSON-line objects on stderr by default
61
+ (one object per line: {"phase":"plan",...}/{"phase":"upload",...}/...).
62
+ Final result envelope goes to stdout. Pass --quiet to silence stderr.
63
+ - Free with active tier - requires allowance auth
58
64
  `;
59
65
 
60
66
  const SUB_HELP = {
61
- deploy: `run402 sites deploy Deploy a static site from a manifest
67
+ deploy: `run402 sites deploy - Deploy a static site from a manifest
62
68
 
63
69
  Usage:
64
- run402 sites deploy --manifest <file> [--project <id>] [--target <target>] [--inherit]
70
+ run402 sites deploy --manifest <file> [--project <id>] [--target <target>]
65
71
  cat manifest.json | run402 sites deploy [--project <id>] [--target <target>]
66
72
 
67
73
  Options:
68
74
  --manifest <file> Path to manifest JSON file (or read from stdin)
69
75
  --project <id> Project ID (defaults to the active project)
70
76
  --target <target> Deployment target (e.g. 'production')
71
- --inherit Copy unchanged files from the previous deployment
72
- (only upload changed files)
73
77
 
74
78
  Manifest format (JSON):
75
79
  {
@@ -83,17 +87,17 @@ Manifest format (JSON):
83
87
 
84
88
  Notes:
85
89
  - Must include at least index.html in the files array
86
- - Free with active tier requires allowance auth
90
+ - Free with active tier - requires allowance auth
87
91
 
88
92
  Examples:
89
93
  run402 sites deploy --manifest site.json
90
- run402 sites deploy --manifest site.json --target production --inherit
94
+ run402 sites deploy --manifest site.json --target production
91
95
  cat site.json | run402 sites deploy
92
96
  `,
93
- "deploy-dir": `run402 sites deploy-dir Deploy a static site from a local directory
97
+ "deploy-dir": `run402 sites deploy-dir - Deploy a static site from a local directory
94
98
 
95
99
  Usage:
96
- run402 sites deploy-dir <path> [--project <id>] [--target <target>] [--inherit]
100
+ run402 sites deploy-dir <path> [--project <id>] [--target <target>] [--quiet]
97
101
 
98
102
  Arguments:
99
103
  <path> Local directory to deploy (positional, required)
@@ -101,23 +105,37 @@ Arguments:
101
105
  Options:
102
106
  --project <id> Project ID (defaults to the active project)
103
107
  --target <target> Deployment target (e.g. 'production')
104
- --inherit Copy unchanged files from the previous deployment
108
+ --quiet Suppress progress events on stderr (events are on by
109
+ default — see Progress events below)
105
110
 
106
111
  Behavior:
107
112
  - Walks <path> recursively, skips .git / node_modules / .DS_Store
108
- - UTF-8 files are inlined as text; binary files are base64-encoded
113
+ - Computes per-file SHA-256 and uploads only bytes the gateway doesn't
114
+ already have (plan/commit transport, v1.32+)
109
115
  - Symlinks are rejected (no following)
110
116
  - Paths in the manifest are POSIX-style relative to <path>
111
117
 
118
+ Progress events:
119
+ By default, the CLI streams JSON-line events to stderr while the deploy
120
+ progresses. Each line is one JSON object terminated by \\n. Phases:
121
+ {"phase":"plan","manifest_size":N} - after POST /deploy/v1/plan
122
+ {"phase":"upload","file":"...","sha256":"...","done":k,"total":N}
123
+ - per uploaded file (k of N)
124
+ {"phase":"commit"} - before POST /deploy/v1/commit
125
+ {"phase":"poll","status":"copying","elapsed_ms":N}
126
+ - per Stage-2 copy poll tick
127
+ Stdout receives only the final result envelope. To consume both streams
128
+ separately: \`run402 sites deploy-dir ./dist --project p > result.json 2> events.log\`.
129
+
112
130
  Notes:
113
- - Practical size limit today is ~100 MB (inline JSON payload).
114
- For larger sites, use a pre-built manifest with the bundle_deploy API
115
- or wait for blob-backed deploys.
116
- - Free with active tier — requires allowance auth
131
+ - Re-deploying an unchanged tree makes no S3 PUTs (returns immediately
132
+ with bytes_uploaded: 0)
133
+ - Free with active tier - requires allowance auth
117
134
 
118
135
  Examples:
119
136
  run402 sites deploy-dir ./dist --project prj_abc
120
- run402 sites deploy-dir ./my-site --project prj_abc --target production --inherit
137
+ run402 sites deploy-dir ./my-site --project prj_abc --target production
138
+ run402 sites deploy-dir ./dist --project prj_abc --quiet
121
139
  `,
122
140
  };
123
141
 
@@ -127,14 +145,58 @@ async function readStdin() {
127
145
  return Buffer.concat(chunks).toString("utf-8");
128
146
  }
129
147
 
148
+ /**
149
+ * Stage manifest files to a temp directory so the SDK's deployDir can walk
150
+ * them. The v1.32 SDK no longer accepts inline file bytes — every deploy
151
+ * goes through plan/commit and reads from a directory.
152
+ */
153
+ function stageFilesToTempDir(files) {
154
+ const stage = mkdtempSync(join(tmpdir(), "run402-deploy-stage-"));
155
+ for (const f of files) {
156
+ if (typeof f.file !== "string" || typeof f.data !== "string") {
157
+ throw new Error("manifest entry missing required 'file' or 'data' string");
158
+ }
159
+ const target = join(stage, f.file);
160
+ mkdirSync(dirname(target), { recursive: true });
161
+ const buf = (f.encoding ?? "utf-8") === "base64"
162
+ ? Buffer.from(f.data, "base64")
163
+ : Buffer.from(f.data, "utf-8");
164
+ writeFileSync(target, buf);
165
+ }
166
+ return stage;
167
+ }
168
+
169
+ /**
170
+ * Returns an onEvent callback that writes each event as a single-line JSON
171
+ * object to stderr — or a no-op when --quiet was passed. The CLI is
172
+ * agent-first; structured stderr lets a piping agent stream progress with
173
+ * `2>events.log` while keeping stdout reserved for the final result envelope.
174
+ *
175
+ * Uses `console.error` so that test harnesses intercepting console output
176
+ * see each event line; `console.error` appends a newline by default.
177
+ */
178
+ function makeStderrEventWriter(quiet) {
179
+ if (quiet) return undefined;
180
+ return (event) => {
181
+ console.error(JSON.stringify(event));
182
+ };
183
+ }
184
+
130
185
  async function deploy(args) {
131
- const opts = { manifest: null, project: undefined, target: undefined, inherit: false };
186
+ const opts = { manifest: null, project: undefined, target: undefined, quiet: false };
132
187
  for (let i = 0; i < args.length; i++) {
133
188
  if (args[i] === "--help" || args[i] === "-h") { console.log(HELP); process.exit(0); }
134
189
  if (args[i] === "--manifest" && args[i + 1]) opts.manifest = args[++i];
135
190
  if (args[i] === "--project" && args[i + 1]) opts.project = args[++i];
136
191
  if (args[i] === "--target" && args[i + 1]) opts.target = args[++i];
137
- if (args[i] === "--inherit") opts.inherit = true;
192
+ if (args[i] === "--quiet") opts.quiet = true;
193
+ if (args[i] === "--inherit") {
194
+ console.error(JSON.stringify({
195
+ status: "error",
196
+ message: "--inherit is removed in v1.32; the SDK now uploads only changed files automatically.",
197
+ }));
198
+ process.exit(1);
199
+ }
138
200
  }
139
201
  const projectId = resolveProjectId(opts.project);
140
202
  const raw = opts.manifest ? readFileSync(opts.manifest, "utf-8") : await readStdin();
@@ -142,13 +204,15 @@ async function deploy(args) {
142
204
  if (opts.manifest) resolveFilePathsInManifest(manifest, dirname(resolve(opts.manifest)));
143
205
 
144
206
  // Preserve the aggressive early exit when no allowance is configured.
145
- allowanceAuthHeaders("/deployments/v1");
207
+ allowanceAuthHeaders("/deploy/v1/plan");
146
208
 
209
+ const stage = stageFilesToTempDir(manifest.files || []);
147
210
  try {
148
- const data = await getSdk().sites.deploy(projectId, {
149
- files: manifest.files,
211
+ const data = await getSdk().sites.deployDir({
212
+ project: projectId,
213
+ dir: stage,
150
214
  target: opts.target,
151
- inherit: opts.inherit,
215
+ onEvent: makeStderrEventWriter(opts.quiet),
152
216
  });
153
217
  if (data.deployment_id) {
154
218
  updateProject(projectId, { last_deployment_id: data.deployment_id });
@@ -156,16 +220,25 @@ async function deploy(args) {
156
220
  console.log(JSON.stringify(data, null, 2));
157
221
  } catch (err) {
158
222
  reportSdkError(err);
223
+ } finally {
224
+ rmSync(stage, { recursive: true, force: true });
159
225
  }
160
226
  }
161
227
 
162
228
  async function deployDir(args) {
163
- const opts = { dir: null, project: undefined, target: undefined, inherit: false };
229
+ const opts = { dir: null, project: undefined, target: undefined, quiet: false };
164
230
  for (let i = 0; i < args.length; i++) {
165
231
  if (args[i] === "--help" || args[i] === "-h") { console.log(SUB_HELP["deploy-dir"]); process.exit(0); }
166
232
  if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
167
233
  if (args[i] === "--target" && args[i + 1]) { opts.target = args[++i]; continue; }
168
- if (args[i] === "--inherit") { opts.inherit = true; continue; }
234
+ if (args[i] === "--quiet") { opts.quiet = true; continue; }
235
+ if (args[i] === "--inherit") {
236
+ console.error(JSON.stringify({
237
+ status: "error",
238
+ message: "--inherit is removed in v1.32; the SDK now uploads only changed files automatically.",
239
+ }));
240
+ process.exit(1);
241
+ }
169
242
  if (!args[i].startsWith("-") && opts.dir === null) { opts.dir = args[i]; continue; }
170
243
  }
171
244
  if (!opts.dir) {
@@ -175,14 +248,14 @@ async function deployDir(args) {
175
248
  const projectId = resolveProjectId(opts.project);
176
249
 
177
250
  // Preserve the aggressive early exit when no allowance is configured.
178
- allowanceAuthHeaders("/deployments/v1");
251
+ allowanceAuthHeaders("/deploy/v1/plan");
179
252
 
180
253
  try {
181
254
  const data = await getSdk().sites.deployDir({
182
255
  project: projectId,
183
256
  dir: opts.dir,
184
257
  target: opts.target,
185
- inherit: opts.inherit,
258
+ onEvent: makeStderrEventWriter(opts.quiet),
186
259
  });
187
260
  if (data.deployment_id) {
188
261
  updateProject(projectId, { last_deployment_id: data.deployment_id });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 and MPP micropayments.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,9 +1,14 @@
1
1
  /**
2
- * `sites` namespace — static site deployments via `/deployments/v1`.
2
+ * `sites` namespace — static site deployments.
3
3
  *
4
- * `deploy` uses allowance-based SIWX auth (no project service key — the
5
- * project is referenced in the request body). Callers persisting
6
- * `last_deployment_id` locally should do so after a successful response.
4
+ * As of v1.32 the inline-bytes deploy path (`POST /deployments/v1` with
5
+ * base64 file blobs) is REMOVED at the gateway (returns 410 Gone). Callers
6
+ * migrate to `NodeSites.deployDir` from `@run402/sdk/node`, which uses the
7
+ * plan/commit transport over `/deploy/v1/plan` + `/deploy/v1/commit`.
8
+ *
9
+ * The isomorphic surface keeps only the public read-only `getDeployment`
10
+ * call. `SiteFile` is preserved for `apps.bundleDeploy` (separate `/deploy/v1`
11
+ * endpoint, unaffected by the v1.32 cutover).
7
12
  */
8
13
  import type { Client } from "../kernel.js";
9
14
  export interface SiteFile {
@@ -13,20 +18,13 @@ export interface SiteFile {
13
18
  data: string;
14
19
  encoding?: "utf-8" | "base64";
15
20
  }
16
- export interface SiteDeployOptions {
17
- /** Files to deploy. Paths are relative to the site root. */
18
- files: SiteFile[];
19
- /** Deployment target label, e.g. `"production"`. */
20
- target?: string;
21
- /**
22
- * When true, unchanged files are copied from the previous deployment —
23
- * only changed/new files need to appear in `files`.
24
- */
25
- inherit?: boolean;
26
- }
27
21
  export interface SiteDeployResult {
28
22
  deployment_id: string;
29
23
  url: string;
24
+ /** Total bytes across the manifest (present when reported by the gateway). */
25
+ bytes_total?: number;
26
+ /** Bytes uploaded in this deploy (0 on a no-op redeploy). */
27
+ bytes_uploaded?: number;
30
28
  }
31
29
  export interface DeploymentInfo {
32
30
  id: string;
@@ -40,12 +38,6 @@ export interface DeploymentInfo {
40
38
  export declare class Sites {
41
39
  private readonly client;
42
40
  constructor(client: Client);
43
- /**
44
- * Deploy a static site. Payment flows through the configured fetch wrapper
45
- * (x402 in Node when a tier purchase is required; typically free with an
46
- * active tier).
47
- */
48
- deploy(projectId: string, opts: SiteDeployOptions): Promise<SiteDeployResult>;
49
41
  /** Get deployment metadata by id. Public — no project auth. */
50
42
  getDeployment(deploymentId: string): Promise<DeploymentInfo>;
51
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sites.d.ts","sourceRoot":"","sources":["../../src/namespaces/sites.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,WAAW,QAAQ;IACvB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,KAAK;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAYnF,+DAA+D;IACzD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAMnE"}
1
+ {"version":3,"file":"sites.d.ts","sourceRoot":"","sources":["../../src/namespaces/sites.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,WAAW,QAAQ;IACvB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,KAAK;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C,+DAA+D;IACzD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAMnE"}
@@ -1,32 +1,20 @@
1
1
  /**
2
- * `sites` namespace — static site deployments via `/deployments/v1`.
2
+ * `sites` namespace — static site deployments.
3
3
  *
4
- * `deploy` uses allowance-based SIWX auth (no project service key — the
5
- * project is referenced in the request body). Callers persisting
6
- * `last_deployment_id` locally should do so after a successful response.
4
+ * As of v1.32 the inline-bytes deploy path (`POST /deployments/v1` with
5
+ * base64 file blobs) is REMOVED at the gateway (returns 410 Gone). Callers
6
+ * migrate to `NodeSites.deployDir` from `@run402/sdk/node`, which uses the
7
+ * plan/commit transport over `/deploy/v1/plan` + `/deploy/v1/commit`.
8
+ *
9
+ * The isomorphic surface keeps only the public read-only `getDeployment`
10
+ * call. `SiteFile` is preserved for `apps.bundleDeploy` (separate `/deploy/v1`
11
+ * endpoint, unaffected by the v1.32 cutover).
7
12
  */
8
13
  export class Sites {
9
14
  client;
10
15
  constructor(client) {
11
16
  this.client = client;
12
17
  }
13
- /**
14
- * Deploy a static site. Payment flows through the configured fetch wrapper
15
- * (x402 in Node when a tier purchase is required; typically free with an
16
- * active tier).
17
- */
18
- async deploy(projectId, opts) {
19
- const body = { project: projectId, files: opts.files };
20
- if (opts.target !== undefined)
21
- body.target = opts.target;
22
- if (opts.inherit)
23
- body.inherit = true;
24
- return this.client.request("/deployments/v1", {
25
- method: "POST",
26
- body,
27
- context: "deploying site",
28
- });
29
- }
30
18
  /** Get deployment metadata by id. Public — no project auth. */
31
19
  async getDeployment(deploymentId) {
32
20
  return this.client.request(`/deployments/v1/${deploymentId}`, {
@@ -1 +1 @@
1
- {"version":3,"file":"sites.js","sourceRoot":"","sources":["../../src/namespaces/sites.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuCH,MAAM,OAAO,KAAK;IACa;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,SAAiB,EAAE,IAAuB;QACrD,MAAM,IAAI,GAA4B,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAChF,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QACzD,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEtC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAmB,iBAAiB,EAAE;YAC9D,MAAM,EAAE,MAAM;YACd,IAAI;YACJ,OAAO,EAAE,gBAAgB;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,aAAa,CAAC,YAAoB;QACtC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAiB,mBAAmB,YAAY,EAAE,EAAE;YAC5E,OAAO,EAAE,qBAAqB;YAC9B,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"sites.js","sourceRoot":"","sources":["../../src/namespaces/sites.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA+BH,MAAM,OAAO,KAAK;IACa;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,+DAA+D;IAC/D,KAAK,CAAC,aAAa,CAAC,YAAoB;QACtC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAiB,mBAAmB,YAAY,EAAE,EAAE;YAC5E,OAAO,EAAE,qBAAqB;YAC9B,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * RFC 8785 (JCS) canonical JSON for the v1.32 deploy-plan manifest digest.
3
+ *
4
+ * MUST stay byte-for-byte identical to the gateway's
5
+ * `services/deploy-plans.ts:canonicalizeJson`. A digest mismatch breaks
6
+ * idempotency: the SDK's hash won't match the gateway's, so retrying a
7
+ * plan creates a NEW plan instead of finding the existing one.
8
+ *
9
+ * Spec for our value domain:
10
+ * - object keys sorted ASCII-ascending
11
+ * - arrays comma-separated, no whitespace
12
+ * - strings/numbers via JSON.stringify (matches RFC 8785 for ASCII paths,
13
+ * hex sha256, integer sizes, ASCII content_types)
14
+ */
15
+ export interface ManifestEntry {
16
+ path: string;
17
+ sha256: string;
18
+ size: number;
19
+ content_type: string;
20
+ }
21
+ export interface Manifest {
22
+ files: ManifestEntry[];
23
+ }
24
+ export declare function canonicalizeJson(value: unknown): string;
25
+ /**
26
+ * Build the canonical manifest object the gateway hashes: `{ files: [...] }`
27
+ * with entries sorted by path and only the four expected keys per entry.
28
+ * `content_type` defaults to `"application/octet-stream"` when absent.
29
+ */
30
+ export declare function buildCanonicalManifest(entries: Array<{
31
+ path: string;
32
+ sha256: string;
33
+ size: number;
34
+ content_type?: string;
35
+ }>): Manifest;
36
+ /**
37
+ * Compute the hex SHA-256 of the canonical JSON encoding of a manifest.
38
+ * Matches the gateway's `computeManifestDigest`.
39
+ */
40
+ export declare function computeManifestDigest(manifest: Manifest): Promise<string>;
41
+ //# sourceMappingURL=canonicalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../../src/node/canonicalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAavD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACpF,QAAQ,CAUV;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAO/E"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * RFC 8785 (JCS) canonical JSON for the v1.32 deploy-plan manifest digest.
3
+ *
4
+ * MUST stay byte-for-byte identical to the gateway's
5
+ * `services/deploy-plans.ts:canonicalizeJson`. A digest mismatch breaks
6
+ * idempotency: the SDK's hash won't match the gateway's, so retrying a
7
+ * plan creates a NEW plan instead of finding the existing one.
8
+ *
9
+ * Spec for our value domain:
10
+ * - object keys sorted ASCII-ascending
11
+ * - arrays comma-separated, no whitespace
12
+ * - strings/numbers via JSON.stringify (matches RFC 8785 for ASCII paths,
13
+ * hex sha256, integer sizes, ASCII content_types)
14
+ */
15
+ export function canonicalizeJson(value) {
16
+ if (value === null)
17
+ return "null";
18
+ if (typeof value === "boolean")
19
+ return value ? "true" : "false";
20
+ if (typeof value === "number")
21
+ return JSON.stringify(value);
22
+ if (typeof value === "string")
23
+ return JSON.stringify(value);
24
+ if (Array.isArray(value))
25
+ return "[" + value.map(canonicalizeJson).join(",") + "]";
26
+ if (typeof value === "object") {
27
+ const obj = value;
28
+ const keys = Object.keys(obj).sort();
29
+ const pairs = keys.map((k) => JSON.stringify(k) + ":" + canonicalizeJson(obj[k]));
30
+ return "{" + pairs.join(",") + "}";
31
+ }
32
+ throw new Error("canonicalizeJson: unsupported value type");
33
+ }
34
+ /**
35
+ * Build the canonical manifest object the gateway hashes: `{ files: [...] }`
36
+ * with entries sorted by path and only the four expected keys per entry.
37
+ * `content_type` defaults to `"application/octet-stream"` when absent.
38
+ */
39
+ export function buildCanonicalManifest(entries) {
40
+ const files = entries
41
+ .map((e) => ({
42
+ path: e.path,
43
+ sha256: e.sha256,
44
+ size: e.size,
45
+ content_type: e.content_type ?? "application/octet-stream",
46
+ }))
47
+ .sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
48
+ return { files };
49
+ }
50
+ /**
51
+ * Compute the hex SHA-256 of the canonical JSON encoding of a manifest.
52
+ * Matches the gateway's `computeManifestDigest`.
53
+ */
54
+ export async function computeManifestDigest(manifest) {
55
+ const canonical = canonicalizeJson(manifest);
56
+ const bytes = new TextEncoder().encode(canonical);
57
+ const hash = await crypto.subtle.digest("SHA-256", bytes);
58
+ return Array.from(new Uint8Array(hash))
59
+ .map((b) => b.toString(16).padStart(2, "0"))
60
+ .join("");
61
+ }
62
+ //# sourceMappingURL=canonicalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalize.js","sourceRoot":"","sources":["../../src/node/canonicalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAaH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACnF,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClF,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACrC,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAqF;IAErF,MAAM,KAAK,GAAoB,OAAO;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,0BAA0B;KAC3D,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAAkB;IAC5D,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAgC,CAAC,CAAC;IACrF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;SACpC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
@@ -14,6 +14,10 @@
14
14
  * const project = await r.projects.provision({ tier: "prototype" });
15
15
  * await r.sites.deployDir({ project: project.project_id, dir: "./my-site" });
16
16
  * ```
17
+ *
18
+ * `deployDir` uses the v1.32 plan/commit transport and only uploads the
19
+ * bytes the gateway doesn't already have. Re-deploying an unchanged tree
20
+ * issues no S3 PUTs.
17
21
  */
18
22
  import { Run402 } from "../index.js";
19
23
  import { NodeSites } from "./sites-node.js";
@@ -49,7 +53,7 @@ export type NodeRun402 = Omit<Run402, "sites"> & {
49
53
  */
50
54
  export declare function run402(opts?: NodeRun402Options): NodeRun402;
51
55
  export { NodeSites } from "./sites-node.js";
52
- export type { DeployDirOptions } from "./sites-node.js";
56
+ export type { DeployDirOptions, DeployEvent } from "./sites-node.js";
53
57
  export { NodeCredentialsProvider } from "./credentials.js";
54
58
  export { setupPaidFetch, createLazyPaidFetch } from "./paid-fetch.js";
55
59
  export { Run402, Run402Error, PaymentRequired, ProjectNotFound, Unauthorized, ApiError, NetworkError, LocalError, } from "../index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC;AAIzD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,IAAI,GAAE,iBAAsB,GAAG,UAAU,CAqB/D;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EACL,MAAM,EACN,WAAW,EACX,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,MAAM,GACP,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC;AAIzD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,IAAI,GAAE,iBAAsB,GAAG,UAAU,CAqB/D;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EACL,MAAM,EACN,WAAW,EACX,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,MAAM,GACP,MAAM,aAAa,CAAC"}
@@ -14,6 +14,10 @@
14
14
  * const project = await r.projects.provision({ tier: "prototype" });
15
15
  * await r.sites.deployDir({ project: project.project_id, dir: "./my-site" });
16
16
  * ```
17
+ *
18
+ * `deployDir` uses the v1.32 plan/commit transport and only uploads the
19
+ * bytes the gateway doesn't already have. Re-deploying an unchanged tree
20
+ * issues no S3 PUTs.
17
21
  */
18
22
  import { getApiBase } from "../../core-dist/config.js";
19
23
  import { Run402 } from "../index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAqB5C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,MAAM,CAAC,OAA0B,EAAE;IACjD,MAAM,OAAO,GAAkB;QAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,UAAU,EAAE;QACrC,WAAW,EAAE,IAAI,uBAAuB,CAAC;YACvC,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC;QACF,KAAK,EACH,IAAI,CAAC,KAAK;YACV,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;KACtF,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjC,yEAAyE;IACzE,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,MAAM,GAAI,IAAI,CAAC,KAAuC,CAAC,MAAM,CAAC;IACnE,IAAwC,CAAC,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAExE,OAAO,IAA6B,CAAC;AACvC,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtE,6EAA6E;AAC7E,OAAO,EACL,MAAM,EACN,WAAW,EACX,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAqB5C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,MAAM,CAAC,OAA0B,EAAE;IACjD,MAAM,OAAO,GAAkB;QAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,UAAU,EAAE;QACrC,WAAW,EAAE,IAAI,uBAAuB,CAAC;YACvC,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC;QACF,KAAK,EACH,IAAI,CAAC,KAAK;YACV,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;KACtF,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjC,yEAAyE;IACzE,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,MAAM,GAAI,IAAI,CAAC,KAAuC,CAAC,MAAM,CAAC;IACnE,IAAwC,CAAC,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAExE,OAAO,IAA6B,CAAC;AACvC,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtE,6EAA6E;AAC7E,OAAO,EACL,MAAM,EACN,WAAW,EACX,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC"}
@@ -1,23 +1,123 @@
1
1
  /**
2
2
  * Node-only augmentation of the `sites` namespace.
3
3
  *
4
- * Adds a `deployDir(dir)` helper that walks a directory on disk, reads
5
- * each file, detects text vs. binary, and assembles the inline
6
- * `SiteFile[]` manifest the isomorphic `Sites.deploy()` expects.
4
+ * `deployDir(dir)` walks a directory, computes per-file SHA-256 + size,
5
+ * builds a canonical manifest, and ships it via the v1.32 plan/commit
6
+ * transport:
7
7
  *
8
- * This file imports `node:fs/promises` and so cannot run in a V8 isolate.
9
- * It is only wired into the SDK via the `@run402/sdk/node` entry point.
8
+ * 1. POST /deploy/v1/plan { manifest_digest, manifest } returns per-file
9
+ * state (`present`, `satisfied_by_plan`, or `missing` + presigned URLs).
10
+ * 2. PUT each `missing` file's bytes to its presigned S3 URL(s),
11
+ * sending `x-amz-checksum-sha256` to satisfy the signed checksum
12
+ * algorithm. Single-PUT covers small files; multipart covers large
13
+ * files (the gateway picks per-part sizing).
14
+ * 3. POST /deploy/v1/commit { plan_id } — Stage 1 (DB) returns
15
+ * synchronously with `applied`, `noop`, `copying`, or `failed`.
16
+ * `copying` triggers a poll loop on `GET /deployments/v1/:id` until
17
+ * `ready` (or `failed`).
18
+ *
19
+ * The canonicalize used to compute `manifest_digest` MUST match the
20
+ * gateway's byte-for-byte (see `./canonicalize.ts`).
21
+ *
22
+ * Imports `node:fs/promises` and so cannot run in a V8 isolate. Wired into
23
+ * the SDK via the `@run402/sdk/node` entry point only.
10
24
  */
11
- import { Sites, type SiteFile, type SiteDeployResult } from "../namespaces/sites.js";
25
+ import { Sites, type SiteDeployResult } from "../namespaces/sites.js";
26
+ import { type ManifestEntry } from "./canonicalize.js";
12
27
  export interface DeployDirOptions {
13
28
  /** Project ID the deployment is linked to. */
14
29
  project: string;
15
30
  /** Local directory to walk. Paths in the manifest are relative to this root. */
16
31
  dir: string;
17
- /** When true, unchanged files are copied from the previous deployment server-side. */
18
- inherit?: boolean;
19
32
  /** Deployment target label, e.g. `"production"`. */
20
33
  target?: string;
34
+ /**
35
+ * Optional progress callback. Invoked synchronously at four phases of the
36
+ * deploy. Errors thrown from the callback are caught and dropped — a buggy
37
+ * consumer can't abort the deploy.
38
+ */
39
+ onEvent?: (event: DeployEvent) => void;
40
+ }
41
+ /**
42
+ * Progress event emitted by {@link NodeSites.deployDir} when the caller
43
+ * supplies an `onEvent` callback. A discriminated union keyed by `phase`.
44
+ */
45
+ export type DeployEvent =
46
+ /** `POST /deploy/v1/plan` returned. Fires once per deploy. */
47
+ {
48
+ phase: "plan";
49
+ manifest_size: number;
50
+ }
51
+ /**
52
+ * One file's bytes were successfully PUT to S3. Fires once per `missing`
53
+ * entry in the plan response — files reported as `present` or
54
+ * `satisfied_by_plan` do not trigger an upload event.
55
+ */
56
+ | {
57
+ phase: "upload";
58
+ file: string;
59
+ sha256: string;
60
+ done: number;
61
+ total: number;
62
+ }
63
+ /** About to call `POST /deploy/v1/commit`. Fires once per deploy. */
64
+ | {
65
+ phase: "commit";
66
+ }
67
+ /** Stage-2 copy poll tick (`GET /deployments/v1/:id`). Fires per iteration when commit returned `copying`. */
68
+ | {
69
+ phase: "poll";
70
+ status: string;
71
+ elapsed_ms: number;
72
+ };
73
+ /** One walked file plus everything we need to hash, plan, and upload it. */
74
+ interface WalkedFile {
75
+ path: string;
76
+ size: number;
77
+ sha256: string;
78
+ content_type: string;
79
+ bytes: Buffer;
80
+ }
81
+ interface PlanFilePresent {
82
+ sha256: string;
83
+ present: true;
84
+ size: number;
85
+ content_type: string;
86
+ }
87
+ interface PlanFileSatisfied {
88
+ sha256: string;
89
+ satisfied_by_plan: true;
90
+ size: number;
91
+ content_type: string;
92
+ }
93
+ interface PlanFileMissing {
94
+ sha256: string;
95
+ missing: true;
96
+ upload_id: string;
97
+ mode: "single" | "multipart";
98
+ key: string;
99
+ staging_key: string;
100
+ part_size_bytes: number;
101
+ part_count: number;
102
+ parts: Array<{
103
+ part_number: number;
104
+ url: string;
105
+ byte_start: number;
106
+ byte_end: number;
107
+ }>;
108
+ expires_at: string;
109
+ }
110
+ type PlanFileResponse = PlanFilePresent | PlanFileSatisfied | PlanFileMissing;
111
+ interface PlanResponse {
112
+ plan_id: string;
113
+ files: PlanFileResponse[];
114
+ }
115
+ interface CommitResponse {
116
+ deployment_id: string;
117
+ url: string;
118
+ status: "applied" | "noop" | "copying" | "failed";
119
+ bytes_total?: number;
120
+ bytes_uploaded?: number;
21
121
  }
22
122
  /**
23
123
  * Sites namespace enriched with the Node-only `deployDir` convenience.
@@ -25,23 +125,23 @@ export interface DeployDirOptions {
25
125
  */
26
126
  export declare class NodeSites extends Sites {
27
127
  /**
28
- * Deploy every file under `dir` as a static site. Equivalent to calling
29
- * {@link Sites.deploy} with a manifest you assembled by hand, but the
30
- * walk, binary detection, and encoding are handled for you.
128
+ * Deploy every file under `dir` as a static site. Walks the tree, hashes
129
+ * each file, plans the deploy with the gateway (which dedupes against
130
+ * already-uploaded content), uploads only the missing bytes, then
131
+ * commits.
31
132
  *
32
133
  * Files named `.git`, `node_modules`, or `.DS_Store` are skipped at every
33
134
  * depth. Symlinks cause a {@link LocalError} — they are not followed.
34
135
  */
35
136
  deployDir(opts: DeployDirOptions): Promise<SiteDeployResult>;
36
137
  }
37
- /**
38
- * Walk `root` and return a `SiteFile[]` with POSIX-style relative paths.
39
- * Exported for tests; not part of the public SDK API.
40
- */
41
- export declare function collectSiteFiles(root: string): Promise<SiteFile[]>;
42
138
  /**
43
139
  * Normalize a relative path to POSIX forward slashes. Exposed for tests;
44
140
  * not part of the public SDK API.
45
141
  */
46
142
  export declare function normalizeRelPath(rel: string): string;
143
+ /** @internal — exposed for tests, not part of the public SDK API. */
144
+ export declare function _collectFilesForTest(root: string): Promise<WalkedFile[]>;
145
+ /** @internal — exposed for tests, not part of the public SDK API. */
146
+ export type { WalkedFile, PlanResponse, CommitResponse, ManifestEntry };
47
147
  //# sourceMappingURL=sites-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sites-node.d.ts","sourceRoot":"","sources":["../../src/node/sites-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAMrF,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC;;;;;;;OAOG;IACG,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAcnE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CA2BxE;AA+CD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"sites-node.d.ts","sourceRoot":"","sources":["../../src/node/sites-node.ts"],"names":[],"mappings":"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"}
@@ -1,49 +1,260 @@
1
1
  /**
2
2
  * Node-only augmentation of the `sites` namespace.
3
3
  *
4
- * Adds a `deployDir(dir)` helper that walks a directory on disk, reads
5
- * each file, detects text vs. binary, and assembles the inline
6
- * `SiteFile[]` manifest the isomorphic `Sites.deploy()` expects.
4
+ * `deployDir(dir)` walks a directory, computes per-file SHA-256 + size,
5
+ * builds a canonical manifest, and ships it via the v1.32 plan/commit
6
+ * transport:
7
7
  *
8
- * This file imports `node:fs/promises` and so cannot run in a V8 isolate.
9
- * It is only wired into the SDK via the `@run402/sdk/node` entry point.
8
+ * 1. POST /deploy/v1/plan { manifest_digest, manifest } returns per-file
9
+ * state (`present`, `satisfied_by_plan`, or `missing` + presigned URLs).
10
+ * 2. PUT each `missing` file's bytes to its presigned S3 URL(s),
11
+ * sending `x-amz-checksum-sha256` to satisfy the signed checksum
12
+ * algorithm. Single-PUT covers small files; multipart covers large
13
+ * files (the gateway picks per-part sizing).
14
+ * 3. POST /deploy/v1/commit { plan_id } — Stage 1 (DB) returns
15
+ * synchronously with `applied`, `noop`, `copying`, or `failed`.
16
+ * `copying` triggers a poll loop on `GET /deployments/v1/:id` until
17
+ * `ready` (or `failed`).
18
+ *
19
+ * The canonicalize used to compute `manifest_digest` MUST match the
20
+ * gateway's byte-for-byte (see `./canonicalize.ts`).
21
+ *
22
+ * Imports `node:fs/promises` and so cannot run in a V8 isolate. Wired into
23
+ * the SDK via the `@run402/sdk/node` entry point only.
10
24
  */
11
25
  import { readdir, readFile, lstat } from "node:fs/promises";
12
26
  import { join, relative, sep } from "node:path";
13
27
  import { Sites } from "../namespaces/sites.js";
14
- import { LocalError } from "../errors.js";
28
+ import { ApiError, LocalError, Run402Error } from "../errors.js";
29
+ import { buildCanonicalManifest, computeManifestDigest, } from "./canonicalize.js";
15
30
  const DEFAULT_IGNORE = new Set([".git", "node_modules", ".DS_Store"]);
16
31
  const CONTEXT = "deploying directory";
32
+ /** Cap on total time spent waiting for Stage 2 copy to drain. */
33
+ const COPY_POLL_TIMEOUT_MS = 10 * 60 * 1000;
34
+ /** Initial poll interval; we hold this for ~30 s, then back off. */
35
+ const COPY_POLL_INITIAL_MS = 1_000;
36
+ const COPY_POLL_MAX_MS = 30_000;
37
+ /** Time after which we re-call /deploy/v1/plan to refresh presigned URLs (1 h TTL). */
38
+ const URL_REFRESH_AT_MS = 50 * 60 * 1000;
17
39
  /**
18
40
  * Sites namespace enriched with the Node-only `deployDir` convenience.
19
41
  * All existing `Sites` methods are inherited unchanged.
20
42
  */
21
43
  export class NodeSites extends Sites {
22
44
  /**
23
- * Deploy every file under `dir` as a static site. Equivalent to calling
24
- * {@link Sites.deploy} with a manifest you assembled by hand, but the
25
- * walk, binary detection, and encoding are handled for you.
45
+ * Deploy every file under `dir` as a static site. Walks the tree, hashes
46
+ * each file, plans the deploy with the gateway (which dedupes against
47
+ * already-uploaded content), uploads only the missing bytes, then
48
+ * commits.
26
49
  *
27
50
  * Files named `.git`, `node_modules`, or `.DS_Store` are skipped at every
28
51
  * depth. Symlinks cause a {@link LocalError} — they are not followed.
29
52
  */
30
53
  async deployDir(opts) {
31
- const files = await collectSiteFiles(opts.dir);
54
+ const emit = (event) => {
55
+ if (!opts.onEvent)
56
+ return;
57
+ try {
58
+ opts.onEvent(event);
59
+ }
60
+ catch {
61
+ // Swallow — a buggy consumer must not abort a deploy in progress.
62
+ }
63
+ };
64
+ const files = await collectFiles(opts.dir);
32
65
  if (files.length === 0) {
33
66
  throw new LocalError(`directory ${opts.dir} contains no deployable files`, CONTEXT);
34
67
  }
35
- return this.deploy(opts.project, {
36
- files,
37
- inherit: opts.inherit,
38
- target: opts.target,
68
+ const manifest = buildCanonicalManifest(files.map((f) => ({
69
+ path: f.path,
70
+ sha256: f.sha256,
71
+ size: f.size,
72
+ content_type: f.content_type,
73
+ })));
74
+ const manifestDigest = await computeManifestDigest(manifest);
75
+ // Map sha → bytes so we can satisfy the plan response without re-walking.
76
+ // Multiple paths may share the same sha (identical files); any copy works.
77
+ const bytesBySha = new Map();
78
+ // Map sha → first manifest path with that content, so upload events can
79
+ // report a human-readable file path even when multiple paths dedupe.
80
+ const pathBySha = new Map();
81
+ for (const f of files) {
82
+ bytesBySha.set(f.sha256, f.bytes);
83
+ if (!pathBySha.has(f.sha256))
84
+ pathBySha.set(f.sha256, f.path);
85
+ }
86
+ // Reach through to the kernel client. `Sites.client` is `private` in TS
87
+ // but enumerable at runtime; the cast bypasses the visibility check.
88
+ const client = this.client;
89
+ const planClient = new ClientFromBase(client);
90
+ const plan = await planClient.requestPlan(opts.project, manifest, manifestDigest);
91
+ const planAt = Date.now();
92
+ emit({ phase: "plan", manifest_size: manifest.files.length });
93
+ const totalMissing = plan.files.filter(isMissing).length;
94
+ let doneCounter = 0;
95
+ let activePlan = plan;
96
+ let activePlanAt = planAt;
97
+ for (const entry of activePlan.files) {
98
+ if (!isMissing(entry))
99
+ continue;
100
+ // Refresh the plan if the URL TTL window is about to close.
101
+ if (Date.now() - activePlanAt > URL_REFRESH_AT_MS) {
102
+ activePlan = await planClient.requestPlan(opts.project, manifest, manifestDigest);
103
+ activePlanAt = Date.now();
104
+ }
105
+ const refreshed = activePlan.files.find((e) => e.sha256 === entry.sha256);
106
+ const target = refreshed && isMissing(refreshed) ? refreshed : entry;
107
+ const bytes = bytesBySha.get(target.sha256);
108
+ if (!bytes) {
109
+ throw new LocalError(`internal: no local bytes for sha ${target.sha256.slice(0, 12)}…`, CONTEXT);
110
+ }
111
+ try {
112
+ await uploadOne(client.fetch, target, bytes);
113
+ }
114
+ catch (err) {
115
+ // S3 returns 403 when the presigned URL has expired. Refresh once.
116
+ if (err instanceof ApiError && err.status === 403) {
117
+ activePlan = await planClient.requestPlan(opts.project, manifest, manifestDigest);
118
+ activePlanAt = Date.now();
119
+ const fresh = activePlan.files.find((e) => e.sha256 === target.sha256);
120
+ if (fresh && isMissing(fresh)) {
121
+ await uploadOne(client.fetch, fresh, bytes);
122
+ }
123
+ else {
124
+ throw err;
125
+ }
126
+ }
127
+ else {
128
+ throw err;
129
+ }
130
+ }
131
+ doneCounter += 1;
132
+ emit({
133
+ phase: "upload",
134
+ file: pathBySha.get(target.sha256) ?? target.sha256,
135
+ sha256: target.sha256,
136
+ done: doneCounter,
137
+ total: totalMissing,
138
+ });
139
+ }
140
+ emit({ phase: "commit" });
141
+ const commit = await planClient.commit(opts.project, activePlan.plan_id);
142
+ if (commit.status === "applied" || commit.status === "noop") {
143
+ return shapeResult(commit);
144
+ }
145
+ if (commit.status === "copying") {
146
+ const final = await pollUntilReady(this, commit.deployment_id, emit);
147
+ return { deployment_id: commit.deployment_id, url: commit.url, ...final };
148
+ }
149
+ // status === "failed": stage 2 exhausted retries. Surface as ApiError so
150
+ // callers can decide whether to re-call deployDir (which re-commits).
151
+ throw new ApiError(`Deploy commit failed for plan ${activePlan.plan_id} after copy retries`, 500, commit, "committing deploy");
152
+ }
153
+ }
154
+ class ClientFromBase {
155
+ client;
156
+ constructor(client) {
157
+ this.client = client;
158
+ }
159
+ async requestPlan(projectId, manifest, manifestDigest) {
160
+ return this.client.request("/deploy/v1/plan", {
161
+ method: "POST",
162
+ body: { project: projectId, manifest_digest: manifestDigest, manifest },
163
+ context: "planning deploy",
164
+ });
165
+ }
166
+ async commit(projectId, planId) {
167
+ return this.client.request("/deploy/v1/commit", {
168
+ method: "POST",
169
+ body: { project: projectId, plan_id: planId },
170
+ context: "committing deploy",
39
171
  });
40
172
  }
41
173
  }
42
- /**
43
- * Walk `root` and return a `SiteFile[]` with POSIX-style relative paths.
44
- * Exported for tests; not part of the public SDK API.
45
- */
46
- export async function collectSiteFiles(root) {
174
+ function isMissing(entry) {
175
+ return entry.missing === true;
176
+ }
177
+ function shapeResult(commit) {
178
+ const out = { deployment_id: commit.deployment_id, url: commit.url };
179
+ if (commit.bytes_total !== undefined)
180
+ out.bytes_total = commit.bytes_total;
181
+ if (commit.bytes_uploaded !== undefined)
182
+ out.bytes_uploaded = commit.bytes_uploaded;
183
+ return out;
184
+ }
185
+ // ─── Upload ──────────────────────────────────────────────────────────────────
186
+ async function uploadOne(fetchFn, entry, bytes) {
187
+ if (entry.mode === "single") {
188
+ if (entry.parts.length !== 1) {
189
+ throw new LocalError(`internal: single-mode upload for ${entry.sha256.slice(0, 12)}… returned ${entry.parts.length} parts`, CONTEXT);
190
+ }
191
+ const part = entry.parts[0];
192
+ const slice = bytes.subarray(part.byte_start, part.byte_end + 1);
193
+ // For single-PUT, the URL pre-commits the whole-object SHA — we send
194
+ // the same value (in base64, not hex) on the PUT.
195
+ const checksum = base64FromHex(entry.sha256);
196
+ await putToS3(fetchFn, part.url, slice, checksum, part.part_number);
197
+ return;
198
+ }
199
+ // multipart: each part PUT carries its own per-part SHA-256 base64.
200
+ for (const part of entry.parts) {
201
+ const slice = bytes.subarray(part.byte_start, part.byte_end + 1);
202
+ const checksum = await sha256Base64(slice);
203
+ await putToS3(fetchFn, part.url, slice, checksum, part.part_number);
204
+ }
205
+ }
206
+ async function putToS3(fetchFn, url, body, checksumBase64, partNumber) {
207
+ let res;
208
+ try {
209
+ res = await fetchFn(url, {
210
+ method: "PUT",
211
+ headers: { "x-amz-checksum-sha256": checksumBase64 },
212
+ body: body,
213
+ });
214
+ }
215
+ catch (err) {
216
+ throw new ApiError(`S3 PUT failed for part ${partNumber}: ${err.message}`, 0, null, "uploading deploy bytes");
217
+ }
218
+ if (!res.ok) {
219
+ const text = await res.text().catch(() => "");
220
+ throw new ApiError(`S3 PUT failed for part ${partNumber} (HTTP ${res.status})${text ? ": " + text.slice(0, 200) : ""}`, res.status, text, "uploading deploy bytes");
221
+ }
222
+ }
223
+ async function pollUntilReady(sites, deploymentId, emit) {
224
+ const start = Date.now();
225
+ let interval = COPY_POLL_INITIAL_MS;
226
+ while (Date.now() - start < COPY_POLL_TIMEOUT_MS) {
227
+ await sleep(interval);
228
+ let info;
229
+ try {
230
+ info = await sites.getDeployment(deploymentId);
231
+ }
232
+ catch (err) {
233
+ // Transient lookup failure: keep polling unless we're a hard error.
234
+ if (err instanceof Run402Error && err.status !== null && err.status >= 500) {
235
+ continue;
236
+ }
237
+ throw err;
238
+ }
239
+ emit({ phase: "poll", status: info.status, elapsed_ms: Date.now() - start });
240
+ if (info.status === "ready" || info.status === "applied") {
241
+ return { status: info.status, url: info.url };
242
+ }
243
+ if (info.status === "failed") {
244
+ throw new ApiError(`Deployment ${deploymentId} entered failed state during copy`, 500, info, "polling deploy");
245
+ }
246
+ // Hold the initial cadence for the first 30 s, then back off to a max.
247
+ if (Date.now() - start > 30_000) {
248
+ interval = Math.min(Math.floor(interval * 1.5), COPY_POLL_MAX_MS);
249
+ }
250
+ }
251
+ throw new ApiError(`Timed out waiting for deployment ${deploymentId} to reach ready (${COPY_POLL_TIMEOUT_MS / 60_000} min)`, 504, null, "polling deploy");
252
+ }
253
+ function sleep(ms) {
254
+ return new Promise((resolve) => setTimeout(resolve, ms));
255
+ }
256
+ // ─── Filesystem walk + hashing ───────────────────────────────────────────────
257
+ async function collectFiles(root) {
47
258
  let rootStat;
48
259
  try {
49
260
  rootStat = await lstat(root);
@@ -89,7 +300,13 @@ async function walkInto(root, current, out) {
89
300
  throw new LocalError(`cannot read file ${fullPath}: ${err.message}`, CONTEXT, err);
90
301
  }
91
302
  const rel = normalizeRelPath(relative(root, fullPath));
92
- out.push(encodeSiteFile(rel, bytes));
303
+ out.push({
304
+ path: rel,
305
+ size: bytes.byteLength,
306
+ sha256: await sha256Hex(bytes),
307
+ content_type: guessContentType(rel),
308
+ bytes,
309
+ });
93
310
  }
94
311
  }
95
312
  }
@@ -100,13 +317,60 @@ async function walkInto(root, current, out) {
100
317
  export function normalizeRelPath(rel) {
101
318
  return sep === "/" ? rel : rel.split(sep).join("/");
102
319
  }
103
- function encodeSiteFile(path, bytes) {
104
- try {
105
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
106
- return { file: path, data: text, encoding: "utf-8" };
107
- }
108
- catch {
109
- return { file: path, data: bytes.toString("base64"), encoding: "base64" };
320
+ async function sha256Hex(bytes) {
321
+ const hash = await crypto.subtle.digest("SHA-256", bytes);
322
+ return Array.from(new Uint8Array(hash))
323
+ .map((b) => b.toString(16).padStart(2, "0"))
324
+ .join("");
325
+ }
326
+ async function sha256Base64(bytes) {
327
+ const hash = await crypto.subtle.digest("SHA-256", bytes);
328
+ return Buffer.from(hash).toString("base64");
329
+ }
330
+ function base64FromHex(hex) {
331
+ if (!/^[0-9a-f]*$/i.test(hex) || hex.length % 2 !== 0) {
332
+ throw new LocalError(`invalid hex sha256: ${hex}`, CONTEXT);
110
333
  }
334
+ return Buffer.from(hex, "hex").toString("base64");
335
+ }
336
+ const CONTENT_TYPES = {
337
+ html: "text/html; charset=utf-8",
338
+ htm: "text/html; charset=utf-8",
339
+ css: "text/css; charset=utf-8",
340
+ js: "application/javascript; charset=utf-8",
341
+ mjs: "application/javascript; charset=utf-8",
342
+ json: "application/json; charset=utf-8",
343
+ svg: "image/svg+xml",
344
+ png: "image/png",
345
+ jpg: "image/jpeg",
346
+ jpeg: "image/jpeg",
347
+ gif: "image/gif",
348
+ webp: "image/webp",
349
+ ico: "image/x-icon",
350
+ woff: "font/woff",
351
+ woff2: "font/woff2",
352
+ ttf: "font/ttf",
353
+ eot: "application/vnd.ms-fontobject",
354
+ otf: "font/otf",
355
+ txt: "text/plain; charset=utf-8",
356
+ md: "text/markdown; charset=utf-8",
357
+ xml: "application/xml",
358
+ pdf: "application/pdf",
359
+ wasm: "application/wasm",
360
+ map: "application/json; charset=utf-8",
361
+ };
362
+ function guessContentType(path) {
363
+ const dot = path.lastIndexOf(".");
364
+ if (dot === -1)
365
+ return "application/octet-stream";
366
+ const ext = path.slice(dot + 1).toLowerCase();
367
+ return CONTENT_TYPES[ext] ?? "application/octet-stream";
368
+ }
369
+ // ─── Test-only exports ───────────────────────────────────────────────────────
370
+ // Kept exported so the unit tests can drive the walk + hash logic without
371
+ // having to spin up an HTTP mock for every concern.
372
+ /** @internal — exposed for tests, not part of the public SDK API. */
373
+ export async function _collectFilesForTest(root) {
374
+ return collectFiles(root);
111
375
  }
112
376
  //# sourceMappingURL=sites-node.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sites-node.js","sourceRoot":"","sources":["../../src/node/sites-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;AACtE,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAatC;;;GAGG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC;;;;;;;OAOG;IACH,KAAK,CAAC,SAAS,CAAC,IAAsB;QACpC,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,UAAU,CAClB,aAAa,IAAI,CAAC,GAAG,+BAA+B,EACpD,OAAO,CACR,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YAC/B,KAAK;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAClB,yBAAyB,IAAI,KAAM,GAAa,CAAC,OAAO,EAAE,EAC1D,OAAO,EACP,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,UAAU,CAClB,oBAAoB,IAAI,wCAAwC,EAChE,OAAO,CACR,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,UAAU,CAClB,QAAQ,IAAI,qBAAqB,EACjC,OAAO,CACR,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAe,EAAE,CAAC;IAC3B,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,IAAY,EACZ,OAAe,EACf,GAAe;IAEf,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAClB,yBAAyB,OAAO,KAAM,GAAa,CAAC,OAAO,EAAE,EAC7D,OAAO,EACP,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3B,MAAM,IAAI,UAAU,CAClB,oBAAoB,QAAQ,wCAAwC,EACpE,OAAO,CACR,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,IAAI,KAAK,CAAC;YACV,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,UAAU,CAClB,oBAAoB,QAAQ,KAAM,GAAa,CAAC,OAAO,EAAE,EACzD,OAAO,EACP,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;YACvD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,KAAa;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAC5E,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"sites-node.js","sourceRoot":"","sources":["../../src/node/sites-node.ts"],"names":[],"mappings":"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"}