@netlify/spark-ui 1.31.0-alpha.5 → 1.31.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +3 -4
  2. package/dist/components/preact/DropZone/DropZone.d.ts +23 -97
  3. package/dist/components/preact/DropZone/useDropDeploy.js +110 -105
  4. package/dist/drop-core/analytics.d.ts +4 -15
  5. package/dist/drop-core/authedApi.d.ts +9 -22
  6. package/dist/drop-core/authedDropClient.d.ts +13 -55
  7. package/dist/drop-core/authedDropClient.js +5 -18
  8. package/dist/drop-core/buildClient.d.ts +19 -53
  9. package/dist/drop-core/buildClient.js +27 -40
  10. package/dist/drop-core/buildStash.d.ts +6 -20
  11. package/dist/drop-core/buildStash.js +36 -24
  12. package/dist/drop-core/client.d.ts +1 -1
  13. package/dist/drop-core/constants.d.ts +1 -21
  14. package/dist/drop-core/deploy.d.ts +5 -17
  15. package/dist/drop-core/detectBuild.d.ts +8 -26
  16. package/dist/drop-core/errors.d.ts +8 -27
  17. package/dist/drop-core/fileTypes.d.ts +4 -14
  18. package/dist/drop-core/readFiles.d.ts +5 -11
  19. package/dist/drop-core/types.d.ts +2 -4
  20. package/dist/drop-core/zip.d.ts +7 -30
  21. package/package.json +1 -1
  22. package/packages/components/preact/DropZone/DropZone.tsx +27 -121
  23. package/packages/components/preact/DropZone/useDropDeploy.test.ts +12 -0
  24. package/packages/components/preact/DropZone/useDropDeploy.ts +39 -60
  25. package/packages/drop-core/README.md +311 -39
  26. package/packages/drop-core/analytics.ts +4 -15
  27. package/packages/drop-core/authedApi.ts +9 -22
  28. package/packages/drop-core/authedDropClient.ts +13 -55
  29. package/packages/drop-core/buildClient.test.ts +19 -4
  30. package/packages/drop-core/buildClient.ts +21 -63
  31. package/packages/drop-core/buildStash.test.ts +19 -0
  32. package/packages/drop-core/buildStash.ts +38 -29
  33. package/packages/drop-core/client.ts +6 -10
  34. package/packages/drop-core/constants.ts +8 -22
  35. package/packages/drop-core/deploy.ts +6 -22
  36. package/packages/drop-core/detectBuild.ts +15 -50
  37. package/packages/drop-core/errors.test.ts +3 -1
  38. package/packages/drop-core/errors.ts +9 -30
  39. package/packages/drop-core/fileTypes.ts +5 -19
  40. package/packages/drop-core/readFiles.ts +5 -12
  41. package/packages/drop-core/types.ts +2 -4
  42. package/packages/drop-core/zip.ts +7 -30
@@ -1,27 +1,13 @@
1
1
  import { DropClient } from './client';
2
2
  import { Digest, DropResponse } from './types';
