@netlify/spark-ui 1.31.0-alpha.2 → 1.31.0-alpha.4

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 (94) hide show
  1. package/dist/components/index.d.ts +2 -2
  2. package/dist/components/preact/DropZone/DropZone.d.ts +28 -2
  3. package/dist/components/preact/DropZone/DropZone.js +58 -51
  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 +25 -23
  7. package/dist/components/preact/DropZone/useDropDeploy.d.ts +11 -6
  8. package/dist/components/preact/DropZone/useDropDeploy.js +183 -129
  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 +25 -19
  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 +55 -0
  16. package/dist/drop-core/authedApi.js +35 -0
  17. package/dist/drop-core/authedApi.test.d.ts +1 -0
  18. package/dist/drop-core/authedDropClient.d.ts +100 -0
  19. package/dist/drop-core/authedDropClient.js +109 -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 +32 -26
  22. package/dist/drop-core/buildClient.js +84 -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}/deploy.d.ts +7 -0
  28. package/dist/drop-core/deploy.js +53 -0
  29. package/dist/drop-core/deploy.test.d.ts +1 -0
  30. package/dist/drop-core/detectBuild.test.d.ts +1 -0
  31. package/dist/drop-core/digest.test.d.ts +1 -0
  32. package/dist/{components/preact/DropZone/drop-core → drop-core}/errors.js +1 -1
  33. package/dist/drop-core/errors.test.d.ts +1 -0
  34. package/dist/drop-core/fileTypes.test.d.ts +1 -0
  35. package/dist/{components/preact/DropZone/drop-core → drop-core}/index.d.ts +3 -0
  36. package/dist/drop-core/index.js +51 -0
  37. package/dist/{components/preact/DropZone/drop-core → drop-core}/readFiles.js +2 -2
  38. package/dist/drop-core/readFiles.test.d.ts +1 -0
  39. package/dist/{components/preact/DropZone/drop-core → drop-core}/types.d.ts +10 -0
  40. package/dist/{components/preact/DropZone/drop-core → drop-core}/zip.js +1 -1
  41. package/dist/drop-core/zip.test.d.ts +1 -0
  42. package/package.json +4 -1
  43. package/packages/components/index.ts +19 -0
  44. package/packages/components/preact/DropZone/DropZone.test.ts +71 -0
  45. package/packages/components/preact/DropZone/DropZone.tsx +32 -2
  46. package/packages/components/preact/DropZone/index.tsx +14 -3
  47. package/packages/components/preact/DropZone/useDropDeploy.test.ts +376 -0
  48. package/packages/components/preact/DropZone/useDropDeploy.ts +305 -134
  49. package/packages/components/preact/index.ts +19 -2
  50. package/packages/drop-core/analytics.test.ts +17 -0
  51. package/packages/drop-core/analytics.ts +122 -0
  52. package/packages/drop-core/authedApi.test.ts +100 -0
  53. package/packages/drop-core/authedApi.ts +94 -0
  54. package/packages/drop-core/authedDropClient.test.ts +196 -0
  55. package/packages/drop-core/authedDropClient.ts +210 -0
  56. package/packages/drop-core/buildClient.test.ts +112 -0
  57. package/packages/{components/preact/DropZone/drop-core → drop-core}/buildClient.ts +55 -70
  58. package/packages/drop-core/buildStash.test.ts +85 -0
  59. package/packages/drop-core/buildStash.ts +185 -0
  60. package/packages/drop-core/deploy.test.ts +135 -0
  61. package/packages/{components/preact/DropZone/drop-core → drop-core}/deploy.ts +18 -4
  62. package/packages/drop-core/detectBuild.test.ts +139 -0
  63. package/packages/drop-core/digest.test.ts +24 -0
  64. package/packages/drop-core/errors.test.ts +69 -0
  65. package/packages/{components/preact/DropZone/drop-core → drop-core}/errors.ts +1 -1
  66. package/packages/drop-core/fileTypes.test.ts +37 -0
  67. package/packages/{components/preact/DropZone/drop-core → drop-core}/index.ts +3 -0
  68. package/packages/drop-core/readFiles.test.ts +125 -0
  69. package/packages/{components/preact/DropZone/drop-core → drop-core}/types.ts +12 -6
  70. package/packages/drop-core/zip.test.ts +92 -0
  71. package/dist/components/preact/DropZone/drop-core/authedDropClient.d.ts +0 -77
  72. package/dist/components/preact/DropZone/drop-core/authedDropClient.js +0 -97
  73. package/dist/components/preact/DropZone/drop-core/buildClient.js +0 -92
  74. package/dist/components/preact/DropZone/drop-core/deploy.js +0 -48
  75. package/dist/components/preact/DropZone/drop-core/index.js +0 -36
  76. package/packages/components/preact/DropZone/drop-core/authedDropClient.ts +0 -185
  77. /package/dist/{components/preact/DropZone/drop-core → drop-core}/client.d.ts +0 -0
  78. /package/dist/{components/preact/DropZone/drop-core → drop-core}/client.js +0 -0
  79. /package/dist/{components/preact/DropZone/drop-core → drop-core}/detectBuild.d.ts +0 -0
  80. /package/dist/{components/preact/DropZone/drop-core → drop-core}/detectBuild.js +0 -0
  81. /package/dist/{components/preact/DropZone/drop-core → drop-core}/digest.d.ts +0 -0
  82. /package/dist/{components/preact/DropZone/drop-core → drop-core}/digest.js +0 -0
  83. /package/dist/{components/preact/DropZone/drop-core → drop-core}/errors.d.ts +0 -0
  84. /package/dist/{components/preact/DropZone/drop-core → drop-core}/fileTypes.d.ts +0 -0
  85. /package/dist/{components/preact/DropZone/drop-core → drop-core}/fileTypes.js +0 -0
  86. /package/dist/{components/preact/DropZone/drop-core → drop-core}/readFiles.d.ts +0 -0
  87. /package/dist/{components/preact/DropZone/drop-core → drop-core}/types.js +0 -0
  88. /package/dist/{components/preact/DropZone/drop-core → drop-core}/zip.d.ts +0 -0
  89. /package/packages/{components/preact/DropZone/drop-core → drop-core}/client.ts +0 -0
  90. /package/packages/{components/preact/DropZone/drop-core → drop-core}/detectBuild.ts +0 -0
  91. /package/packages/{components/preact/DropZone/drop-core → drop-core}/digest.ts +0 -0
  92. /package/packages/{components/preact/DropZone/drop-core → drop-core}/fileTypes.ts +0 -0
  93. /package/packages/{components/preact/DropZone/drop-core → drop-core}/readFiles.ts +0 -0
  94. /package/packages/{components/preact/DropZone/drop-core → drop-core}/zip.ts +0 -0
