@netlify/spark-ui 1.29.0 → 1.31.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +4 -3
  2. package/dist/chunks/browser.Ca2rTNTu.js +769 -0
  3. package/dist/components/articleCopyActions.d.ts +9 -0
  4. package/dist/components/articleCopyActions.test.d.ts +1 -0
  5. package/dist/components/index.d.ts +2 -2
  6. package/dist/components/preact/DropZone/DropZone.d.ts +84 -15
  7. package/dist/components/preact/DropZone/DropZone.js +117 -85
  8. package/dist/components/preact/DropZone/drop-core/authedDropClient.d.ts +80 -0
  9. package/dist/components/preact/DropZone/drop-core/authedDropClient.js +99 -0
  10. package/dist/components/preact/DropZone/drop-core/buildClient.d.ts +98 -0
  11. package/dist/components/preact/DropZone/drop-core/buildClient.js +92 -0
  12. package/dist/components/preact/DropZone/drop-core/client.d.ts +4 -0
  13. package/dist/components/preact/DropZone/drop-core/client.js +2 -1
  14. package/dist/components/preact/DropZone/drop-core/deploy.d.ts +31 -0
  15. package/dist/components/preact/DropZone/drop-core/deploy.js +36 -18
  16. package/dist/components/preact/DropZone/drop-core/detectBuild.d.ts +26 -0
  17. package/dist/components/preact/DropZone/drop-core/detectBuild.js +80 -53
  18. package/dist/components/preact/DropZone/drop-core/errors.d.ts +38 -0
  19. package/dist/components/preact/DropZone/drop-core/errors.js +50 -5
  20. package/dist/components/preact/DropZone/drop-core/fileTypes.d.ts +17 -0
  21. package/dist/components/preact/DropZone/drop-core/fileTypes.js +11 -0
  22. package/dist/components/preact/DropZone/drop-core/index.d.ts +4 -0
  23. package/dist/components/preact/DropZone/drop-core/index.js +32 -15
  24. package/dist/components/preact/DropZone/drop-core/readFiles.js +1 -1
  25. package/dist/components/preact/DropZone/drop-core/types.d.ts +6 -1
  26. package/dist/components/preact/DropZone/drop-core/zip.d.ts +46 -0
  27. package/dist/components/preact/DropZone/drop-core/zip.js +43 -0
  28. package/dist/components/preact/DropZone/index.d.ts +3 -2
  29. package/dist/components/preact/DropZone/index.js +27 -9
  30. package/dist/components/preact/DropZone/useDropDeploy.d.ts +36 -5
  31. package/dist/components/preact/DropZone/useDropDeploy.js +140 -65
  32. package/dist/components/preact/index.d.ts +2 -1
  33. package/dist/components/preact/index.js +17 -12
  34. package/package.json +5 -2
  35. package/packages/components/ArticleCopyActions.astro +122 -0
  36. package/packages/components/articleCopyActions.test.ts +143 -0
  37. package/packages/components/articleCopyActions.ts +41 -0
  38. package/packages/components/index.ts +15 -0
  39. package/packages/components/preact/DropZone/DropZone.tsx +145 -24
  40. package/packages/components/preact/DropZone/drop-core/authedDropClient.ts +194 -0
  41. package/packages/components/preact/DropZone/drop-core/buildClient.ts +190 -0
  42. package/packages/components/preact/DropZone/drop-core/client.ts +1 -1
  43. package/packages/components/preact/DropZone/drop-core/deploy.ts +65 -0
  44. package/packages/components/preact/DropZone/drop-core/detectBuild.ts +125 -41
  45. package/packages/components/preact/DropZone/drop-core/errors.ts +84 -1
  46. package/packages/components/preact/DropZone/drop-core/fileTypes.ts +34 -0
  47. package/packages/components/preact/DropZone/drop-core/index.ts +4 -0
  48. package/packages/components/preact/DropZone/drop-core/readFiles.ts +1 -1
  49. package/packages/components/preact/DropZone/drop-core/types.ts +12 -1
  50. package/packages/components/preact/DropZone/drop-core/zip.ts +118 -0
  51. package/packages/components/preact/DropZone/index.tsx +24 -0
  52. package/packages/components/preact/DropZone/useDropDeploy.ts +219 -34
  53. package/packages/components/preact/index.ts +2 -1
  54. package/dist/chunks/browser.D58rdxmc.js +0 -444
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Wire up the "Copy URL" / "Copy page" buttons rendered by ArticleCopyActions.astro.
3
+ *
4
+ * This runs on every `astro:page-load` (initial load *and* every client-side
5
+ * navigation via `<ClientRouter />`), so the buttons keep working after routing
6
+ * to another article/post. Each wrapper is guarded so re-initialising the same
7
+ * DOM node never attaches duplicate listeners.
8
+ */
9
+ export declare function initArticleCopyActions(root?: ParentNode): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -5,6 +5,6 @@ export { default as SearchLink } from './preact/SearchModal/components/SearchLin
5
5
  export { default as SearchHeading } from './preact/SearchModal/components/SearchHeading/SearchHeading';