3
3
  export interface AuthenticatedDropClientConfig {
4
- /**
5
- * Base for JSON API calls, routed through the consumer's access-control
6
- * rewrite so the session cookie authenticates them. On netlify.com that
7
- * rewrite is `/access-control/*` → the app's access-control function, which
8
- * makes `/access-control/bb-api/api/v1` the API root (the default here).
9
- */
4
+ /** Root for session-cookie JSON calls, via the consumer's access-control rewrite. */
10
5
  proxyBase?: string;
11
- /**
12
- * Direct API base for the file uploads. The proxy above runs in a Lambda with
13
- * a ~6MB request body limit, so file PUTs go straight to `api.netlify.com`
14
- * (whose `/api/*` CORS policy allows any origin).
15
- */
6
+ /** Direct base for file PUTs — the proxy's ~6MB Lambda body limit can't carry uploads. */
16
7
  apiBase?: string;
17
8
  /**
18
- * Mint a bearer token for those direct uploads. On netlify.com this is
19
- * `GET /access-control/generate-access-control-token`, whose
20
- * `accessControlToken` the API decrypts back into the user's access token.
21
- * Resolving `null` means "no session" and surfaces as `NotAuthenticatedError`.
22
- *
23
- * Called repeatedly, not once: the token issued to a non-app origin lives
24
- * only 30 seconds, which a multi-file upload routinely outlasts.
9
+ * Mint the upload bearer; `null` means "no session". Called repeatedly —
10
+ * non-app origins get 30-second tokens, which an upload queue outlasts.
25
11
  */
26
12
  getUploadToken: () => Promise<string | null>;
27
13
  /** Create the site in this account. Omit to use the user's default account. */
@@ -32,25 +18,10 @@ export interface AuthenticatedDropClientConfig {
32
18
  timeoutMs?: number;
33
19
  }
34
20
  /**
35
- * Authenticated drop client for logged-in visitors. Unlike `AnonymousDropClient`
36
- * — which creates an account-less site the visitor must later claim — this
37
- * deploys straight into the visitor's account via the sites/deploys API,
38
- * mirroring what app.netlify.com does for a logged-in drag-and-drop deploy.
39
- * There is no claim step, so the caller should hand off to the site's dashboard
40
- * page rather than the claim page.
41
- *
42
- * Requests split across two hosts by necessity. JSON calls (create the site,
43
- * create the deploy, poll it) go through the consumer's access-control proxy on
44
- * the session cookie — no token involved, and nothing that can expire mid-deploy.
45
- * File uploads can't: the proxy caps bodies at ~6MB, so they go direct to
46
- * `api.netlify.com` with a bearer, re-minted as it ages (see `TOKEN_REUSE_MS`).
47
- *
48
- * Error-mapping invariant: only pre-mutation failures — a null upload token at
49
- * the gate, or a 401/403 creating the site — throw `NotAuthenticatedError`,
50
- * which is the signal the drop flow may safely retry anonymously. Once the site
51
- * exists, any auth failure throws a plain error instead: falling back at that
52
- * point would strand an empty site in the account *and* deploy a duplicate
53
- * anonymously.
21
+ * Deploys a static drop straight into a logged-in visitor's account — no claim
22
+ * step. JSON rides the cookie proxy; file PUTs go direct on a re-minted bearer.
23
+ * Invariant: NotAuthenticatedError only pre-mutation — after the site exists, a
24
+ * fallback would strand an empty site and deploy a duplicate.
54
25
  */
55
26
  export declare class AuthenticatedDropClient implements DropClient {
56
27
  private proxyBase;
@@ -67,27 +38,14 @@ export declare class AuthenticatedDropClient implements DropClient {
67
38
  */
68
39
  getToken(): Promise<string>;
69
40
  /**
70
- * A token young enough to authorize an upload, minting a new one once the
71
- * cached one nears its 30s expiry. A mint failure here is a plain `Error`,
72
- * never `NotAuthenticatedError` — the site exists by now (class doc).
41
+ * A token younger than its 30s expiry. Mint failures here are plain Errors,
42
+ * never NotAuthenticatedError — the site exists by now (class doc).
73
43
  */
74
44
  private freshToken;
75
- /**
76
- * Two proxied calls: create the site in the visitor's account, then the
77
- * deploy on it. Mapped into the anonymous `DropResponse` shape so
78
- * `deployFiles` runs unchanged over either client.
79
- */
45
+ /** Site + deploy via the proxy, mapped into `DropResponse` so `deployFiles` runs unchanged. */
80
46
  createDeploy(files: Digest, _token: string): Promise<DropResponse>;
81
- /**
82
- * Direct to `api.netlify.com` on a freshly-aged bearer — the threaded `token`
83
- * is ignored because an upload queue easily outlives the 30 seconds it was
84
- * minted with. PUT (not POST) so paths with "&" etc. work.
85
- */
47
+ /** Direct on a fresh bearer (the threaded one has expired). PUT so paths with "&" work. */
86
48
  uploadFile(deployId: string, path: string, content: ArrayBuffer, _token: string): Promise<void>;
87
- /**
88
- * Poll the deploy through the proxy until it settles: resolve on `ready`,
89
- * throw on `error`/`rejected`, throw on timeout. On the session cookie rather
90
- * than a bearer, so a deploy taking minutes can't outlive its own credentials.
91
- */
49
+ /** Polls on the session cookie, so a slow deploy can't outlive its own credentials. */
92
50
  waitUntilReady(deploy: DropResponse, _token: string): Promise<void>;
93
51
  }
@@ -33,9 +33,8 @@ class B {
33
33
  return this.cachedToken = { value: e, mintedAt: Date.now() }, e;
34
34
  }
35
35
  /**
36
- * A token young enough to authorize an upload, minting a new one once the
37
- * cached one nears its 30s expiry. A mint failure here is a plain `Error`,
38
- * never `NotAuthenticatedError` — the site exists by now (class doc).
36
+ * A token younger than its 30s expiry. Mint failures here are plain Errors,
37
+ * never NotAuthenticatedError — the site exists by now (class doc).
39
38
  */
40
39
  async freshToken() {
41
40
  const e = Date.now();
@@ -45,11 +44,7 @@ class B {
45
44
  if (!t) throw new Error("could not renew the upload token");
46
45
  return this.cachedToken = { value: t, mintedAt: e }, t;
47
46
  }
48
- /**
49
- * Two proxied calls: create the site in the visitor's account, then the
50
- * deploy on it. Mapped into the anonymous `DropResponse` shape so
51
- * `deployFiles` runs unchanged over either client.
52
- */
47
+ /** Site + deploy via the proxy, mapped into `DropResponse` so `deployFiles` runs unchanged. */
53
48
  async createDeploy(e, t) {
54
49
  const o = await p(this.proxyBase, this.accountSlug), s = await fetch(`${this.proxyBase}/sites/${o.id}/deploys`, {
55
50
  method: "POST",
@@ -66,11 +61,7 @@ class B {
66
61
  required: n.required ?? []
67
62
  };
68
63
  }
69
- /**
70
- * Direct to `api.netlify.com` on a freshly-aged bearer — the threaded `token`
71
- * is ignored because an upload queue easily outlives the 30 seconds it was
72
- * minted with. PUT (not POST) so paths with "&" etc. work.
73
- */
64
+ /** Direct on a fresh bearer (the threaded one has expired). PUT so paths with "&" work. */
74
65
  async uploadFile(e, t, o, s) {
75
66
  const n = await this.freshToken(), r = await fetch(`${this.apiBase}/deploys/${e}/files${t}`, {
76
67
  method: "PUT",
@@ -79,11 +70,7 @@ class B {
79
70
  });
80
71
  if (!r.ok) throw l(`upload ${t}`, r.status);
81
72
  }
82
- /**
83
- * Poll the deploy through the proxy until it settles: resolve on `ready`,
84
- * throw on `error`/`rejected`, throw on timeout. On the session cookie rather
85
- * than a bearer, so a deploy taking minutes can't outlive its own credentials.
86
- */
73
+ /** Polls on the session cookie, so a slow deploy can't outlive its own credentials. */
87
74
  async waitUntilReady(e, t) {
88
75
  const o = await h({
89
76
  proxyBase: this.proxyBase,
@@ -2,7 +2,6 @@ import { BuildSite } from './types';
2
2
  export type { BuildSite } from './types';
3
3
  /** The subset of a Netlify Build returned by `POST /sites/{id}/builds`. */
4
4
  export interface BuildResponse {
5
- /** build id */
6
5
  id: string;
7
6
  /** the deploy the build produces — present as soon as the build is enqueued */
8
7
  deploy_id?: string;
@@ -13,28 +12,14 @@ export interface BuildClient {
13
12
  waitUntilBuildReady(build: BuildResponse): Promise<void>;
14
13
  }
15
14
  export interface AuthenticatedBuildClientConfig {
16
- /**
17
- * Base for JSON API calls, routed through the consumer's access-control
18
- * rewrite so the session cookie authenticates them. On netlify.com that
19
- * rewrite is `/access-control/*` → the app's access-control function, which
20
- * makes `/access-control/bb-api/api/v1` the API root (the default here).
21
- */
15
+ /** Root for session-cookie JSON calls, via the consumer's access-control rewrite. */
22
16
  proxyBase?: string;
23
- /**
24
- * Direct API base for the zip upload. The proxy above runs in a Lambda with a
25
- * ~6MB request body limit, so the multipart POST has to go straight to
26
- * `api.netlify.com` (whose `/api/*` CORS policy allows any origin).
27
- */
17
+ /** Direct base for the zip upload — the proxy's ~6MB Lambda body limit can't carry it. */
28
18
  apiBase?: string;
29
19
  /**
30
- * Mint a bearer token for that direct upload. On netlify.com this is
31
- * `GET /access-control/generate-access-control-token`, whose
32
- * `accessControlToken` the API decrypts back into the user's access token.
33
- * Resolving `null` means "no session" and surfaces as `NotAuthenticatedError`.
34
- *
35
- * Note the token issued to a non-app origin lives only 30 seconds. It is
36
- * minted immediately before the upload, but a zip that takes longer than that
37
- * to transfer will still be rejected mid-flight — see `createBuildFromZip`.
20
+ * Mint the upload bearer; `null` means "no session". Non-app origins get
21
+ * 30-second tokens — a zip slower than that fails mid-flight (see
22
+ * `createBuildFromZip`).
38
23
  */
39
24
  getUploadToken: () => Promise<string | null>;
40
25
  /** Create the site in this account. Omit to use the user's default account. */
@@ -47,16 +32,9 @@ export interface AuthenticatedBuildClientConfig {
47
32
  timeoutMs?: number;
48
33
  }
49
34
  /**
50
- * Authenticated drop client for projects that need a build. Unlike
51
- * `AnonymousDropClient` — which creates an account-less static site and can only
52
- * upload pre-built files — this creates a real site in the user's account and
53
- * hands buildbot a source archive to build, mirroring what app.netlify.com does
54
- * for a drag-and-drop build.
55
- *
56
- * Requests split across two hosts by necessity: JSON calls go through the
57
- * consumer's access-control proxy (cookie session, no token handling in the
58
- * browser), while the zip upload goes direct to `api.netlify.com` with a
59
- * short-lived bearer token because the proxy can't carry a body that large.
35
+ * Builds a drop in a logged-in visitor's account: create the site, hand
36
+ * buildbot the source zip. JSON rides the cookie proxy; the zip goes direct
37
+ * with a bearer (the proxy can't carry it).
60
38
  */
61
39
  export declare class AuthenticatedBuildClient implements BuildClient {
62
40
  private proxyBase;
@@ -67,35 +45,23 @@ export declare class AuthenticatedBuildClient implements BuildClient {
67
45
  private pollIntervalMs;
68
46
  private timeoutMs;
69
47
  constructor({ proxyBase, apiBase, getUploadToken, accountSlug, title, pollIntervalMs, timeoutMs, }: AuthenticatedBuildClientConfig);
70
- /**
71
- * Create the site the build deploys into. A logged-out visitor fails here,
72
- * before anything is created — see `createSiteInAccount` for the error
73
- * mapping that makes the signup fallback safe.
74
- */
48
+ /** A logged-out visitor fails here, before anything is created (see `createSiteInAccount`). */
75
49
  createSite(): Promise<BuildSite>;
76
50
  /**
77
- * Upload the source archive and enqueue a build. Direct to `api.netlify.com`
78
- * (see the note on `apiBase`), authorized by a freshly minted bearer token.
79
- * `Content-Type` is left unset on purpose so the browser adds the multipart
80
- * boundary itself.
51
+ * One multipart request on a fresh 30s bearer — a slower zip fails mid-flight
52
+ * (the app origin gets 300s; raising ours is a platform-side change).
53
+ * Content-Type stays unset so the browser adds the multipart boundary.
81
54
  *
82
- * Known ceiling: this is one request on a 30s token, and re-minting can't
83
- * help once it is in flight. A zip that takes longer than that to upload will
84
- * fail — the app origin gets 300s for exactly this reason, and lifting the
85
- * limit for other origins is a platform-side change.
55
+ * Never NotAuthenticatedError, including for a 401: the site exists by the
56
+ * time this runs, so signalling "no session" would strand it and re-stash
57
+ * the drop for the next visit to strand another. `createSite` is the auth
58
+ * gate for this path.
86
59
  */
87
60
  createBuildFromZip(siteId: string, zip: File): Promise<BuildResponse>;
88
61
  /**
89
- * Poll the build's deploy until it settles, mirroring
90
- * `AnonymousDropClient.waitUntilReady` — resolve on `ready`, throw on
91
- * `error`/`rejected`, throw on timeout. A build that hasn't been given a
92
- * deploy id yet has nothing pollable, so it resolves immediately and the
93
- * caller falls back to the site page.
94
- *
95
- * Optional in the drop flow, and off by default: a build takes minutes, so
96
- * the useful hand-off is usually the project page rather than a spinner.
97
- * Polling rides the session cookie, so it cannot outlive its credentials the
98
- * way a 30s bearer would.
62
+ * Optional (a build takes minutes; the useful hand-off is the project page).
63
+ * Polls on the cookie, so it can't outlive its credentials; no deploy id yet
64
+ * means nothing pollable, so it resolves immediately.
99
65
  */
100
66
  waitUntilBuildReady(build: BuildResponse): Promise<void>;
101
67
  }
@@ -1,16 +1,16 @@
1
- var u = Object.defineProperty;
2
- var d = (r, t, e) => t in r ? u(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
- var o = (r, t, e) => d(r, typeof t != "symbol" ? t + "" : t, e);
1
+ var d = Object.defineProperty;
2
+ var u = (i, t, e) => t in i ? d(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
3
+ var o = (i, t, e) => u(i, typeof t != "symbol" ? t + "" : t, e);
4
4
  import { createSiteInAccount as c, pollDeployUntilSettled as h } from "./authedApi.js";
5
5
  import { DEFAULT_PROXY_BASE as m, DEFAULT_API_BASE as B, DEFAULT_POLL_INTERVAL_MS as y, DEFAULT_TIMEOUT_MS as w } from "./constants.js";
6
- import { NotAuthenticatedError as l, BuildCreateError as f, BuildFailedError as A, BuildTimeoutError as E } from "./errors.js";
7
- class U {
6
+ import { BuildCreateError as l, BuildFailedError as M, BuildTimeoutError as T } from "./errors.js";
7
+ class _ {
8
8
  constructor({
9
9
  proxyBase: t = m,
10
10
  apiBase: e = B,
11
11
  getUploadToken: a,
12
- accountSlug: s,
13
- title: i = "Build from drop deployment",
12
+ accountSlug: r,
13
+ title: s = "Build from drop deployment",
14
14
  pollIntervalMs: n = y,
15
15
  timeoutMs: p = w
16
16
  }) {
@@ -21,52 +21,39 @@ class U {
21
21
  o(this, "title");
22
22
  o(this, "pollIntervalMs");
23
23
  o(this, "timeoutMs");
24
- this.proxyBase = t, this.apiBase = e, this.getUploadToken = a, this.accountSlug = s, this.title = i, this.pollIntervalMs = n, this.timeoutMs = p;
24
+ this.proxyBase = t, this.apiBase = e, this.getUploadToken = a, this.accountSlug = r, this.title = s, this.pollIntervalMs = n, this.timeoutMs = p;
25
25
  }
26
- /**
27
- * Create the site the build deploys into. A logged-out visitor fails here,
28
- * before anything is created — see `createSiteInAccount` for the error
29
- * mapping that makes the signup fallback safe.
30
- */
26
+ /** A logged-out visitor fails here, before anything is created (see `createSiteInAccount`). */
31
27
  async createSite() {
32
28
  return c(this.proxyBase, this.accountSlug);
33
29
  }
34
30
  /**
35
- * Upload the source archive and enqueue a build. Direct to `api.netlify.com`
36
- * (see the note on `apiBase`), authorized by a freshly minted bearer token.
37
- * `Content-Type` is left unset on purpose so the browser adds the multipart
38
- * boundary itself.
31
+ * One multipart request on a fresh 30s bearer — a slower zip fails mid-flight
32
+ * (the app origin gets 300s; raising ours is a platform-side change).
33
+ * Content-Type stays unset so the browser adds the multipart boundary.
39
34
  *
40
- * Known ceiling: this is one request on a 30s token, and re-minting can't
41
- * help once it is in flight. A zip that takes longer than that to upload will
42
- * fail — the app origin gets 300s for exactly this reason, and lifting the
43
- * limit for other origins is a platform-side change.
35
+ * Never NotAuthenticatedError, including for a 401: the site exists by the
36
+ * time this runs, so signalling "no session" would strand it and re-stash
37
+ * the drop for the next visit to strand another. `createSite` is the auth
38
+ * gate for this path.
44
39
  */
45
40
  async createBuildFromZip(t, e) {
46
41
  const a = await this.getUploadToken();
47
42
  if (!a) throw new l();
48
- const s = new FormData();
49
- s.append("zip", e), s.append("title", this.title);
50
- const i = await fetch(`${this.apiBase}/sites/${t}/builds`, {
43
+ const r = new FormData();
44
+ r.append("zip", e), r.append("title", this.title);
45
+ const s = await fetch(`${this.apiBase}/sites/${t}/builds`, {
51
46
  method: "POST",
52
47
  headers: { Authorization: `Bearer ${a}` },
53
- body: s
48
+ body: r
54
49
  });
55
- if (i.status === 401 || i.status === 403) throw new l();
56
- if (!i.ok) throw new f(i.status);
57
- return i.json();
50
+ if (!s.ok) throw new l(s.status);
51
+ return s.json();
58
52
  }
59
53
  /**
60
- * Poll the build's deploy until it settles, mirroring
61
- * `AnonymousDropClient.waitUntilReady` — resolve on `ready`, throw on
62
- * `error`/`rejected`, throw on timeout. A build that hasn't been given a
63
- * deploy id yet has nothing pollable, so it resolves immediately and the
64
- * caller falls back to the site page.
65
- *
66
- * Optional in the drop flow, and off by default: a build takes minutes, so
67
- * the useful hand-off is usually the project page rather than a spinner.
68
- * Polling rides the session cookie, so it cannot outlive its credentials the
69
- * way a 30s bearer would.
54
+ * Optional (a build takes minutes; the useful hand-off is the project page).
55
+ * Polls on the cookie, so it can't outlive its credentials; no deploy id yet
56
+ * means nothing pollable, so it resolves immediately.
70
57
  */
71
58
  async waitUntilBuildReady(t) {
72
59
  if (!t.deploy_id) return;
@@ -77,9 +64,9 @@ class U {
77
64
  timeoutMs: this.timeoutMs
78
65
  });
79
66
  if (e.outcome !== "ready")
80
- throw e.outcome === "failed" ? new A(e.errorMessage) : new E();
67
+ throw e.outcome === "failed" ? new M(e.errorMessage) : new T();
81
68
  }
82
69
  }
83
70
  export {
84
- U as AuthenticatedBuildClient
71
+ _ as AuthenticatedBuildClient
85
72
  };
@@ -1,20 +1,10 @@
1
1
  import { InferredBuildSettings } from './detectBuild';
2
2
  /**
3
- * Carry a build-required drop across the signup round-trip.
4
- *
5
- * A logged-out visitor's drop can't be built (builds have no anonymous
6
- * equivalent), so before handing them to signup the zone stashes the source
7
- * archive here — IndexedDB, because the zip is far beyond localStorage's
8
- * budget. When they return to this origin authenticated, the zone deploys the
9
- * stash automatically instead of asking them to drop again.
10
- *
11
- * Storage is per-origin: a stash written on www.netlify.com can only ever be
12
- * resumed on www.netlify.com. That is the design, not a limitation — it is
13
- * exactly why the hand-off page must bring the visitor back here.
14
- *
15
- * Every operation is failure-swallowing: the stash is an enhancement, and a
16
- * browser without IndexedDB (or with a full quota) degrades to the plain
17
- * signup redirect the zone always supported.
3
+ * Carries a build drop across the signup round-trip: stash the zip in
4
+ * IndexedDB before the hand-off, deploy it when the visitor returns
5
+ * authenticated. Storage is per-origin by design — the hand-off must bring
6
+ * them back here. Every operation swallows failures: no storage means the
7
+ * plain signup redirect the zone always supported.
18
8
  */
19
9
  export interface DropBuildStash {
20
10
  /** The `source.zip` exactly as it would have been uploaded. */
@@ -23,11 +13,7 @@ export interface DropBuildStash {
23
13
  buildSettings: InferredBuildSettings | null;
24
14
  createdAt: number;
25
15
  }
26
- /**
27
- * Stash a drop's source archive for an automatic deploy after signup. Returns
28
- * whether it was actually stored — callers use that only to phrase the
29
- * hand-off, never to block it.
30
- */
16
+ /** Stash the archive for auto-deploy after signup. The result phrases the hand-off, never blocks it. */
31
17
  export declare function saveDropStash(zipFile: File, buildSettings?: InferredBuildSettings | null): Promise<boolean>;
32
18
  /** The stashed drop, or null when there is none, it expired, or storage is unavailable. */
33
19
  export declare function loadDropStash(): Promise<DropBuildStash | null>;
@@ -1,17 +1,20 @@
1
- const f = "netlify-drop";
1
+ const l = "netlify-drop";
2
2
  const s = "build-stash", c = "latest", i = "dropBuildStash";
3
- const S = (e) => Promise.race([
3
+ const f = (e) => Promise.race([
4
4
  e,
5
5
  new Promise((r, t) => {
6
6
  setTimeout(() => t(new Error("IndexedDB operation timed out")), 3e3);
7
7
  })
8
- ]), d = () => new Promise((e, r) => {
9
- const t = indexedDB.open(f, 1);
8
+ ]), S = () => new Promise((e, r) => {
9
+ const t = indexedDB.deleteDatabase(l);
10
+ t.onsuccess = () => e(), t.onblocked = () => e(), t.onerror = () => r(t.error);
11
+ }), p = () => new Promise((e, r) => {
12
+ const t = indexedDB.open(l, 1);
10
13
  t.onupgradeneeded = () => {
11
14
  t.result.createObjectStore(s);
12
15
  }, t.onsuccess = () => e(t.result), t.onerror = () => r(t.error);
13
- }), u = (e, r) => S(
14
- d().then(
16
+ }), u = (e, r) => f(
17
+ p().then(
15
18
  (t) => new Promise((o, a) => {
16
19
  const n = r(t.transaction(s, e).objectStore(s));
17
20
  n.onsuccess = () => {
@@ -21,33 +24,42 @@ const S = (e) => Promise.race([
21
24
  };
22
25
  })
23
26
  )
24
- ), p = async (e) => {
27
+ ), y = async (e) => {
25
28
  const { quota: r, usage: t } = await navigator.storage.estimate();
26
29
  return r !== void 0 && t !== void 0 && r - t > e * 2;
27
30
  }, T = () => {
28
- localStorage.setItem(i, JSON.stringify({ expiresAt: Date.now() + 864e5 }));
29
- }, l = () => {
31
+ localStorage.setItem(
32
+ i,
33
+ JSON.stringify({ value: "true", expiry: Date.now() + 864e5 })
34
+ );
35
+ }, d = () => {
30
36
  localStorage.removeItem(i);
31
37
  };
32
- async function A(e, r = null) {
38
+ async function O(e, r = null) {
33
39
  var t;
34
40
  try {
35
- if (typeof indexedDB > "u" || !((t = navigator.storage) != null && t.estimate) || e.size > 209715200 || !await p(e.size))
41
+ if (typeof indexedDB > "u" || !((t = navigator.storage) != null && t.estimate) || e.size > 209715200 || !await y(e.size))
36
42
  return !1;
37
- const o = { zipFile: e, buildSettings: r, createdAt: Date.now() };
43
+ const o = {
44
+ // Round-trip through JSON so an injected detector's richer object can't
45
+ // fail the structured clone the put() below performs.
46
+ buildSettings: r && JSON.parse(JSON.stringify(r)),
47
+ zipFile: e,
48
+ createdAt: Date.now()
49
+ };
38
50
  return await u("readwrite", (a) => a.put(o, c)), T(), !0;
39
51
  } catch {
40
52
  return !1;
41
53
  }
42
54
  }
43
- async function D() {
55
+ async function w() {
44
56
  try {
45
57
  if (typeof indexedDB > "u") return null;
46
58
  const e = await u(
47
59
  "readonly",
48
60
  (t) => t.get(c)
49
61
  ), r = e && _(e.zipFile);
50
- return !e || !r ? null : Date.now() - e.createdAt > 864e5 ? (await y(), null) : { ...e, zipFile: r };
62
+ return !e || !r ? null : Date.now() - e.createdAt > 864e5 ? (await D(), null) : { ...e, zipFile: r };
51
63
  } catch {
52
64
  return null;
53
65
  }
@@ -55,30 +67,30 @@ async function D() {
55
67
  function _(e) {
56
68
  return e instanceof File && e.name ? e : e instanceof Blob || e && typeof e.arrayBuffer == "function" ? new File([e], "source.zip", { type: "application/zip" }) : null;
57
69
  }
58
- async function y() {
70
+ async function D() {
59
71
  try {
60
- l();
72
+ d();
61
73
  } catch {
62
74
  }
63
75
  try {
64
76
  if (typeof indexedDB > "u") return;
65
- await u("readwrite", (e) => e.delete(c));
77
+ await u("readwrite", (e) => e.delete(c)), await f(S());
66
78
  } catch {
67
79
  }
68
80
  }
69
- function O() {
81
+ function E() {
70
82
  try {
71
83
  const e = localStorage.getItem(i);
72
84
  if (!e) return !1;
73
- const { expiresAt: r } = JSON.parse(e);
74
- return typeof r != "number" || Date.now() > r ? (l(), !1) : !0;
85
+ const { value: r, expiry: t } = JSON.parse(e);
86
+ return r !== "true" || typeof t != "number" || Date.now() > t ? (d(), !1) : !0;
75
87
  } catch {
76
88
  return !1;
77
89
  }
78
90
  }
79
91
  export {
80
- y as clearDropStash,
81
- O as hasDropStashMarker,
82
- D as loadDropStash,
83
- A as saveDropStash
92
+ D as clearDropStash,
93
+ E as hasDropStashMarker,
94
+ w as loadDropStash,
95
+ O as saveDropStash
84
96
  };
@@ -11,7 +11,7 @@ export interface DropClient {
11
11
  }
12
12
  /**
13
13
  * Unauthenticated drop client. Authorized server-side by the Referer header,
14
- * so it only works from an allowlisted origin (see backend checklist).
14
+ * so it only works from an allowlisted origin.
15
15
  */
16
16
  export declare class AnonymousDropClient implements DropClient {
17
17
  private apiBase;
@@ -1,29 +1,9 @@
1
- /**
2
- * The tunables shared across drop-core, one place and one comment each.
3
- * Module-specific constants (e.g. buildStash's storage caps) stay with their
4
- * module; anything two modules read belongs here.
5
- */
6
- /**
7
- * Root for session-cookie JSON calls, via the consumer's access-control
8
- * rewrite. On netlify.com, `/access-control/*` targets the app's
9
- * access-control function; the paths an origin may call are allowlisted
10
- * server-side.
11
- */
1
+ /** Tunables shared across drop-core. Module-specific constants stay with their module. */
12
2
  export declare const DEFAULT_PROXY_BASE = "/access-control/bb-api/api/v1";
13
3
  /** Direct api.netlify.com root for the uploads the proxy's ~6MB Lambda body limit can't carry. */
14
4
  export declare const DEFAULT_API_BASE = "https://api.netlify.com/api/v1";
15
5
  export declare const DEFAULT_POLL_INTERVAL_MS = 1000;
16
6
  export declare const DEFAULT_TIMEOUT_MS: number;
17
7
  export declare const MAX_CONCURRENT_UPLOADS = 5;
18
- /**
19
- * How long a minted upload token is reused before minting another: 20 seconds.
20
- * The server issues non-app origins a 30s token; the gap leaves margin for the
21
- * request the token authorizes to finish.
22
- */
23
8
  export declare const TOKEN_REUSE_MS: number;
24
- /**
25
- * Waits between attempts of a failed file upload, as data so the whole curve
26
- * can be overridden (the tus-js-client `retryDelays` idiom). Three retries,
27
- * linear backoff; 400/422 responses never retry — see `uploadWithRetry`.
28
- */
29
9
  export declare const UPLOAD_RETRY_DELAYS_MS: readonly number[];
@@ -10,7 +10,6 @@ export interface DeployOptions {
10
10
  }
11
11
  export declare function deployFiles(client: DropClient, files: ProcessedFile[], opts?: DeployOptions): Promise<DeployResult>;
12
12
  export interface BuildDeployResult {
13
- /** The site created for this build. */
14
13
  site: BuildSite;
15
14
  /** The enqueued build; `deploy_id` is what the app's deploy page keys off. */
16
15
  build: BuildResponse;
@@ -21,26 +20,15 @@ export interface BuildDeployResult {
21
20
  }
22
21
  export interface BuildDeployOptions extends DeployOptions {
23
22
  /**
24
- * Poll until the build finishes instead of returning as soon as it's enqueued.
25
- * Off by default: a build takes minutes, so the useful hand-off is usually the
26
- * app's deploy page with its live logs, not a spinner. Turn it on when the
27
- * caller needs to know the outcome — only then can a `BuildFailedError` reach
28
- * it with the build's own error message.
23
+ * Wait for the build instead of returning at enqueue. Off by default (builds
24
+ * take minutes); on is the only way a BuildFailedError reaches the caller.
29
25
  */
30
26
  waitForBuild?: boolean;
31
27
  }
32
28
  /**
33
- * The build counterpart to `deployFiles`: zip the drop, create a site in the
34
- * user's account, and hand the archive to buildbot. Requires an authenticated
35
- * client — builds have no anonymous equivalent.
36
- *
37
- * Site creation deliberately happens *after* zipping, so a zip that fails
38
- * doesn't leave an empty site behind in the user's account.
29
+ * Zip the drop, create a site, hand buildbot the archive. Zipping comes first
30
+ * on purpose: a failed zip must not leave an empty site behind.
39
31
  */
40
32
  export declare function deployBuild(client: BuildClient, files: ProcessedFile[], opts?: BuildDeployOptions): Promise<BuildDeployResult>;
41
- /**
42
- * Deploy an archive that was already zipped — the resume path for a drop
43
- * stashed across the signup round-trip (see `buildStash`), where `source.zip`
44
- * and its synthesised settings were produced before the visitor left.
45
- */
33
+ /** Deploy an already-zipped archive — the resume path for a stashed drop (see `buildStash`). */
46
34
  export declare function deployZippedBuild(client: BuildClient, { file: zip, buildSettings }: ZipResult, opts?: BuildDeployOptions): Promise<BuildDeployResult>;