@@ -1,185 +0,0 @@
1
- import { apiError, type DropClient } from './client';
2
- import type { BuildSite } from './buildClient';
3
- import type { Digest, DropResponse } from './types';
4
- import { NotAuthenticatedError, SiteCreateError } from './errors';
5
-
6
- export interface AuthenticatedDropClientConfig {
7
- /**
8
- * API base for every call. Requests go straight to `api.netlify.com`, whose
9
- * `/api/*` CORS policy allows any origin, rather than through a consumer's
10
- * access-control rewrite — that proxy allowlists paths per origin, and site
11
- * and deploy creation are not on the list for netlify.com (they 400 with
12
- * `x-debug: request origin not allowed to call endpoint`).
13
- */
14
- apiBase?: string;
15
- /**
16
- * Mint a short-lived bearer token authorizing the calls. On netlify.com this
17
- * is `GET /access-control/generate-access-control-token`, whose
18
- * `accessControlToken` the API decrypts back into the user's access token.
19
- * Resolving `null` means "no session" and surfaces as `NotAuthenticatedError`.
20
- */
21
- getUploadToken: () => Promise<string | null>;
22
- /** Create the site in this account. Omit to use the user's default account. */
23
- accountSlug?: string;
24
- /** How often to re-read the deploy while waiting. Default 1s. */
25
- pollIntervalMs?: number;
26
- /** How long to wait before giving up on the deploy. Default 10 minutes. */
27
- timeoutMs?: number;
28
- }
29
-
30
- // Same defaults as AuthenticatedBuildClient — the ~15 lines shared with it
31
- // (constants + site create) are deliberately duplicated rather than extracted,
32
- // to avoid churn on the in-review build-client branch. Worth unifying once both
33
- // have landed.
34
- const API_BASE = 'https://api.netlify.com/api/v1';
35
- const POLL_INTERVAL_MS = 1000;
36
- const TIMEOUT_MS = 10 * 60 * 1000;
37
-
38
- /** Deploy states that end the wait — `ready` succeeds, the rest are failures. */
39
- const TERMINAL_STATES = new Set(['ready', 'error', 'rejected']);
40
-
41
- interface DeployState {
42
- state?: string;
43
- error_message?: string;
44
- }
45
-
46
- /** Response body of `POST /sites/{id}/deploys` — the slice the drop flow needs. */
47
- interface SiteDeployResponse {
48
- /** deploy id (BSON) */
49
- id: string;
50
- /** SHAs the backend does NOT yet have — only these get uploaded */
51
- required?: string[];
52
- }
53
-
54
- /**
55
- * Authenticated drop client for logged-in visitors. Unlike `AnonymousDropClient`
56
- * — which creates an account-less site the visitor must later claim — this
57
- * deploys straight into the visitor's account via the sites/deploys API,
58
- * mirroring what app.netlify.com does for a logged-in drag-and-drop deploy.
59
- * There is no claim step, so the caller should hand off to the site's dashboard
60
- * page rather than the claim page.
61
- *
62
- * Every request goes direct to `api.netlify.com` with the short-lived bearer
63
- * from `getUploadToken()`. Routing the JSON calls through a consumer's
64
- * access-control rewrite instead (cookie session, no token in the browser)
65
- * would be the tighter design, but that proxy allowlists paths per origin and
66
- * site/deploy creation isn't allowed from netlify.com — it 400s before
67
- * reaching the API. Revisit if that allowlist gains these paths.
68
- *
69
- * Error-mapping invariant: only pre-mutation failures — a null upload token, or
70
- * a 401/403 creating the site — throw `NotAuthenticatedError`, which is the
71
- * signal the drop flow may safely retry anonymously. Once the site exists, an
72
- * auth failure throws a plain status-carrying error instead: falling back at
73
- * that point would strand an empty site in the account *and* deploy a duplicate
74
- * anonymously.
75
- */
76
- export class AuthenticatedDropClient implements DropClient {
77
- private apiBase: string;
78
- private getUploadToken: () => Promise<string | null>;
79
- private accountSlug?: string;
80
- private pollIntervalMs: number;
81
- private timeoutMs: number;
82
-
83
- constructor({
84
- apiBase = API_BASE,
85
- getUploadToken,
86
- accountSlug,
87
- pollIntervalMs = POLL_INTERVAL_MS,
88
- timeoutMs = TIMEOUT_MS,
89
- }: AuthenticatedDropClientConfig) {
90
- this.apiBase = apiBase;
91
- this.getUploadToken = getUploadToken;
92
- this.accountSlug = accountSlug;
93
- this.pollIntervalMs = pollIntervalMs;
94
- this.timeoutMs = timeoutMs;
95
- }
96
-
97
- /**
98
- * The token here is the upload bearer, not a drop token — it authorizes every
99
- * call this client makes. Minted once per deploy, before anything is created,
100
- * so a missing session fails the whole attempt cleanly.
101
- */
102
- async getToken(): Promise<string> {
103
- const token = await this.getUploadToken();
104
- if (!token) throw new NotAuthenticatedError();
105
- return token;
106
- }
107
-
108
- /**
109
- * Two calls: create the site in the visitor's account, then the deploy on it.
110
- * Mapped into the anonymous `DropResponse` shape so `deployFiles` runs
111
- * unchanged over either client.
112
- */
113
- async createDeploy(files: Digest, token: string): Promise<DropResponse> {
114
- const path = this.accountSlug ? `/${this.accountSlug}/sites` : '/sites';
115
- const siteRes = await fetch(`${this.apiBase}${path}`, {
116
- method: 'POST',
117
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
118
- body: JSON.stringify({ created_via: 'drop' }),
119
- });
120
- // A logged-out visitor 401s here, before anything is created — the one
121
- // point an anonymous fallback is still safe (see the class doc).
122
- if (siteRes.status === 401 || siteRes.status === 403) throw new NotAuthenticatedError();
123
- if (!siteRes.ok) throw new SiteCreateError(siteRes.status);
124
- const site: BuildSite = await siteRes.json();
125
-
126
- const deployRes = await fetch(`${this.apiBase}/sites/${site.id}/deploys`, {
127
- method: 'POST',
128
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
129
- body: JSON.stringify({ files, deploy_source: 'drop' }),
130
- });
131
- if (!deployRes.ok) throw apiError('deploy create', deployRes.status);
132
- const deploy: SiteDeployResponse = await deployRes.json();
133
-
134
- return {
135
- id: site.id,
136
- deploy_id: deploy.id,
137
- subdomain: site.name,
138
- required: deploy.required ?? [],
139
- };
140
- }
141
-
142
- async uploadFile(
143
- deployId: string,
144
- path: string,
145
- content: ArrayBuffer,
146
- token: string
147
- ): Promise<void> {
148
- // PUT (not POST) so paths with "&" etc. work.
149
- const res = await fetch(`${this.apiBase}/deploys/${deployId}/files${path}`, {
150
- method: 'PUT',
151
- headers: { 'Content-Type': 'application/octet-stream', Authorization: `Bearer ${token}` },
152
- body: content,
153
- });
154
- if (!res.ok) throw apiError(`upload ${path}`, res.status);
155
- }
156
-
157
- /**
158
- * Poll until the deploy settles: resolve on `ready`, throw on
159
- * `error`/`rejected`, throw on timeout. Reuses the bearer minted at the start
160
- * of the deploy, so a deploy that outlives the token's TTL will start seeing
161
- * 401s here — those are ignored like any other non-ok read and the loop runs
162
- * to its timeout rather than failing fast. Acceptable while static deploys
163
- * finish in seconds; re-mint per poll if that stops being true.
164
- */
165
- async waitUntilReady(deploy: DropResponse, token: string): Promise<void> {
166
- const deadline = this.timeoutMs / this.pollIntervalMs;
167
- for (let i = 0; i < deadline; i++) {
168
- const res = await fetch(`${this.apiBase}/deploys/${deploy.deploy_id}`, {
169
- headers: { Authorization: `Bearer ${token}` },
170
- });
171
- if (res.ok) {
172
- const state: DeployState = await res.json();
173
- if (state.state && TERMINAL_STATES.has(state.state)) {
174
- if (state.state === 'ready') return;
175
- throw new Error(
176
- state.error_message ? `deploy failed: ${state.error_message}` : 'deploy failed'
177
- );
178
- }
179
- }
180
- await new Promise(r => setTimeout(r, this.pollIntervalMs));
181
- }
182
- // Same message the anonymous client uses, so humanizeDropError maps it.
183
- throw new Error('Deploy did not become ready in time');
184
- }
185
- }