6
6
  export { default as SearchToggle } from './preact/SearchModal/components/SearchToggle/SearchToggle';
7
7
  export { default as SearchModal } from './preact/SearchModal/components/SearchModal/SearchModal';
8
- export { DropZone, AnonymousDropClient, deployFiles, readDataTransfer, readFileList, } from './preact/DropZone';
9
- export type { DropClient, DropResponse, Digest, DeployResult, DeployProgress, DeployPhase, ProcessedFile, } from './preact/DropZone';
8
+ export { DropZone, AnonymousDropClient, AuthenticatedBuildClient, AuthenticatedDropClient, deployBuild, deployFiles, detectBuild, inferBuildSettings, readDataTransfer, readFileList, zipFiles, } from './preact/DropZone';
9
+ export type { AuthenticatedBuildClientConfig, AuthenticatedDropClientConfig, BuildClient, BuildDeployResult, BuildDetection, BuildSite, DeployedBuild, DeployedSite, DropClient, DropResponse, Digest, DeployResult, DeployProgress, DeployPhase, InferredBuildSettings, ProcessedFile, } from './preact/DropZone';
10
10
  export { type IconName } from './preact/Icon';
@@ -1,6 +1,6 @@
1
1
  import { ComponentChildren } from 'preact';
2
- import { BuildDetection, DropClient } from './drop-core';
3
- import { DeployedSite, DeployError, TrackFn } from './useDropDeploy';
2
+ import { BuildClient, BuildDetection, DropClient } from './drop-core';
3
+ import { DeployedBuild, DeployedSite, DeployError, TrackFn } from './useDropDeploy';
4
4
  interface Props {
5
5
  /** Content the drop layer sits on top of. The zone is invisible until you drag over it. */
6
6
  children?: ComponentChildren;
@@ -10,23 +10,81 @@ interface Props {
10
10
  client?: DropClient;
11
11
  /** Build the "claim this site" URL from the assigned site name (subdomain). */
12
12
  claimUrl?: (siteName: string) => string;
13
+ /**
14
+ * Where an authenticated static deploy sends the visitor — the site already
15
+ * lives in their account, so there is no claim step. Defaults to the new
16
+ * site's project overview on app.netlify.com.
17
+ */
18
+ siteDashboardUrl?: (siteName: string) => string;
13
19
  /**
14
20
  * Where to send a build-required drop (framework source, build command,
15
- * functions) when `onBuildRequired` isn't set. Such projects can't deploy
16
- * through the anonymous static drop API, so by default the user is redirected
17
- * here — the app signup page with `next=/drop` — to build on the
18
- * authenticated Drop page. Override to point at your own destination.
21
+ * functions) that can't be built here — no `getUploadToken`/`buildClient`, or a
22
+ * visitor who isn't logged in. Such projects can't deploy through the anonymous
23
+ * static drop API, so by default the user is redirected here — the app signup
24
+ * page with `next=/drop` — to build on the authenticated Drop page. Override to
25
+ * point at your own destination.
19
26
  */
20
27
  buildSignupUrl?: string;
21
28
  /**
22
- * Called when a build-required drop is detected, instead of the default
23
- * `buildSignupUrl` redirect. Use it to own the hand-off yourself (custom
24
- * routing, persisting the drop, gating on auth). When set, the component does
25
- * not redirect. The static drop path is unaffected either way.
29
+ * Mint a short-lived bearer token for direct api.netlify.com uploads — pass it
30
+ * to switch on the whole authenticated path for logged-in visitors: static
31
+ * drops deploy straight into their account (then `siteDashboardUrl`), and
32
+ * build-required drops build in place instead of being handed off to the app.
33
+ * On netlify.com this reads `GET /access-control/generate-access-control-token`.
34
+ * Resolving `null` means "no session": static drops fall back to the anonymous
35
+ * claim flow, build drops to `buildSignupUrl`.
36
+ * See [Logged-in visitors](#logged-in-visitors).
37
+ */
38
+ getUploadToken?: () => Promise<string | null>;
39
+ /**
40
+ * Inject a custom `BuildClient` (tests, staging, a different account flow).
41
+ * Takes precedence over `getUploadToken` for the build path.
42
+ */
43
+ buildClient?: BuildClient;
44
+ /**
45
+ * Inject a custom authenticated `DropClient` for static drops (tests, staging,
46
+ * a different account flow). Takes precedence over `getUploadToken` for the
47
+ * static path. Distinct from `client`, which is the anonymous fallback.
48
+ */
49
+ authenticatedDropClient?: DropClient;
50
+ /**
51
+ * API root for the authenticated paths' JSON calls, which go through your
52
+ * access-control rewrite so the session cookie authorizes them. Defaults to
53
+ * `/access-control/bb-api/api/v1`. (Uploads always go direct to
54
+ * `api.netlify.com` — the proxy can't carry bodies that large.)
55
+ */
56
+ proxyBase?: string;
57
+ /** Create the visitor's new project in this account. Omit for their default account. */
58
+ accountSlug?: string;
59
+ /**
60
+ * Where an enqueued build sends the visitor. Defaults to the new site's
61
+ * project overview, same as a static deploy; `build.deployId` is available for
62
+ * consumers who want the deploy-log page instead.
63
+ */
64
+ buildDeployUrl?: (build: DeployedBuild) => string;
65
+ /**
66
+ * Poll until the build finishes before handing off, instead of redirecting as
67
+ * soon as it's enqueued. The trade-off: waiting means a failing build surfaces
68
+ * its own error message here (via `onError` and the overlay) rather than the
69
+ * visitor discovering it on the deploy page — at the cost of holding them on a
70
+ * spinner for the length of the build, which is minutes. Off by default.
71
+ */
72
+ waitForBuild?: boolean;
73
+ /**
74
+ * Called when a build-required drop is detected, instead of building it or
75
+ * redirecting. Use it to own the hand-off yourself (custom routing, persisting
76
+ * the drop, gating on auth). When set, the component neither builds nor
77
+ * redirects. The static drop path is unaffected either way.
26
78
  */
27
79
  onBuildRequired?: (info: BuildDetection) => void;
28
80
  /** Called once the deploy is live. */
29
81
  onDeploy?: (result: DeployedSite) => void;
82
+ /**
83
+ * Called once a build-required drop has been zipped, uploaded and enqueued —
84
+ * before the redirect to `buildDeployUrl`. The site exists at this point but
85
+ * won't serve content until the build finishes.
86
+ */
87
+ onBuildDeploy?: (build: DeployedBuild) => void;
30
88
  /**
31
89
  * Called when a deploy fails, with a humanized `message` (ready to show a
32
90
  * user — the same copy the built-in overlay uses) and the raw `reason`. Pair
@@ -46,11 +104,22 @@ interface Props {
46
104
  * properties. Stays provider-agnostic — wire it to Segment/Amplitude/GA in the
47
105
  * consuming app, e.g. `onTrack={(name, props) => new Analytics().track(name, props)}`.
48
106
  * Emitted events (all include `dropzone_id` when `analyticsId` is set):
49
- * - `dropzone_files_dropped` — files dropped via drag ({ method: 'drag', file_count })
107
+ * - `dropzone_files_dropped` — files dropped via drag ({ method: 'drag', file_count, file_types })
50
108
  * - `dropzone_browse_opened` — file picker opened ({ method: 'keyboard' | 'click' })
51
- * - `dropzone_build_required` — drop needs a build; handed off to signup ({ method, reason, file_count })
52
- * - `dropzone_deploy_succeeded` — deploy went live ({ method, site_name, file_count })
53
- * - `dropzone_deploy_failed` — deploy errored out ({ method, reason, file_count? })
109
+ * - `dropzone_build_required` — drop needs a build (built here, or handed off) ({ method, reason, file_count, file_types })
110
+ * - `dropzone_deploy_succeeded` — static deploy went live ({ method, site_name, file_count, authenticated, auth_fallback?, file_types })
111
+ * - `dropzone_build_deploy_succeeded` — build enqueued for a logged-in visitor ({ method, reason, site_name, file_count, framework?, file_types })
112
+ * - `dropzone_deploy_failed` — deploy or build errored out ({ method, reason, file_count?, file_types })
113
+ *
114
+ * `authenticated` says whether the deploy landed in the visitor's account;
115
+ * `auth_fallback: true` is sent only when an authenticated attempt bounced
116
+ * (stale auth hint) and the drop completed anonymously — omitted otherwise.
117
+ * `file_types` is the sorted, de-duplicated list of MIME types in the
118
+ * selection, with `application/octet-stream` standing in wherever the browser
119
+ * reports none (a dragged folder, an `.exe`). It is omitted when the browser
120
+ * exposes no files at all. Note `dropzone_files_dropped` only fires for drag —
121
+ * a file-picker selection reports its types on the outcome events, as
122
+ * `dropzone_browse_opened` fires before any file exists.
54
123
  */
55
124
  onTrack?: TrackFn;
56
125
  /**
@@ -61,5 +130,5 @@ interface Props {
61
130
  analyticsId?: string;
62
131
  className?: string;
63
132
  }
64
- export default function DropZone({ children, apiBase, client, claimUrl, buildSignupUrl, onBuildRequired, onDeploy, onError, showStatus, onTrack, analyticsId, className, }: Props): import("preact").JSX.Element;
133
+ export default function DropZone({ children, apiBase, client, claimUrl, siteDashboardUrl, buildSignupUrl, getUploadToken, buildClient, authenticatedDropClient, proxyBase, accountSlug, buildDeployUrl, waitForBuild, onBuildRequired, onDeploy, onBuildDeploy, onError, showStatus, onTrack, analyticsId, className, }: Props): import("preact").JSX.Element;
65
134
  export {};
@@ -1,154 +1,186 @@
1
1
  import { u as e } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { A as D, d as E, y as I } from "../../../chunks/hooks.module.DRcxknIz.js";
3
- import { readFileList as P, readDataTransfer as U } from "./drop-core/readFiles.js";
2
+ import { A as j, d as G, y as K } from "../../../chunks/hooks.module.DRcxknIz.js";
3
+ import { readFileList as L, readDataTransfer as Z } from "./drop-core/readFiles.js";
4
+ import { getFileTypes as D } from "./drop-core/fileTypes.js";
4
5
  import "./drop-core/detectBuild.js";
5
- import { useDropDeploy as j } from "./useDropDeploy.js";
6
- import { Fragment as k } from "preact";
7
- import '../../../assets/DropZone.css';const B = 120, F = (n) => `https://app.netlify.com/drop/${n}`, M = "https://app.netlify.com/signup?next=/drop&utm_campaign=drop";
8
- function H({
9
- children: n,
10
- apiBase: l,
11
- client: i,
12
- claimUrl: s = F,
13
- buildSignupUrl: u = M,
14
- onBuildRequired: w,
15
- onDeploy: A,
16
- onError: C,
6
+ import "./drop-core/zip.js";
7
+ import { useDropDeploy as q } from "./useDropDeploy.js";
8
+ import { Fragment as w } from "preact";
9
+ import '../../../assets/DropZone.css';const H = 120, J = (r) => `https://app.netlify.com/drop/${r}`, Q = (r) => `https://app.netlify.com/projects/${r}`, W = "https://app.netlify.com/signup?next=/drop&utm_campaign=drop", X = ({ siteName: r }) => `https://app.netlify.com/projects/${r}`;
10
+ function ce({
11
+ children: r,
12
+ apiBase: a,
13
+ client: l,
14
+ claimUrl: c = J,
15
+ siteDashboardUrl: u = Q,
16
+ buildSignupUrl: A = W,
17
+ getUploadToken: T,
18
+ buildClient: C,
19
+ authenticatedDropClient: S,
20
+ proxyBase: _,
21
+ accountSlug: $,
22
+ buildDeployUrl: B = X,
23
+ waitForBuild: U,
24
+ onBuildRequired: P,
25
+ onDeploy: R,
26
+ onBuildDeploy: x,
27
+ onError: E,
17
28
  showStatus: f = !0,
18
- onTrack: c,
29
+ onTrack: d,
19
30
  analyticsId: g,
20
- className: T
31
+ className: F
21
32
  }) {
22
- const h = (r, t = {}) => c == null ? void 0 : c(r, g ? { dropzone_id: g, ...t } : t), { status: a, warning: m, busy: o, deploy: v, reset: _ } = j({
23
- apiBase: l,
24
- client: i,
25
- claimUrl: s,
26
- buildSignupUrl: u,
27
- onBuildRequired: w,
28
- onDeploy: A,
29
- onError: C,
30
- track: h
31
- }), { active: p, keepActive: S, endDrag: R } = N(), b = D(null), y = (r) => {
32
- var t;
33
- h("dropzone_browse_opened", { method: r }), (t = b.current) == null || t.click();
34
- }, $ = f && (p || o || a.kind === "error"), x = [
33
+ const y = (t, n = {}) => d == null ? void 0 : d(t, g ? { dropzone_id: g, ...n } : n), { status: s, warning: b, busy: i, deploy: m, reset: I } = q({
34
+ apiBase: a,
35
+ client: l,
36
+ claimUrl: c,
37
+ siteDashboardUrl: u,
38
+ buildSignupUrl: A,
39
+ getUploadToken: T,
40
+ buildClient: C,
41
+ authenticatedDropClient: S,
42
+ proxyBase: _,
43
+ accountSlug: $,
44
+ buildDeployUrl: B,
45
+ waitForBuild: U,
46
+ onBuildRequired: P,
47
+ onDeploy: R,
48
+ onBuildDeploy: x,
49
+ onError: E,
50
+ track: y
51
+ }), { active: p, keepActive: M, endDrag: N } = Y(), h = j(null), v = (t) => {
52
+ var n;
53
+ y("dropzone_browse_opened", { method: t }), (n = h.current) == null || n.click();
54
+ }, O = f && (p || i || s.kind === "error"), V = [
35
55
  "n-dropzone",
36
56
  p && "is-active",
37
- o && "is-busy",
57
+ i && "is-busy",
38
58
  !f && "is-bare",
39
- T
59
+ F
40
60
  ].filter(Boolean).join(" ");
41
61
  return /* @__PURE__ */ e(
42
62
  "div",
43
63
  {
44
- class: x,
45
- "data-state": a.kind,
64
+ class: V,
65
+ "data-state": s.kind,
46
66
  role: "button",
47
- tabIndex: o ? -1 : 0,
67
+ tabIndex: i ? -1 : 0,
48
68
  "aria-label": "Drop a site folder or zip here to deploy it, or press Enter to browse for files",
49
- "aria-busy": o,
50
- onDragOver: (r) => {
51
- o || (r.preventDefault(), S());
69
+ "aria-busy": i,
70
+ onDragOver: (t) => {
71
+ i || (t.preventDefault(), M());
52
72
  },
53
- onDrop: (r) => {
54
- if (r.preventDefault(), R(), o) return;
55
- const t = r.dataTransfer;
56
- t && v(() => U(t), "drag");
73
+ onDrop: (t) => {
74
+ if (t.preventDefault(), N(), i) return;
75
+ const n = t.dataTransfer;
76
+ n && m(() => Z(n), "drag", D(n.files));
57
77
  },
58
- onClick: (r) => {
59
- !o && r.target === r.currentTarget && y("click");
78
+ onClick: (t) => {
79
+ !i && t.target === t.currentTarget && v("click");
60
80
  },
61
- onKeyDown: (r) => {
62
- o || (r.key === "Enter" || r.key === " ") && (r.preventDefault(), y("keyboard"));
81
+ onKeyDown: (t) => {
82
+ i || (t.key === "Enter" || t.key === " ") && (t.preventDefault(), v("keyboard"));
63
83
  },
64
84
  children: [
65
- /* @__PURE__ */ e("div", { class: "n-dropzone-content", children: n }),
85
+ /* @__PURE__ */ e("div", { class: "n-dropzone-content", children: r }),
66
86
  /* @__PURE__ */ e(
67
87
  "input",
68
88
  {
69
- ref: b,
89
+ ref: h,
70
90
  type: "file",
71
91
  hidden: !0,
72
92
  multiple: !0,
73
93
  webkitdirectory: "",
74
94
  directory: "",
75
- onChange: (r) => {
76
- var z;
77
- const t = r.currentTarget;
78
- (z = t.files) != null && z.length && v(() => P(t.files), "browse"), t.value = "";
95
+ onChange: (t) => {
96
+ var k;
97
+ const n = t.currentTarget;
98
+ if ((k = n.files) != null && k.length) {
99
+ const z = n.files;
100
+ m(() => L(z), "browse", D(z));
101
+ }
102
+ n.value = "";
79
103
  }
80
104
  }
81
105
  ),
82
- $ && /* @__PURE__ */ e(
106
+ O && /* @__PURE__ */ e(
83
107
  "div",
84
108
  {
85
109
  class: "n-dropzone-overlay n-fadeIn",
86
110
  children: [
87
- /* @__PURE__ */ e(O, { status: a, active: p, onReset: _ }),
88
- m && a.kind !== "error" && o && /* @__PURE__ */ e("p", { class: "n-dropzone-warning", role: "status", children: m })
111
+ /* @__PURE__ */ e(ee, { status: s, active: p, onReset: I }),
112
+ b && s.kind !== "error" && i && /* @__PURE__ */ e("p", { class: "n-dropzone-warning", role: "status", children: b })
89
113
  ]
90
114
  },
91
- a.kind === "idle" ? "active" : a.kind
115
+ s.kind === "idle" ? "active" : s.kind
92
116
  )
93
117
  ]
94
118
  }
95
119
  );
96
120
  }
97
- function N() {
98
- const [n, l] = E(!1), i = D();
99
- return I(() => () => clearTimeout(i.current), []), { active: n, keepActive: () => {
100
- l(!0), clearTimeout(i.current), i.current = setTimeout(() => l(!1), B);
121
+ function Y() {
122
+ const [r, a] = G(!1), l = j();
123
+ return K(() => () => clearTimeout(l.current), []), { active: r, keepActive: () => {
124
+ a(!0), clearTimeout(l.current), l.current = setTimeout(() => a(!1), H);
101
125
  }, endDrag: () => {
102
- clearTimeout(i.current), l(!1);
126
+ clearTimeout(l.current), a(!1);
103
127
  } };
104
128
  }
105
- function O({
106
- status: n,
107
- active: l,
108
- onReset: i
129
+ function ee({
130
+ status: r,
131
+ active: a,
132
+ onReset: l
109
133
  }) {
110
- switch (n.kind) {
134
+ switch (r.kind) {
111
135
  // Idle-but-active means a drag is hovering the zone.
112
136
  case "idle":
113
- return l ? /* @__PURE__ */ e("p", { class: "n-dropzone-hint", children: "Drop to deploy" }) : null;
137
+ return a ? /* @__PURE__ */ e("p", { class: "n-dropzone-hint", children: "Drop to deploy" }) : null;
114
138
  case "reading":
115
- return /* @__PURE__ */ e(d, { label: "Reading files…" });
139
+ return /* @__PURE__ */ e(o, { label: "Reading files…" });
140
+ case "zipping":
141
+ return /* @__PURE__ */ e(o, { label: "Packaging your project…" });
116
142
  case "creating":
117
- return /* @__PURE__ */ e(d, { label: "Creating deploy…" });
143
+ return /* @__PURE__ */ e(o, { label: "Creating deploy…" });
118
144
  case "processing":
119
- return /* @__PURE__ */ e(d, { label: "Processing — almost live…" });
145
+ return /* @__PURE__ */ e(o, { label: "Processing — almost live…" });
146
+ case "building":
147
+ return /* @__PURE__ */ e(o, { label: "Building your project…" });
120
148
  case "redirecting":
121
- return /* @__PURE__ */ e(
122
- d,
123
- {
124
- label: n.target === "signup" ? "This project needs a build — taking you to Netlify…" : "Site’s live — taking you to claim it…"
125
- }
126
- );
149
+ switch (r.target) {
150
+ case "signup":
151
+ return /* @__PURE__ */ e(o, { label: "This project needs a build — taking you to Netlify…" });
152
+ case "build":
153
+ return /* @__PURE__ */ e(o, { label: "Build started — taking you to your new project…" });
154
+ case "claim":
155
+ return /* @__PURE__ */ e(o, { label: "Site’s live — taking you to claim it…" });
156
+ case "dashboard":
157
+ return /* @__PURE__ */ e(o, { label: "Site’s live — taking you to your new project…" });
158
+ }
127
159
  case "uploading": {
128
- const s = n.total ? Math.round(n.uploaded / n.total * 100) : 0;
129
- return /* @__PURE__ */ e(k, { children: [
130
- /* @__PURE__ */ e(d, { label: `Uploading ${n.uploaded}/${n.total} files…` }),
131
- /* @__PURE__ */ e("div", { class: "n-dropzone-progress", role: "progressbar", "aria-valuenow": s, children: /* @__PURE__ */ e("div", { class: "n-dropzone-progress-bar", style: { width: `${s}%` } }) })
160
+ const c = r.total ? Math.round(r.uploaded / r.total * 100) : 0;
161
+ return /* @__PURE__ */ e(w, { children: [
162
+ /* @__PURE__ */ e(o, { label: `Uploading ${r.uploaded}/${r.total} files…` }),
163
+ /* @__PURE__ */ e("div", { class: "n-dropzone-progress", role: "progressbar", "aria-valuenow": c, children: /* @__PURE__ */ e("div", { class: "n-dropzone-progress-bar", style: { width: `${c}%` } }) })
132
164
  ] });
133
165
  }
134
166
  case "error":
135
- return /* @__PURE__ */ e(k, { children: [
167
+ return /* @__PURE__ */ e(w, { children: [
136
168
  /* @__PURE__ */ e("p", { class: "n-dropzone-title n-dropzone-title--error", children: "Something went wrong" }),
137
- /* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", role: "alert", children: n.message }),
138
- /* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-browse", onClick: i, children: "Try again" })
169
+ /* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", role: "alert", children: r.message }),
170
+ /* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-browse", onClick: l, children: "Try again" })
139
171
  ] });
140
172
  }
141
173
  }
142
- function d({ label: n }) {
174
+ function o({ label: r }) {
143
175
  return /* @__PURE__ */ e("div", { class: "n-dropzone-loading", children: [
144
176
  /* @__PURE__ */ e("span", { class: "n-dropzone-spinner", "aria-hidden": "true" }),
145
177
  /* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", "aria-live": "polite", children: [
146
- n,
178
+ r,
147
179
  " ",
148
180
  /* @__PURE__ */ e("span", { class: "n-dropzone-nudge", children: "Please don’t refresh." })
149
181
  ] })
150
182
  ] });
151
183
  }
152
184
  export {
153
- H as default
185
+ ce as default
154
186
  };
@@ -0,0 +1,80 @@
1
+ import { DropClient } from './client';
2
+ import { Digest, DropResponse } from './types';
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
+ */
10
+ proxyBase?: string;
11
+ /**
12
+ * Direct API base for the per-file uploads. The proxy above runs in a Lambda
13
+ * with a ~6MB request body limit, so file PUTs go straight to
14
+ * `api.netlify.com` (whose `/api/*` CORS policy allows any origin).
15
+ */
16
+ apiBase?: string;
17
+ /**
18
+ * Mint a short-lived bearer token for those direct uploads. On netlify.com
19
+ * this is `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
+ getUploadToken: () => Promise<string | null>;
24
+ /** Create the site in this account. Omit to use the user's default account. */
25
+ accountSlug?: string;
26
+ /** How often to re-read the deploy while waiting. Default 1s. */
27
+ pollIntervalMs?: number;
28
+ /** How long to wait before giving up on the deploy. Default 10 minutes. */
29
+ timeoutMs?: number;
30
+ }
31
+ /**
32
+ * Authenticated drop client for logged-in visitors. Unlike `AnonymousDropClient`
33
+ * — which creates an account-less site the visitor must later claim — this
34
+ * deploys straight into the visitor's account via the sites/deploys API,
35
+ * mirroring what app.netlify.com does for a logged-in drag-and-drop deploy.
36
+ * There is no claim step, so the caller should hand off to the site's dashboard
37
+ * page rather than the claim page.
38
+ *
39
+ * Requests split across two hosts by necessity: JSON calls go through the
40
+ * consumer's access-control proxy (cookie session, no token handling in the
41
+ * browser), while the per-file PUTs go direct to `api.netlify.com` with the
42
+ * short-lived bearer from `getUploadToken()` because the proxy can't carry
43
+ * large bodies.
44
+ *
45
+ * Error-mapping invariant: only pre-mutation failures — a null upload token, or
46
+ * a 401/403 creating the site — throw `NotAuthenticatedError`, which is the
47
+ * signal the drop flow may safely retry anonymously. Once the site exists, an
48
+ * auth failure throws a plain status-carrying error instead: falling back at
49
+ * that point would strand an empty site in the account *and* deploy a duplicate
50
+ * anonymously.
51
+ */
52
+ export declare class AuthenticatedDropClient implements DropClient {
53
+ private proxyBase;
54
+ private apiBase;
55
+ private getUploadToken;
56
+ private accountSlug?;
57
+ private pollIntervalMs;
58
+ private timeoutMs;
59
+ constructor({ proxyBase, apiBase, getUploadToken, accountSlug, pollIntervalMs, timeoutMs, }: AuthenticatedDropClientConfig);
60
+ /**
61
+ * The token here is the upload bearer, not a drop token — it authorizes the
62
+ * direct-API file PUTs, while the proxied JSON calls ignore it and ride the
63
+ * session cookie. Minted once per deploy, before anything is created, so a
64
+ * missing session fails the whole attempt cleanly.
65
+ */
66
+ getToken(): Promise<string>;
67
+ /**
68
+ * Two proxied calls: create the site in the visitor's account, then the
69
+ * deploy on it. Mapped into the anonymous `DropResponse` shape so
70
+ * `deployFiles` runs unchanged over either client.
71
+ */
72
+ createDeploy(files: Digest, _token: string): Promise<DropResponse>;
73
+ uploadFile(deployId: string, path: string, content: ArrayBuffer, token: string): Promise<void>;
74
+ /**
75
+ * Poll the deploy through the proxy until it settles — the session cookie
76
+ * outlives the short-lived upload bearer, so a slow deploy can't expire its
77
+ * own poll. Resolve on `ready`, throw on `error`/`rejected`, throw on timeout.
78
+ */
79
+ waitUntilReady(deploy: DropResponse, _token: string): Promise<void>;
80
+ }
@@ -0,0 +1,99 @@
1
+ var p = Object.defineProperty;
2
+ var h = (n, t, s) => t in n ? p(n, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : n[t] = s;
3
+ var r = (n, t, s) => h(n, typeof t != "symbol" ? t + "" : t, s);
4
+ import { apiError as d } from "./client.js";
5
+ import { NotAuthenticatedError as l, SiteCreateError as y } from "./errors.js";
6
+ const u = "/access-control/bb-api/api/v1", w = "https://api.netlify.com/api/v1", f = 1e3, m = 10 * 60 * 1e3, T = /* @__PURE__ */ new Set(["ready", "error", "rejected"]);
7
+ class g {
8
+ constructor({
9
+ proxyBase: t = u,
10
+ apiBase: s = w,
11
+ getUploadToken: i,
12
+ accountSlug: o,
13
+ pollIntervalMs: a = f,
14
+ timeoutMs: e = m
15
+ }) {
16
+ r(this, "proxyBase");
17
+ r(this, "apiBase");
18
+ r(this, "getUploadToken");
19
+ r(this, "accountSlug");
20
+ r(this, "pollIntervalMs");
21
+ r(this, "timeoutMs");
22
+ this.proxyBase = t, this.apiBase = s, this.getUploadToken = i, this.accountSlug = o, this.pollIntervalMs = a, this.timeoutMs = e;
23
+ }
24
+ /**
25
+ * The token here is the upload bearer, not a drop token — it authorizes the
26
+ * direct-API file PUTs, while the proxied JSON calls ignore it and ride the
27
+ * session cookie. Minted once per deploy, before anything is created, so a
28
+ * missing session fails the whole attempt cleanly.
29
+ */
30
+ async getToken() {
31
+ const t = await this.getUploadToken();
32
+ if (!t) throw new l();
33
+ return t;
34
+ }
35
+ /**
36
+ * Two proxied calls: create the site in the visitor's account, then the
37
+ * deploy on it. Mapped into the anonymous `DropResponse` shape so
38
+ * `deployFiles` runs unchanged over either client.
39
+ */
40
+ async createDeploy(t, s) {
41
+ const i = this.accountSlug ? `/${this.accountSlug}/sites` : "/sites", o = await fetch(`${this.proxyBase}${i}`, {
42
+ method: "POST",
43
+ credentials: "include",
44
+ headers: { "Content-Type": "application/json" },
45
+ body: JSON.stringify({ created_via: "drop" })
46
+ });
47
+ if (o.status === 401 || o.status === 403) throw new l();
48
+ if (!o.ok) throw new y(o.status);
49
+ const a = await o.json(), e = await fetch(`${this.proxyBase}/sites/${a.id}/deploys`, {
50
+ method: "POST",
51
+ credentials: "include",
52
+ headers: { "Content-Type": "application/json" },
53
+ body: JSON.stringify({ files: t, deploy_source: "drop" })
54
+ });
55
+ if (!e.ok) throw d("deploy create", e.status);
56
+ const c = await e.json();
57
+ return {
58
+ id: a.id,
59
+ deploy_id: c.id,
60
+ subdomain: a.name,
61
+ required: c.required ?? []
62
+ };
63
+ }
64
+ async uploadFile(t, s, i, o) {
65
+ const a = await fetch(`${this.apiBase}/deploys/${t}/files${s}`, {
66
+ method: "PUT",
67
+ headers: { "Content-Type": "application/octet-stream", Authorization: `Bearer ${o}` },
68
+ body: i
69
+ });
70
+ if (!a.ok) throw d(`upload ${s}`, a.status);
71
+ }
72
+ /**
73
+ * Poll the deploy through the proxy until it settles — the session cookie
74
+ * outlives the short-lived upload bearer, so a slow deploy can't expire its
75
+ * own poll. Resolve on `ready`, throw on `error`/`rejected`, throw on timeout.
76
+ */
77
+ async waitUntilReady(t, s) {
78
+ const i = this.timeoutMs / this.pollIntervalMs;
79
+ for (let o = 0; o < i; o++) {
80
+ const a = await fetch(`${this.proxyBase}/deploys/${t.deploy_id}`, {
81
+ credentials: "include"
82
+ });
83
+ if (a.ok) {
84
+ const e = await a.json();
85
+ if (e.state && T.has(e.state)) {
86
+ if (e.state === "ready") return;
87
+ throw new Error(
88
+ e.error_message ? `deploy failed: ${e.error_message}` : "deploy failed"
89
+ );
90
+ }
91
+ }
92
+ await new Promise((e) => setTimeout(e, this.pollIntervalMs));
93
+ }
94
+ throw new Error("Deploy did not become ready in time");
95
+ }
96
+ }
97
+ export {
98
+ g as AuthenticatedDropClient
99
+ };