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

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 (100) hide show
  1. package/dist/components/index.d.ts +2 -2
  2. package/dist/components/preact/DropZone/DropZone.d.ts +27 -9
  3. package/dist/components/preact/DropZone/DropZone.js +59 -54
  4. package/dist/components/preact/DropZone/DropZone.test.d.ts +1 -0
  5. package/dist/components/preact/DropZone/index.d.ts +3 -3
  6. package/dist/components/preact/DropZone/index.js +29 -27
  7. package/dist/components/preact/DropZone/useDropDeploy.d.ts +9 -6
  8. package/dist/components/preact/DropZone/useDropDeploy.js +182 -131
  9. package/dist/components/preact/DropZone/useDropDeploy.test.d.ts +1 -0
  10. package/dist/components/preact/index.d.ts +2 -2
  11. package/dist/components/preact/index.js +24 -18
  12. package/dist/drop-core/analytics.d.ts +107 -0
  13. package/dist/drop-core/analytics.js +21 -0
  14. package/dist/drop-core/analytics.test.d.ts +1 -0
  15. package/dist/drop-core/authedApi.d.ts +45 -0
  16. package/dist/drop-core/authedApi.js +30 -0
  17. package/dist/drop-core/authedApi.test.d.ts +1 -0
  18. package/dist/{components/preact/DropZone/drop-core → drop-core}/authedDropClient.d.ts +4 -11
  19. package/dist/drop-core/authedDropClient.js +100 -0
  20. package/dist/drop-core/authedDropClient.test.d.ts +1 -0
  21. package/dist/{components/preact/DropZone/drop-core → drop-core}/buildClient.d.ts +5 -13
  22. package/dist/drop-core/buildClient.js +85 -0
  23. package/dist/drop-core/buildClient.test.d.ts +1 -0
  24. package/dist/drop-core/buildStash.d.ts +37 -0
  25. package/dist/drop-core/buildStash.js +84 -0
  26. package/dist/drop-core/buildStash.test.d.ts +0 -0
  27. package/dist/{components/preact/DropZone/drop-core → drop-core}/client.d.ts +3 -1
  28. package/dist/drop-core/client.js +47 -0
  29. package/dist/drop-core/constants.d.ts +29 -0
  30. package/dist/drop-core/constants.js +10 -0
  31. package/dist/{components/preact/DropZone/drop-core → drop-core}/deploy.d.ts +9 -0
  32. package/dist/drop-core/deploy.js +66 -0
  33. package/dist/drop-core/deploy.test.d.ts +1 -0
  34. package/dist/drop-core/detectBuild.test.d.ts +1 -0
  35. package/dist/drop-core/digest.test.d.ts +1 -0
  36. package/dist/{components/preact/DropZone/drop-core → drop-core}/errors.d.ts +29 -0
  37. package/dist/drop-core/errors.js +119 -0
  38. package/dist/drop-core/errors.test.d.ts +1 -0
  39. package/dist/drop-core/fileTypes.test.d.ts +1 -0
  40. package/dist/drop-core/index.d.ts +25 -0
  41. package/dist/drop-core/index.js +47 -0
  42. package/dist/{components/preact/DropZone/drop-core → drop-core}/readFiles.js +2 -2
  43. package/dist/drop-core/readFiles.test.d.ts +1 -0
  44. package/dist/{components/preact/DropZone/drop-core → drop-core}/types.d.ts +10 -0
  45. package/dist/{components/preact/DropZone/drop-core → drop-core}/zip.js +1 -1
  46. package/dist/drop-core/zip.test.d.ts +1 -0
  47. package/package.json +4 -1
  48. package/packages/components/index.ts +19 -0
  49. package/packages/components/preact/DropZone/DropZone.test.ts +71 -0
  50. package/packages/components/preact/DropZone/DropZone.tsx +29 -9
  51. package/packages/components/preact/DropZone/index.tsx +14 -3
  52. package/packages/components/preact/DropZone/useDropDeploy.test.ts +376 -0
  53. package/packages/components/preact/DropZone/useDropDeploy.ts +301 -145
  54. package/packages/components/preact/index.ts +19 -2
  55. package/packages/drop-core/README.md +91 -0
  56. package/packages/drop-core/analytics.test.ts +17 -0
  57. package/packages/drop-core/analytics.ts +122 -0
  58. package/packages/drop-core/authedApi.test.ts +100 -0
  59. package/packages/drop-core/authedApi.ts +81 -0
  60. package/packages/drop-core/authedDropClient.test.ts +196 -0
  61. package/packages/{components/preact/DropZone/drop-core → drop-core}/authedDropClient.ts +30 -72
  62. package/packages/drop-core/buildClient.test.ts +112 -0
  63. package/packages/{components/preact/DropZone/drop-core → drop-core}/buildClient.ts +27 -57
  64. package/packages/drop-core/buildStash.test.ts +85 -0
  65. package/packages/drop-core/buildStash.ts +185 -0
  66. package/packages/{components/preact/DropZone/drop-core → drop-core}/client.ts +18 -6
  67. package/packages/drop-core/constants.ts +39 -0
  68. package/packages/drop-core/deploy.test.ts +177 -0
  69. package/packages/{components/preact/DropZone/drop-core → drop-core}/deploy.ts +59 -9
  70. package/packages/drop-core/detectBuild.test.ts +139 -0
  71. package/packages/drop-core/digest.test.ts +24 -0
  72. package/packages/drop-core/errors.test.ts +98 -0
  73. package/packages/{components/preact/DropZone/drop-core → drop-core}/errors.ts +68 -11
  74. package/packages/drop-core/fileTypes.test.ts +37 -0
  75. package/packages/drop-core/index.ts +44 -0
  76. package/packages/drop-core/readFiles.test.ts +124 -0
  77. package/packages/{components/preact/DropZone/drop-core → drop-core}/types.ts +12 -6
  78. package/packages/drop-core/zip.test.ts +92 -0
  79. package/dist/components/preact/DropZone/drop-core/authedDropClient.js +0 -123
  80. package/dist/components/preact/DropZone/drop-core/buildClient.js +0 -99
  81. package/dist/components/preact/DropZone/drop-core/client.js +0 -45
  82. package/dist/components/preact/DropZone/drop-core/deploy.js +0 -48
  83. package/dist/components/preact/DropZone/drop-core/errors.js +0 -82
  84. package/dist/components/preact/DropZone/drop-core/index.d.ts +0 -11
  85. package/dist/components/preact/DropZone/drop-core/index.js +0 -36
  86. package/packages/components/preact/DropZone/drop-core/index.ts +0 -11
  87. /package/dist/{components/preact/DropZone/drop-core → drop-core}/detectBuild.d.ts +0 -0
  88. /package/dist/{components/preact/DropZone/drop-core → drop-core}/detectBuild.js +0 -0
  89. /package/dist/{components/preact/DropZone/drop-core → drop-core}/digest.d.ts +0 -0
  90. /package/dist/{components/preact/DropZone/drop-core → drop-core}/digest.js +0 -0
  91. /package/dist/{components/preact/DropZone/drop-core → drop-core}/fileTypes.d.ts +0 -0
  92. /package/dist/{components/preact/DropZone/drop-core → drop-core}/fileTypes.js +0 -0
  93. /package/dist/{components/preact/DropZone/drop-core → drop-core}/readFiles.d.ts +0 -0
  94. /package/dist/{components/preact/DropZone/drop-core → drop-core}/types.js +0 -0
  95. /package/dist/{components/preact/DropZone/drop-core → drop-core}/zip.d.ts +0 -0
  96. /package/packages/{components/preact/DropZone/drop-core → drop-core}/detectBuild.ts +0 -0
  97. /package/packages/{components/preact/DropZone/drop-core → drop-core}/digest.ts +0 -0
  98. /package/packages/{components/preact/DropZone/drop-core → drop-core}/fileTypes.ts +0 -0
  99. /package/packages/{components/preact/DropZone/drop-core → drop-core}/readFiles.ts +0 -0
  100. /package/packages/{components/preact/DropZone/drop-core → drop-core}/zip.ts +0 -0
@@ -62,21 +62,14 @@ export declare class AuthenticatedDropClient implements DropClient {
62
62
  private cachedToken?;
63
63
  constructor({ proxyBase, apiBase, getUploadToken, accountSlug, pollIntervalMs, timeoutMs, }: AuthenticatedDropClientConfig);
64
64
  /**
65
- * The auth gate. Minting here — before anything is created — is what lets a
66
- * logged-out visitor fail cleanly and fall back to the anonymous flow. The
67
- * returned token is threaded through `deployFiles`, but only the uploads
68
- * actually need one, and they re-mint rather than trusting this to still be
69
- * valid by the time they run.
65
+ * The auth gate: minting here, before anything is created, is what lets a
66
+ * logged-out visitor fail cleanly and fall back to the anonymous flow.
70
67
  */
71
68
  getToken(): Promise<string>;
72
69
  /**
73
70
  * A token young enough to authorize an upload, minting a new one once the
74
- * cached one nears its 30s expiry.
75
- *
76
- * Deliberately NOT `NotAuthenticatedError` when minting fails: by the time
77
- * uploads run the site already exists, and that error would send the drop
78
- * back through the anonymous path — leaving an empty project behind and
79
- * deploying a duplicate. See the class doc.
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).
80
73
  */
81
74
  private freshToken;
82
75
  /**
@@ -0,0 +1,100 @@
1
+ var c = Object.defineProperty;
2
+ var d = (i, e, t) => e in i ? c(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
3
+ var a = (i, e, t) => d(i, typeof e != "symbol" ? e + "" : e, t);
4
+ import { createSiteInAccount as p, pollDeployUntilSettled as h } from "./authedApi.js";
5
+ import { DEFAULT_PROXY_BASE as u, DEFAULT_API_BASE as y, DEFAULT_POLL_INTERVAL_MS as T, DEFAULT_TIMEOUT_MS as m, TOKEN_REUSE_MS as w } from "./constants.js";
6
+ import { apiError as l } from "./client.js";
7
+ import { NotAuthenticatedError as k, DeployFailedError as f, DeployTimeoutError as _ } from "./errors.js";
8
+ class B {
9
+ constructor({
10
+ proxyBase: e = u,
11
+ apiBase: t = y,
12
+ getUploadToken: o,
13
+ accountSlug: s,
14
+ pollIntervalMs: n = T,
15
+ timeoutMs: r = m
16
+ }) {
17
+ a(this, "proxyBase");
18
+ a(this, "apiBase");
19
+ a(this, "getUploadToken");
20
+ a(this, "accountSlug");
21
+ a(this, "pollIntervalMs");
22
+ a(this, "timeoutMs");
23
+ a(this, "cachedToken");
24
+ this.proxyBase = e, this.apiBase = t, this.getUploadToken = o, this.accountSlug = s, this.pollIntervalMs = n, this.timeoutMs = r;
25
+ }
26
+ /**
27
+ * The auth gate: minting here, before anything is created, is what lets a
28
+ * logged-out visitor fail cleanly and fall back to the anonymous flow.
29
+ */
30
+ async getToken() {
31
+ const e = await this.getUploadToken();
32
+ if (!e) throw new k();
33
+ return this.cachedToken = { value: e, mintedAt: Date.now() }, e;
34
+ }
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).
39
+ */
40
+ async freshToken() {
41
+ const e = Date.now();
42
+ if (this.cachedToken && e - this.cachedToken.mintedAt < w)
43
+ return this.cachedToken.value;
44
+ const t = await this.getUploadToken();
45
+ if (!t) throw new Error("could not renew the upload token");
46
+ return this.cachedToken = { value: t, mintedAt: e }, t;
47
+ }
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
+ */
53
+ async createDeploy(e, t) {
54
+ const o = await p(this.proxyBase, this.accountSlug), s = await fetch(`${this.proxyBase}/sites/${o.id}/deploys`, {
55
+ method: "POST",
56
+ credentials: "include",
57
+ headers: { "Content-Type": "application/json" },
58
+ body: JSON.stringify({ files: e, deploy_source: "drop" })
59
+ });
60
+ if (!s.ok) throw l("deploy create", s.status);
61
+ const n = await s.json();
62
+ return {
63
+ id: o.id,
64
+ deploy_id: n.id,
65
+ subdomain: o.name,
66
+ required: n.required ?? []
67
+ };
68
+ }
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
+ */
74
+ async uploadFile(e, t, o, s) {
75
+ const n = await this.freshToken(), r = await fetch(`${this.apiBase}/deploys/${e}/files${t}`, {
76
+ method: "PUT",
77
+ headers: { "Content-Type": "application/octet-stream", Authorization: `Bearer ${n}` },
78
+ body: o
79
+ });
80
+ if (!r.ok) throw l(`upload ${t}`, r.status);
81
+ }
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
+ */
87
+ async waitUntilReady(e, t) {
88
+ const o = await h({
89
+ proxyBase: this.proxyBase,
90
+ deployId: e.deploy_id,
91
+ pollIntervalMs: this.pollIntervalMs,
92
+ timeoutMs: this.timeoutMs
93
+ });
94
+ if (o.outcome !== "ready")
95
+ throw o.outcome === "failed" ? new f(o.errorMessage) : new _();
96
+ }
97
+ }
98
+ export {
99
+ B as AuthenticatedDropClient
100
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,13 +1,5 @@
1
- /** The subset of a Netlify Site the drop flow needs to redirect and report. */
2
- export interface BuildSite {
3
- /** site id (UUID) */
4
- id: string;
5
- /** the *.netlify.app subdomain, also the app's URL segment */
6
- name: string;
7
- /** https://<name>.netlify.app */
8
- ssl_url?: string;
9
- url?: string;
10
- }
1
+ import { BuildSite } from './types';
2
+ export type { BuildSite } from './types';
11
3
  /** The subset of a Netlify Build returned by `POST /sites/{id}/builds`. */
12
4
  export interface BuildResponse {
13
5
  /** build id */
@@ -76,9 +68,9 @@ export declare class AuthenticatedBuildClient implements BuildClient {
76
68
  private timeoutMs;
77
69
  constructor({ proxyBase, apiBase, getUploadToken, accountSlug, title, pollIntervalMs, timeoutMs, }: AuthenticatedBuildClientConfig);
78
70
  /**
79
- * Create the site the build deploys into. Goes through the proxy with
80
- * `credentials: 'include'` so the app session authorizes it; without an
81
- * account slug the API picks the user's default account.
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.
82
74
  */
83
75
  createSite(): Promise<BuildSite>;
84
76
  /**
@@ -0,0 +1,85 @@
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);
4
+ import { createSiteInAccount as c, pollDeployUntilSettled as h } from "./authedApi.js";
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 {
8
+ constructor({
9
+ proxyBase: t = m,
10
+ apiBase: e = B,
11
+ getUploadToken: a,
12
+ accountSlug: s,
13
+ title: i = "Build from drop deployment",
14
+ pollIntervalMs: n = y,
15
+ timeoutMs: p = w
16
+ }) {
17
+ o(this, "proxyBase");
18
+ o(this, "apiBase");
19
+ o(this, "getUploadToken");
20
+ o(this, "accountSlug");
21
+ o(this, "title");
22
+ o(this, "pollIntervalMs");
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;
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
+ */
31
+ async createSite() {
32
+ return c(this.proxyBase, this.accountSlug);
33
+ }
34
+ /**
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.
39
+ *
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.
44
+ */
45
+ async createBuildFromZip(t, e) {
46
+ const a = await this.getUploadToken();
47
+ 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`, {
51
+ method: "POST",
52
+ headers: { Authorization: `Bearer ${a}` },
53
+ body: s
54
+ });
55
+ if (i.status === 401 || i.status === 403) throw new l();
56
+ if (!i.ok) throw new f(i.status);
57
+ return i.json();
58
+ }
59
+ /**
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.
70
+ */
71
+ async waitUntilBuildReady(t) {
72
+ if (!t.deploy_id) return;
73
+ const e = await h({
74
+ proxyBase: this.proxyBase,
75
+ deployId: t.deploy_id,
76
+ pollIntervalMs: this.pollIntervalMs,
77
+ timeoutMs: this.timeoutMs
78
+ });
79
+ if (e.outcome !== "ready")
80
+ throw e.outcome === "failed" ? new A(e.errorMessage) : new E();
81
+ }
82
+ }
83
+ export {
84
+ U as AuthenticatedBuildClient
85
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { InferredBuildSettings } from './detectBuild';
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.
18
+ */
19
+ export interface DropBuildStash {
20
+ /** The `source.zip` exactly as it would have been uploaded. */
21
+ zipFile: File;
22
+ /** Settings synthesised into the archive's netlify.toml, if any. */
23
+ buildSettings: InferredBuildSettings | null;
24
+ createdAt: number;
25
+ }
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
+ */
31
+ export declare function saveDropStash(zipFile: File, buildSettings?: InferredBuildSettings | null): Promise<boolean>;
32
+ /** The stashed drop, or null when there is none, it expired, or storage is unavailable. */
33
+ export declare function loadDropStash(): Promise<DropBuildStash | null>;
34
+ /** Remove the stash and its marker. Idempotent; never throws. */
35
+ export declare function clearDropStash(): Promise<void>;
36
+ /** Cheap synchronous check so page loads don't open IndexedDB just to look for a stash. */
37
+ export declare function hasDropStashMarker(): boolean;
@@ -0,0 +1,84 @@
1
+ const f = "netlify-drop";
2
+ const s = "build-stash", c = "latest", i = "dropBuildStash";
3
+ const S = (e) => Promise.race([
4
+ e,
5
+ new Promise((r, t) => {
6
+ setTimeout(() => t(new Error("IndexedDB operation timed out")), 3e3);
7
+ })
8
+ ]), d = () => new Promise((e, r) => {
9
+ const t = indexedDB.open(f, 1);
10
+ t.onupgradeneeded = () => {
11
+ t.result.createObjectStore(s);
12
+ }, t.onsuccess = () => e(t.result), t.onerror = () => r(t.error);
13
+ }), u = (e, r) => S(
14
+ d().then(
15
+ (t) => new Promise((o, a) => {
16
+ const n = r(t.transaction(s, e).objectStore(s));
17
+ n.onsuccess = () => {
18
+ t.close(), o(n.result);
19
+ }, n.onerror = () => {
20
+ t.close(), a(n.error);
21
+ };
22
+ })
23
+ )
24
+ ), p = async (e) => {
25
+ const { quota: r, usage: t } = await navigator.storage.estimate();
26
+ return r !== void 0 && t !== void 0 && r - t > e * 2;
27
+ }, T = () => {
28
+ localStorage.setItem(i, JSON.stringify({ expiresAt: Date.now() + 864e5 }));
29
+ }, l = () => {
30
+ localStorage.removeItem(i);
31
+ };
32
+ async function A(e, r = null) {
33
+ var t;
34
+ try {
35
+ if (typeof indexedDB > "u" || !((t = navigator.storage) != null && t.estimate) || e.size > 209715200 || !await p(e.size))
36
+ return !1;
37
+ const o = { zipFile: e, buildSettings: r, createdAt: Date.now() };
38
+ return await u("readwrite", (a) => a.put(o, c)), T(), !0;
39
+ } catch {
40
+ return !1;
41
+ }
42
+ }
43
+ async function D() {
44
+ try {
45
+ if (typeof indexedDB > "u") return null;
46
+ const e = await u(
47
+ "readonly",
48
+ (t) => t.get(c)
49
+ ), r = e && _(e.zipFile);
50
+ return !e || !r ? null : Date.now() - e.createdAt > 864e5 ? (await y(), null) : { ...e, zipFile: r };
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ function _(e) {
56
+ 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
+ }
58
+ async function y() {
59
+ try {
60
+ l();
61
+ } catch {
62
+ }
63
+ try {
64
+ if (typeof indexedDB > "u") return;
65
+ await u("readwrite", (e) => e.delete(c));
66
+ } catch {
67
+ }
68
+ }
69
+ function O() {
70
+ try {
71
+ const e = localStorage.getItem(i);
72
+ if (!e) return !1;
73
+ const { expiresAt: r } = JSON.parse(e);
74
+ return typeof r != "number" || Date.now() > r ? (l(), !1) : !0;
75
+ } catch {
76
+ return !1;
77
+ }
78
+ }
79
+ export {
80
+ y as clearDropStash,
81
+ O as hasDropStashMarker,
82
+ D as loadDropStash,
83
+ A as saveDropStash
84
+ };
File without changes
@@ -15,7 +15,9 @@ export interface DropClient {
15
15
  */
16
16
  export declare class AnonymousDropClient implements DropClient {
17
17
  private apiBase;
18
- constructor(apiBase?: string);
18
+ private pollIntervalMs;
19
+ private timeoutMs;
20
+ constructor(apiBase?: string, pollIntervalMs?: number, timeoutMs?: number);
19
21
  getToken(): Promise<string>;
20
22
  createDeploy(files: Digest, token: string): Promise<DropResponse>;
21
23
  uploadFile(deployId: string, path: string, content: ArrayBuffer, token: string): Promise<void>;
@@ -0,0 +1,47 @@
1
+ import { DEFAULT_API_BASE as p, DEFAULT_POLL_INTERVAL_MS as l, DEFAULT_TIMEOUT_MS as h } from "./constants.js";
2
+ import { DeployTimeoutError as d } from "./errors.js";
3
+ function i(r, t) {
4
+ return Object.assign(new Error(`${r} failed: ${t}`), { status: t });
5
+ }
6
+ class y {
7
+ constructor(t = p, o = l, e = h) {
8
+ this.apiBase = t, this.pollIntervalMs = o, this.timeoutMs = e;
9
+ }
10
+ async getToken() {
11
+ const t = await fetch(`${this.apiBase}/drop/token`, { method: "POST" });
12
+ if (!t.ok) throw i("drop/token", t.status);
13
+ return (await t.json()).token;
14
+ }
15
+ async createDeploy(t, o) {
16
+ const e = await fetch(`${this.apiBase}/drop`, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify({ files: t, token: o })
20
+ });
21
+ if (!e.ok) throw i("drop", e.status);
22
+ return e.json();
23
+ }
24
+ async uploadFile(t, o, e, a) {
25
+ const s = await fetch(`${this.apiBase}/deploys/${t}/files${o}`, {
26
+ method: "PUT",
27
+ headers: { "Content-Type": "application/octet-stream", Authorization: `Bearer ${a}` },
28
+ body: e
29
+ });
30
+ if (!s.ok) throw i(`upload ${o}`, s.status);
31
+ }
32
+ async waitUntilReady(t, o) {
33
+ const e = this.timeoutMs / this.pollIntervalMs;
34
+ for (let a = 0; a < e; a++) {
35
+ const s = await fetch(`${this.apiBase}/deploys/${t.deploy_id}`, {
36
+ headers: { Authorization: `Bearer ${o}` }
37
+ });
38
+ if (s.ok && (await s.json()).state === "ready") return;
39
+ await new Promise((n) => setTimeout(n, this.pollIntervalMs));
40
+ }
41
+ throw new d();
42
+ }
43
+ }
44
+ export {
45
+ y as AnonymousDropClient,
46
+ i as apiError
47
+ };
@@ -0,0 +1,29 @@
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
+ */
12
+ export declare const DEFAULT_PROXY_BASE = "/access-control/bb-api/api/v1";
13
+ /** Direct api.netlify.com root for the uploads the proxy's ~6MB Lambda body limit can't carry. */
14
+ export declare const DEFAULT_API_BASE = "https://api.netlify.com/api/v1";
15
+ export declare const DEFAULT_POLL_INTERVAL_MS = 1000;
16
+ export declare const DEFAULT_TIMEOUT_MS: number;
17
+ 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
+ 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
+ export declare const UPLOAD_RETRY_DELAYS_MS: readonly number[];
@@ -0,0 +1,10 @@
1
+ const _ = "/access-control/bb-api/api/v1", E = "https://api.netlify.com/api/v1", t = 1e3, A = 6e5, c = 5, o = 2e4, s = [1e3, 2e3, 3e3];
2
+ export {
3
+ E as DEFAULT_API_BASE,
4
+ t as DEFAULT_POLL_INTERVAL_MS,
5
+ _ as DEFAULT_PROXY_BASE,
6
+ A as DEFAULT_TIMEOUT_MS,
7
+ c as MAX_CONCURRENT_UPLOADS,
8
+ o as TOKEN_REUSE_MS,
9
+ s as UPLOAD_RETRY_DELAYS_MS
10
+ };
@@ -2,8 +2,11 @@ import { BuildClient, BuildResponse, BuildSite } from './buildClient';
2
2
  import { DropClient } from './client';
3
3
  import { InferredBuildSettings } from './detectBuild';
4
4
  import { DeployProgress, DeployResult, ProcessedFile } from './types';
5
+ import { ZipResult } from './zip';
5
6
  export interface DeployOptions {
6
7
  onProgress?: (e: DeployProgress) => void;
8
+ /** Waits between retries of a failed file upload. Override for tests or a different curve. */
9
+ retryDelays?: readonly number[];
7
10
  }
8
11
  export declare function deployFiles(client: DropClient, files: ProcessedFile[], opts?: DeployOptions): Promise<DeployResult>;
9
12
  export interface BuildDeployResult {
@@ -35,3 +38,9 @@ export interface BuildDeployOptions extends DeployOptions {
35
38
  * doesn't leave an empty site behind in the user's account.
36
39
  */
37
40
  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
+ */
46
+ export declare function deployZippedBuild(client: BuildClient, { file: zip, buildSettings }: ZipResult, opts?: BuildDeployOptions): Promise<BuildDeployResult>;
@@ -0,0 +1,66 @@
1
+ import { UPLOAD_RETRY_DELAYS_MS as w, MAX_CONCURRENT_UPLOADS as o } from "./constants.js";
2
+ import { getDigest as y } from "./digest.js";
3
+ import { zipFiles as f } from "./zip.js";
4
+ const s = /* @__PURE__ */ new Set([400, 422]);
5
+ async function m(e, l, n, t, a) {
6
+ for (let u = 0; ; u++)
7
+ try {
8
+ await e.uploadFile(l, n.path, n.content, t);
9
+ return;
10
+ } catch (i) {
11
+ const p = i.status;
12
+ if (p !== void 0 && s.has(p) || u >= a.length) throw i;
13
+ await new Promise((h) => setTimeout(h, a[u]));
14
+ }
15
+ }
16
+ async function R(e, l, n, t, a, u, i) {
17
+ const p = n.filter((c) => t.has(c.sha));
18
+ let h = 0, r = 0;
19
+ const d = async () => {
20
+ for (; h < p.length; ) {
21
+ const c = p[h++];
22
+ await m(e, l, c, a, u), i == null || i(++r);
23
+ }
24
+ };
25
+ await Promise.all(Array.from({ length: Math.min(o, p.length) }, d));
26
+ }
27
+ async function T(e, l, n = {}) {
28
+ if (!l.length) throw new Error("No files to deploy");
29
+ const { onProgress: t, retryDelays: a = w } = n;
30
+ t == null || t({ phase: "creating" });
31
+ const u = await e.getToken(), i = await e.createDeploy(y(l), u), p = i.required.length;
32
+ return t == null || t({ phase: "uploading", uploaded: 0, total: p }), await R(
33
+ e,
34
+ i.deploy_id,
35
+ l,
36
+ new Set(i.required),
37
+ u,
38
+ a,
39
+ (h) => t == null ? void 0 : t({ phase: "uploading", uploaded: h, total: p })
40
+ ), t == null || t({ phase: "processing" }), await e.waitUntilReady(i, u), t == null || t({ phase: "ready" }), { deploy: i, token: u, url: `https://${i.subdomain}.netlify.app` };
41
+ }
42
+ async function B(e, l, n = {}) {
43
+ var a;
44
+ if (!l.length) throw new Error("No files to deploy");
45
+ (a = n.onProgress) == null || a.call(n, { phase: "zipping" });
46
+ const t = await f(l);
47
+ return S(e, t, n);
48
+ }
49
+ async function S(e, { file: l, buildSettings: n }, t = {}) {
50
+ const { onProgress: a, waitForBuild: u = !1 } = t;
51
+ a == null || a({ phase: "creating" });
52
+ const i = await e.createSite();
53
+ a == null || a({ phase: "uploading" });
54
+ const p = await e.createBuildFromZip(i.id, l);
55
+ return a == null || a({ phase: "building" }), u && await e.waitUntilBuildReady(p), a == null || a({ phase: "ready" }), {
56
+ site: i,
57
+ build: p,
58
+ buildSettings: n,
59
+ url: i.ssl_url || i.url || `https://${i.name}.netlify.app`
60
+ };
61
+ }
62
+ export {
63
+ B as deployBuild,
64
+ T as deployFiles,
65
+ S as deployZippedBuild
66
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,7 @@
6
6
  * deployed as a one-asset site (matches react-ui).
7
7
  */
8
8
  export declare class UnsupportedDropError extends Error {
9
+ readonly code = "unsupported-drop";
9
10
  constructor();
10
11
  }
11
12
  /**
@@ -17,10 +18,21 @@ export declare class UnsupportedDropError extends Error {
17
18
  * through login/signup and have them drop again.
18
19
  */
19
20
  export declare class NotAuthenticatedError extends Error {
21
+ readonly code = "not-authenticated";
20
22
  constructor(message?: string);
21
23
  }
24
+ /**
25
+ * Whether an error is the pre-mutation "no session" signal, however it
26
+ * traveled. This package ships as both raw TypeScript (`./components`) and
27
+ * compiled dist (`./preact`, `./drop-core`), so two copies of the class can
28
+ * coexist in one app and defeat `instanceof` — the `code` brand is the
29
+ * copy-proof fallback. Control flow that decides whether an anonymous retry
30
+ * is safe must use this, never a bare `instanceof`.
31
+ */
32
+ export declare function isNotAuthenticatedError(err: unknown): err is NotAuthenticatedError;
22
33
  /** `POST …/sites` failed, so there is nothing to build into. Carries the HTTP status. */
23
34
  export declare class SiteCreateError extends Error {
35
+ readonly code = "site-create-failed";
24
36
  status?: number;
25
37
  constructor(status?: number);
26
38
  }
@@ -29,6 +41,7 @@ export declare class SiteCreateError extends Error {
29
41
  * started. Distinct from `BuildFailedError`, which is a build that ran and broke.
30
42
  */
31
43
  export declare class BuildCreateError extends Error {
44
+ readonly code = "build-create-failed";
32
45
  status?: number;
33
46
  constructor(status?: number);
34
47
  }
@@ -40,10 +53,26 @@ export declare class BuildCreateError extends Error {
40
53
  */
41
54
  export declare class BuildFailedError extends Error {
42
55
  reason?: string | undefined;
56
+ readonly code = "build-failed";
43
57
  constructor(reason?: string | undefined);
44
58
  }
45
59
  /** The build hadn't settled before the poller gave up. The build itself may still finish. */
46
60
  export declare class BuildTimeoutError extends Error {
61
+ readonly code = "build-timeout";
62
+ constructor();
63
+ }
64
+ /**
65
+ * A static deploy ran and ended in `error`/`rejected`. Like a build failure,
66
+ * the API's `error_message` is the actionable part, so it travels on the error.
67
+ */
68
+ export declare class DeployFailedError extends Error {
69
+ reason?: string | undefined;
70
+ readonly code = "deploy-failed";
71
+ constructor(reason?: string | undefined);
72
+ }
73
+ /** The deploy hadn't gone ready before the poller gave up. It may still finish. */
74
+ export declare class DeployTimeoutError extends Error {
75
+ readonly code = "deploy-timeout";
47
76
  constructor();
48
77
  }
49
78
  /**