tempest-react-sdk 0.82.0 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +6 -6
  2. package/bin/lib/openapi/generate.hostile.test.mjs +91 -0
  3. package/bin/lib/openapi/generate.mjs +42 -3
  4. package/dist/auth/create-tempest-auth.cjs +1 -1
  5. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  6. package/dist/auth/create-tempest-auth.js +135 -132
  7. package/dist/auth/create-tempest-auth.js.map +1 -1
  8. package/dist/http/resumable-upload.cjs +1 -1
  9. package/dist/http/resumable-upload.cjs.map +1 -1
  10. package/dist/http/resumable-upload.js +124 -75
  11. package/dist/http/resumable-upload.js.map +1 -1
  12. package/dist/http/upload-with-progress.cjs +1 -1
  13. package/dist/http/upload-with-progress.cjs.map +1 -1
  14. package/dist/http/upload-with-progress.js +100 -44
  15. package/dist/http/upload-with-progress.js.map +1 -1
  16. package/dist/query/offline-persistence.cjs +1 -1
  17. package/dist/query/offline-persistence.cjs.map +1 -1
  18. package/dist/query/offline-persistence.js +57 -44
  19. package/dist/query/offline-persistence.js.map +1 -1
  20. package/dist/sw/cache.cjs +1 -1
  21. package/dist/sw/cache.cjs.map +1 -1
  22. package/dist/sw/cache.js +82 -32
  23. package/dist/sw/cache.js.map +1 -1
  24. package/dist/sw.d.ts +21 -1
  25. package/dist/tempest-react-sdk.d.ts +90 -6
  26. package/dist/vite/base-url.cjs +1 -1
  27. package/dist/vite/base-url.cjs.map +1 -1
  28. package/dist/vite/base-url.js +5 -1
  29. package/dist/vite/base-url.js.map +1 -1
  30. package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
  31. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  32. package/dist/vite/tempest-pwa-dev-sw.js +96 -31
  33. package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
  34. package/dist/vite/tempest-pwa-manifest.cjs +2 -2
  35. package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
  36. package/dist/vite/tempest-pwa-manifest.js +32 -16
  37. package/dist/vite/tempest-pwa-manifest.js.map +1 -1
  38. package/dist/vite.cjs +1 -1
  39. package/dist/vite.d.ts +63 -12
  40. package/dist/vite.js +4 -4
  41. package/package.json +1 -1
@@ -5383,7 +5383,7 @@ export declare interface CreateRefreshQueueOptions {
5383
5383
  * | --- | --- | --- |
5384
5384
  * | Create | `POST {endpoint}` + `Upload-Length`, `Upload-Metadata`, `Idempotency-Key` | `201` + `Location` (the upload URL, absolute or endpoint-relative) |
5385
5385
  * | Probe | `HEAD {uploadUrl}` | `200`/`204` + `Upload-Offset` |
5386
- * | Write | `PATCH {uploadUrl}` + `Upload-Offset`, `Content-Type: application/offset+octet-stream`, chunk body | `204` + the new `Upload-Offset`; `409` when the offset does not match |
5386
+ * | Write | `PATCH {uploadUrl}` + `Upload-Offset`, `Content-Type: application/offset+octet-stream`, chunk body | `204` + the new `Upload-Offset`, past the one sent; `409` when the offset does not match |
5387
5387
  * | Discard | `DELETE {uploadUrl}` | `204` |
5388
5388
  *
5389
5389
  * ## The failure that actually happens
@@ -9673,7 +9673,27 @@ export declare type InstallOutcome = "accepted" | "dismissed" | "unsupported";
9673
9673
  * - on `fetch`, serves precached assets cache-first and falls back to the
9674
9674
  * `navigateFallback` document for offline navigations (SPA routing).
9675
9675
  *
9676
- * Same-origin only. Register this LAST, after any {@link installRuntimeCache}.
9676
+ * Survives the browser stopping and restarting the worker, which it does
9677
+ * whenever the worker sits idle and which never repeats `install`. Nothing the
9678
+ * fetch handler needs lives only in memory: `install` and `activate` record, in
9679
+ * an entry inside the versioned cache, when that version was installed and
9680
+ * activated, and a restarted worker reads back the activated version and its
9681
+ * pathnames from Cache Storage. Before 0.83.0 the cache name and the precached
9682
+ * paths were module variables filled only by `install`, so after the first
9683
+ * restart offline navigations failed with "offline and no cached app shell",
9684
+ * precached assets went to the network, and an `activate` in a run without
9685
+ * `install` deleted every precache, the current one included (#572).
9686
+ *
9687
+ * `activate` deletes the other `${cacheName}-*` caches only once it knows which
9688
+ * version it is activating; when no version recorded an install it deletes
9689
+ * nothing. In the first request after a restart, while the worker is still
9690
+ * reading which paths are precached, a same-origin `GET` that a runtime route
9691
+ * did not answer is answered with `fetch(request)` when it is not precached —
9692
+ * the decision to call `respondWith` cannot wait for Cache Storage. From then on
9693
+ * a non-precached request is left to the browser, as before.
9694
+ *
9695
+ * Same-origin only. Register this LAST, after any {@link installRuntimeCache}:
9696
+ * a request a runtime route answered is never answered again here.
9677
9697
  */
9678
9698
  export declare function installPrecache(options?: InstallPrecacheOptions): void;
9679
9699
 
@@ -12220,6 +12240,16 @@ export declare interface OfflineQueryPersistenceOptions {
12220
12240
  * the principal after a `/me` round trip would otherwise wipe its own cache
12221
12241
  * on every cold start. The next write replaces it.
12222
12242
  *
12243
+ * The handle is bound to the first signed-in owner it writes or restores.
12244
+ * Once bound, a write under another owner — nobody, because the token
12245
+ * expired without `clear()`, or a second account on the same tab — deletes
12246
+ * the snapshot instead of stamping the bound owner's cached responses with
12247
+ * the new owner (#563); the same owner coming back writes again. Writes
12248
+ * before anyone signs in stay anonymous and are restored while nobody is,
12249
+ * so an app that persists logged-out data keeps doing so. An anonymous
12250
+ * snapshot written by 0.82.0 is not restored (it may hold an expired
12251
+ * session's cache): one cold start, once.
12252
+ *
12223
12253
  * Read synchronously. A non-string, non-number value (the `Promise` of an
12224
12254
  * `async` getter) or a getter that throws reads as nobody. Omit it to restore
12225
12255
  * every snapshot, as before 0.82.0.
@@ -14804,6 +14834,13 @@ export declare interface ResumableUpload {
14804
14834
  /**
14805
14835
  * Stop for good.
14806
14836
  *
14837
+ * A later `start()`/`resume()` on the same handle attaches as a new handle
14838
+ * would (since 0.83.0): it resumes the stored record only when the current
14839
+ * owner stamped it, and otherwise creates a new upload with a fresh
14840
+ * `Idempotency-Key`. Until then a token-bound handle resumed its own upload
14841
+ * with whatever token was current — after an account switch, the next
14842
+ * account's (#561).
14843
+ *
14807
14844
  * @param options - `discard: true` also sends `DELETE` (tus termination) and
14808
14845
  * forgets the persisted record, so the next `start()` uploads from zero.
14809
14846
  */
@@ -14834,6 +14871,16 @@ export declare interface ResumableUploadOptions {
14834
14871
  * Extra headers on every request inside the credential scope — the
14835
14872
  * `endpoint`'s origin plus {@link trustedOrigins}. A `Location` on another
14836
14873
  * host gets only the tus headers, never these (since 0.82.0).
14874
+ *
14875
+ * A request that carries them never follows a redirect (since 0.83.0): it
14876
+ * goes out over `fetch` with `redirect: "manual"`, and a redirect rejects
14877
+ * with a `TempestApiError` of `code: "REDIRECT_REFUSED"`. An
14878
+ * `XMLHttpRequest` follows every redirect, and these headers went with it
14879
+ * to the other origin (#564). The cost: `fetch` reports no upload
14880
+ * progress, so `onProgress` then ticks once per confirmed chunk — lower
14881
+ * `chunkSize` for a finer bar, or send the credential through `getToken`,
14882
+ * which keeps the `XMLHttpRequest` (the browser strips `Authorization` on
14883
+ * a cross-origin redirect).
14837
14884
  */
14838
14885
  headers?: Record<string, string>;
14839
14886
  /** Returns the current bearer token, read before each request. */
@@ -17326,10 +17373,15 @@ export declare interface TempestAuth<TUser, TCredentials> {
17326
17373
  * on their own from `api`, and `createResumableUpload` from `getToken`;
17327
17374
  * pass it as `sessionKey` or `getPrincipal` anywhere else.
17328
17375
  *
17329
- * A session that has no lineage — persisted before lineages existed, or
17330
- * restored from a refresh cookie without a `login()` in this storage —
17331
- * answers `"unlineaged"`. Replacing it still takes a
17332
- * `login()`, which mints one, so the key still changes on a switch.
17376
+ * A session restored by `refresh()` with none installed — the boot
17377
+ * restore from a refresh cookie, an OAuth callback — is a new sign-in and
17378
+ * gets a lineage of its own, like `login()` (#561). A session persisted
17379
+ * before lineages existed has none in storage; its key is minted by this
17380
+ * preset for as long as that session lasts, so it survives the session's
17381
+ * rotations but no other session ever shares it. Until 0.82.0 both cases
17382
+ * answered one constant for every account, and an upload bound by
17383
+ * `getToken: auth.getToken` was resumed by the next account restored the
17384
+ * same way.
17333
17385
  */
17334
17386
  sessionKey: () => string | null;
17335
17387
  /**
@@ -18463,6 +18515,11 @@ export declare interface UploadProgressEvent {
18463
18515
  * back to `XMLHttpRequest`. It mirrors the error contract used by
18464
18516
  * {@link createApiClient}: non-2xx responses reject with a `TempestApiError`.
18465
18517
  *
18518
+ * An `XMLHttpRequest` cannot refuse a redirect; see
18519
+ * {@link UploadWithProgressOptions.redirect} for what follows one, what the
18520
+ * helper still refuses, and the `fetch` path that trades progress for never
18521
+ * following.
18522
+ *
18466
18523
  * @returns The parsed JSON response, or the raw text when the response is not JSON.
18467
18524
  */
18468
18525
  export declare function uploadWithProgress<T = unknown>(options: UploadWithProgressOptions): Promise<T>;
@@ -18516,6 +18573,33 @@ export declare interface UploadWithProgressOptions {
18516
18573
  * Defaults to a generated id. Return an empty string to disable.
18517
18574
  */
18518
18575
  requestId?: () => string;
18576
+ /**
18577
+ * What to do when the server answers with a redirect. Default `"follow"`.
18578
+ *
18579
+ * - `"follow"` — the `XMLHttpRequest` path, with byte-level progress. The
18580
+ * browser follows every redirect and offers no way to stop it: measured
18581
+ * in Chrome 150 (#570), a same-origin upload answered `301`/`302`/`303`/
18582
+ * `307`/`308` towards another origin delivered `X-Api-Key` from
18583
+ * {@link headers} and the SDK's own `X-CSRF-Token` there on all five, and
18584
+ * the body on `307`/`308`; only `Authorization` was stripped. What this
18585
+ * path does enforce (since 0.83.0): an answer from another origin than
18586
+ * the one the request was sent to is never the result — it rejects with
18587
+ * `code: "REDIRECT_REFUSED"`, so that host cannot hand the app a URL to
18588
+ * store. Until 0.82.0 it resolved with the other host's body.
18589
+ * - `"error"` — goes out over `fetch` with `redirect: "manual"`, and a
18590
+ * redirect rejects with `code: "REDIRECT_REFUSED"` before anything is
18591
+ * sent to the other host. The cost: `fetch` reports no upload progress,
18592
+ * so `onProgress` fires once, when the server confirms the whole body. A
18593
+ * streamed `fetch` body would report progress, but Chrome refuses one
18594
+ * over HTTP/1.1 (`TypeError: Failed to fetch`, measured) and Firefox and
18595
+ * Safari do not support it.
18596
+ *
18597
+ * Use `"error"` when {@link headers} carry a credential (an `X-Api-Key`) and
18598
+ * the server behind `url` could redirect; sending the bearer through
18599
+ * {@link getToken} keeps `"follow"` safe for it, because the browser strips
18600
+ * `Authorization` on a cross-origin redirect.
18601
+ */
18602
+ redirect?: "follow" | "error";
18519
18603
  }
18520
18604
 
18521
18605
  /**
@@ -1,2 +1,2 @@
1
- function e(e){return e.endsWith(`/`)?e:`${e}/`}exports.basePrefix=e;
1
+ function e(e){return e.endsWith(`/`)?e:`${e}/`}function t(t,n){let r=e(t),i=n.split(/[?#]/)[0];return r!==`/`&&i.startsWith(r)?i.slice(r.length):i.replace(/^\/+/,``)}exports.basePrefix=e,exports.toOutputFile=t;
2
2
  //# sourceMappingURL=base-url.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-url.cjs","names":["basePrefix","base"],"sources":["../../src/vite/base-url.ts"],"sourcesContent":["/**\n * The resolved Vite `base` as a prefix that always ends in a slash.\n *\n * One rule, three call sites: the manifest prefixes the URLs it precaches, and\n * the dev middleware matches the paths the browser asks for. They have to agree\n * by definition — when they drift, dev serves `/app/sw.js` while the manifest\n * precached `/sw.js`, and the symptom only appears on a subpath deploy, which is\n * the least exercised configuration there is.\n *\n * @param base - Vite's resolved `base`, with or without a trailing slash.\n * @returns The same base, guaranteed to end in `/`.\n */\nexport function basePrefix(base: string): string {\n return base.endsWith(\"/\") ? base : `${base}/`;\n}\n"],"mappings":"AAYA,SAAgBA,EAAW,EAAsB,CAC7C,OAAOC,EAAK,SAAS,GAAG,EAAIA,EAAO,GAAGA,EAAK,EAC/C,C"}
1
+ {"version":3,"file":"base-url.cjs","names":["basePrefix","base","toOutputFile","prefix","path","url"],"sources":["../../src/vite/base-url.ts"],"sourcesContent":["/**\n * The resolved Vite `base` as a prefix that always ends in a slash.\n *\n * One rule, three call sites: the manifest prefixes the URLs it precaches, and\n * the dev middleware matches the paths the browser asks for. They have to agree\n * by definition — when they drift, dev serves `/app/sw.js` while the manifest\n * precached `/sw.js`, and the symptom only appears on a subpath deploy, which is\n * the least exercised configuration there is.\n *\n * @param base - Vite's resolved `base`, with or without a trailing slash.\n * @returns The same base, guaranteed to end in `/`.\n */\nexport function basePrefix(base: string): string {\n return base.endsWith(\"/\") ? base : `${base}/`;\n}\n\n/**\n * The output file a root-absolute URL resolves to, relative to `outDir`.\n *\n * Emitted files are named relative to the build output, while the options that\n * point at them (`swUrl`, `additionalUrls`) are URLs that may or may not carry\n * the base. Both spellings land on the same file, so a check that a URL was\n * emitted does not depend on which one the config used.\n *\n * @param base - Vite's resolved `base`.\n * @param url - A root-absolute URL, with or without the base.\n * @returns The path of the file under `outDir`, without a leading slash.\n */\nexport function toOutputFile(base: string, url: string): string {\n const prefix = basePrefix(base);\n const path = url.split(/[?#]/)[0];\n if (prefix !== \"/\" && path.startsWith(prefix)) return path.slice(prefix.length);\n return path.replace(/^\\/+/, \"\");\n}\n"],"mappings":"AAYA,SAAgBA,EAAW,EAAsB,CAC7C,OAAOC,EAAK,SAAS,GAAG,EAAIA,EAAO,GAAGA,EAAK,EAC/C,CAcA,SAAgBC,EAAa,EAAc,EAAqB,CAC5D,IAAMC,EAASH,EAAWC,CAAI,EACxBG,EAAOC,EAAI,MAAM,MAAM,CAAC,CAAC,GAE/B,OADIF,IAAW,KAAOC,EAAK,WAAWD,CAAM,EAAUC,EAAK,MAAMD,EAAO,MAAM,EACvEC,EAAK,QAAQ,OAAQ,EAAE,CAClC,C"}
@@ -2,7 +2,11 @@
2
2
  function e(e) {
3
3
  return e.endsWith("/") ? e : `${e}/`;
4
4
  }
5
+ function t(t, n) {
6
+ let r = e(t), i = n.split(/[?#]/)[0];
7
+ return r !== "/" && i.startsWith(r) ? i.slice(r.length) : i.replace(/^\/+/, "");
8
+ }
5
9
  //#endregion
6
- export { e as basePrefix };
10
+ export { e as basePrefix, t as toOutputFile };
7
11
 
8
12
  //# sourceMappingURL=base-url.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-url.js","names":["basePrefix","base"],"sources":["../../src/vite/base-url.ts"],"sourcesContent":["/**\n * The resolved Vite `base` as a prefix that always ends in a slash.\n *\n * One rule, three call sites: the manifest prefixes the URLs it precaches, and\n * the dev middleware matches the paths the browser asks for. They have to agree\n * by definition — when they drift, dev serves `/app/sw.js` while the manifest\n * precached `/sw.js`, and the symptom only appears on a subpath deploy, which is\n * the least exercised configuration there is.\n *\n * @param base - Vite's resolved `base`, with or without a trailing slash.\n * @returns The same base, guaranteed to end in `/`.\n */\nexport function basePrefix(base: string): string {\n return base.endsWith(\"/\") ? base : `${base}/`;\n}\n"],"mappings":";AAYA,SAAgBA,EAAW,GAAsB;CAC7C,OAAOC,EAAK,SAAS,GAAG,IAAIA,IAAO,GAAGA,EAAK;AAC/C"}
1
+ {"version":3,"file":"base-url.js","names":["basePrefix","base","toOutputFile","prefix","path","url"],"sources":["../../src/vite/base-url.ts"],"sourcesContent":["/**\n * The resolved Vite `base` as a prefix that always ends in a slash.\n *\n * One rule, three call sites: the manifest prefixes the URLs it precaches, and\n * the dev middleware matches the paths the browser asks for. They have to agree\n * by definition — when they drift, dev serves `/app/sw.js` while the manifest\n * precached `/sw.js`, and the symptom only appears on a subpath deploy, which is\n * the least exercised configuration there is.\n *\n * @param base - Vite's resolved `base`, with or without a trailing slash.\n * @returns The same base, guaranteed to end in `/`.\n */\nexport function basePrefix(base: string): string {\n return base.endsWith(\"/\") ? base : `${base}/`;\n}\n\n/**\n * The output file a root-absolute URL resolves to, relative to `outDir`.\n *\n * Emitted files are named relative to the build output, while the options that\n * point at them (`swUrl`, `additionalUrls`) are URLs that may or may not carry\n * the base. Both spellings land on the same file, so a check that a URL was\n * emitted does not depend on which one the config used.\n *\n * @param base - Vite's resolved `base`.\n * @param url - A root-absolute URL, with or without the base.\n * @returns The path of the file under `outDir`, without a leading slash.\n */\nexport function toOutputFile(base: string, url: string): string {\n const prefix = basePrefix(base);\n const path = url.split(/[?#]/)[0];\n if (prefix !== \"/\" && path.startsWith(prefix)) return path.slice(prefix.length);\n return path.replace(/^\\/+/, \"\");\n}\n"],"mappings":";AAYA,SAAgBA,EAAW,GAAsB;CAC7C,OAAOC,EAAK,SAAS,GAAG,IAAIA,IAAO,GAAGA,EAAK;AAC/C;AAcA,SAAgBC,EAAa,GAAc,GAAqB;CAC5D,IAAMC,IAASH,EAAWC,CAAI,GACxBG,IAAOC,EAAI,MAAM,MAAM,CAAC,CAAC;CAE/B,OADIF,MAAW,OAAOC,EAAK,WAAWD,CAAM,IAAUC,EAAK,MAAMD,EAAO,MAAM,IACvEC,EAAK,QAAQ,QAAQ,EAAE;AAClC"}
@@ -1,2 +1,2 @@
1
- const e=require("./base-url.cjs");let t=require("node:path");function n(e){return async(t,n,r)=>{let i=(t.url??``).split(`?`)[0];if(e.matches(i,e.swUrl))try{let t=await e.buildWorker();n.setHeader(`Content-Type`,`application/javascript`),n.setHeader(`Service-Worker-Allowed`,`/`),n.setHeader(`Cache-Control`,`no-cache`),n.end(t)}catch(e){n.statusCode=500,n.end(`// SW dev build failed:\n// ${String(e)}`)}else e.matches(i,e.manifestUrl)?(n.setHeader(`Content-Type`,`application/json`),n.setHeader(`Cache-Control`,`no-cache`),n.end(JSON.stringify({version:`dev`,urls:[]}))):r()}}function r(r={}){let{swSrc:i=`src/sw.ts`,swUrl:a=`/sw.js`,manifestUrl:o=`/precache-manifest.json`,enabled:s=!0}=r,c=process.cwd(),l=`/`;function u(t,n){if(t===n)return!0;let r=e.basePrefix(l);return r!==`/`&&t===`${r}${n.replace(/^\//,``)}`}let d=null,f=null;async function p(){return d||(d=await(await import(`esbuild`)).context({entryPoints:[(0,t.resolve)(c,i)],bundle:!0,format:`iife`,platform:`browser`,target:`es2020`,write:!1,absWorkingDir:c,logLevel:`silent`}),d)}function m(){if(f)return f;let e=(async()=>{let e=(await(await p()).rebuild()).outputFiles?.[0]?.text;if(e===void 0)throw Error(`esbuild produced no service-worker output`);return e})();f=e;let t=()=>{f=null};return e.then(t,t),e}async function h(){let e=d;d=null,f=null,await e?.dispose()}return{name:`tempest-pwa-dev-sw`,apply:`serve`,async buildEnd(){await h()},configResolved(e){c=e.root??process.cwd(),l=e.base??`/`},configureServer(e){s&&(e.httpServer?.once(`close`,()=>void h()),e.middlewares.use(n({swUrl:a,manifestUrl:o,matches:u,buildWorker:m})))}}}exports.tempestPwaDevSw=r;
1
+ const e=require("./base-url.cjs");let t=require("node:path");var n=`tempest-pwa-dev-sw`;function r(e){return`/* tempest-precache-version: ${e} */\n`}function i(e,t){let n=e[t];if(n?.type!==`asset`)return;let r=typeof n.source==`string`?n.source:new TextDecoder().decode(n.source);try{let e=JSON.parse(r);return typeof e.version==`string`?e.version:void 0}catch{return}}function a(e){return async(t,n,r)=>{let i=(t.url??``).split(`?`)[0];if(e.matches(i,e.swUrl))try{let t=await e.buildWorker();n.setHeader(`Content-Type`,`application/javascript`),n.setHeader(`Service-Worker-Allowed`,`/`),n.setHeader(`Cache-Control`,`no-cache`),n.end(t)}catch(e){n.statusCode=500,n.end(`// SW dev build failed:\n// ${String(e)}`)}else e.matches(i,e.manifestUrl)?(n.setHeader(`Content-Type`,`application/json`),n.setHeader(`Cache-Control`,`no-cache`),n.end(JSON.stringify({version:`dev`,urls:[]}))):r()}}function o(o={}){let{swSrc:s=`src/sw.ts`,swUrl:c=`/sw.js`,manifestUrl:l=`/precache-manifest.json`,enabled:u=!0,build:d=!0}=o,f=process.cwd(),p=`/`,m=!1,h=`production`,g,_,v=!0,y=`warn`;function b(t,n){if(t===n)return!0;let r=e.basePrefix(p);return r!==`/`&&t===`${r}${n.replace(/^\//,``)}`}let x=null,S=null;async function C(){return x||(x=await(await import(`esbuild`)).context({entryPoints:[(0,t.resolve)(f,s)],bundle:!0,format:`iife`,platform:`browser`,target:`es2020`,write:!1,absWorkingDir:f,logLevel:`silent`}),x)}function w(){if(S)return S;let e=(async()=>{let e=(await(await C()).rebuild()).outputFiles?.[0]?.text;if(e===void 0)throw Error(`esbuild produced no service-worker output`);return e})();S=e;let t=()=>{S=null};return e.then(t,t),e}async function T(){let e=x;x=null,S=null,await e?.dispose()}async function E(){let e=await(await import(`vite`)).build({configFile:!1,root:f,mode:h,envDir:g,envPrefix:_,publicDir:!1,logLevel:y,build:{write:!1,emptyOutDir:!1,copyPublicDir:!1,sourcemap:!1,minify:v,lib:{entry:(0,t.resolve)(f,s),formats:[`iife`],name:`sw`,fileName:()=>`sw.js`}}}),n=Array.isArray(e)?e:[e];for(let{output:e=[]}of n){let t=e.find(e=>e.type===`chunk`&&e.isEntry);if(t?.code!==void 0)return t.code}throw Error(`the build produced no service-worker chunk`)}return{name:n,api:{swUrl:c,build:d},async buildEnd(){await T()},configResolved(e){f=e.root??process.cwd(),p=e.base??`/`,m=e.command===`build`&&!e.build?.ssr,h=e.mode??`production`,g=e.envDir,_=e.envPrefix,v=e.build?.minify??!0,y=e.logLevel===`silent`||e.logLevel===`error`?e.logLevel:`warn`},configureServer(e){u&&(e.httpServer?.once(`close`,()=>void T()),e.middlewares.use(a({swUrl:c,manifestUrl:l,matches:b,buildWorker:w})))},generateBundle:{order:`post`,async handler(t,n){if(!m||!d)return;let a;try{a=await E()}catch(e){this.error(`[tempest-pwa-dev-sw] could not bundle the service worker "${s}" into ${c}: ${e instanceof Error?e.message:String(e)}. Fix the entry, point \`swSrc\` at it, or pass \`build: false\` if another step bundles the worker.`)}let o=i(n,e.toOutputFile(p,l));this.emitFile({type:`asset`,fileName:e.toOutputFile(p,c),source:o===void 0?a:r(o)+a})}}}}exports.PWA_WORKER_PLUGIN_NAME=n,exports.precacheVersionBanner=r,exports.tempestPwaDevSw=o;
2
2
  //# sourceMappingURL=tempest-pwa-dev-sw.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tempest-pwa-dev-sw.cjs","names":["basePrefix","resolve","require_base_url","node_path","createDevSwMiddleware","url","req","deps","source","res","error","next","tempestPwaDevSw","options","root","base","matches","target","prefix","context","pending","ensureContext","swSrc","buildWorker","run","text","clear","disposeContext","current","config","enabled","server"],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * The dev middleware: serve the compiled worker, and a placeholder manifest.\n *\n * Extracted from the plugin so the request handling reads on its own — the\n * plugin body is then only wiring. The manifest is deliberately empty in dev:\n * precaching a set of URLs Vite rewrites on every change would serve stale\n * modules, and the worker's runtime routes still work without it.\n *\n * @param deps - The two URLs, the path matcher and the incremental build.\n * @returns A connect-style middleware.\n */\nfunction createDevSwMiddleware(deps: {\n swUrl: string;\n manifestUrl: string;\n matches: (url: string, target: string) => boolean;\n buildWorker: () => Promise<string>;\n}): (\n req: { url?: string },\n res: {\n statusCode?: number;\n setHeader: (name: string, value: string) => void;\n end: (body: string) => void;\n },\n next: () => void,\n) => Promise<void> {\n return async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (deps.matches(url, deps.swUrl)) {\n try {\n const source = await deps.buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (deps.matches(url, deps.manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n };\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild — through one incremental context, not a cold build per request —\n * and serves it as a classic worker, plus an empty `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(\n createDevSwMiddleware({ swUrl, manifestUrl, matches, buildWorker }),\n );\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"A,6DAsCA,SAASI,EAAsB,EAaZ,CACf,OAAO,MAAO,EAAK,EAAK,IAAS,CAC7B,IAAMC,GAAOC,EAAI,KAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,GAEvC,GAAIC,EAAK,QAAQF,EAAKE,EAAK,KAAK,EAC5B,GAAI,CACA,IAAMC,EAAS,MAAMD,EAAK,YAAY,EACtCE,EAAI,UAAU,eAAgB,wBAAwB,EACtDA,EAAI,UAAU,yBAA0B,GAAG,EAC3CA,EAAI,UAAU,gBAAiB,UAAU,EACzCA,EAAI,IAAID,CAAM,CAClB,OAASE,EAAO,CACZ,EAAI,WAAa,IACjBD,EAAI,IAAI,+BAA+B,OAAOC,CAAK,GAAG,CAC1D,MAIAH,EAAK,QAAQF,EAAKE,EAAK,WAAW,GAClCE,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,UAAU,gBAAiB,UAAU,EACzCA,EAAI,IAAI,KAAK,UAAU,CAAE,QAAS,MAAO,KAAM,CAAC,CAAE,CAAC,CAAC,GAIxDE,EAAK,CACT,CACJ,CAkBA,SAAgBC,EAAgB,EAAkC,CAAC,EAAsB,CACrF,GAAM,CACF,QAAQ,YACR,QAAQ,SACR,cAAc,0BACd,UAAU,IACVC,EAEAC,EAAO,QAAQ,IAAI,EACnBC,EAAO,IAUX,SAASC,EAAQ,EAAa,EAAyB,CACnD,GAAIX,IAAQY,EAAQ,MAAO,GAC3B,IAAMC,EAASlB,EAAAA,WAAWe,CAAI,EAE9B,OADIG,IAAW,KACRb,IAAQ,GAAGa,IAASD,EAAO,QAAQ,MAAO,EAAE,GACvD,CAeA,IAAIE,EAA+B,KAC/BC,EAAkC,KAWtC,eAAeC,GAAuC,CAelD,OAdIF,IAIJ,EAAU,MAAM,MAHO,OAAO,WAAA,CAGN,QAAQ,CAC5B,YAAa,EAAA,EAAClB,EAAAA,QAAAA,CAAQa,EAAMQ,CAAK,CAAC,EAClC,OAAQ,GACR,OAAQ,OACR,SAAU,UACV,OAAQ,SACR,MAAO,GACP,cAAeR,EACf,SAAU,QACd,CAAC,EACMK,EACX,CAkBA,SAASI,GAA+B,CACpC,GAAIH,EAAS,OAAOA,EAEpB,IAAMI,GAAO,SAA6B,CAGtC,IAAMC,GAAO,MADQ,MADHJ,EAAc,EAAA,CACP,QAAQ,EAAA,CACb,cAAc,EAAE,EAAE,KACtC,GAAII,IAAS,IAAA,GAAW,MAAU,MAAM,2CAA2C,EACnF,OAAOA,CACX,EAAA,CAAG,EAEH,EAAUD,EACV,IAAME,MAAoB,CACtB,EAAU,IACd,EAEA,OADAF,EAAI,KAAKE,EAAOA,CAAK,EACdF,CACX,CASA,eAAeG,GAAgC,CAC3C,IAAMC,EAAUT,EAChB,EAAU,KACV,EAAU,KACV,MAAMS,GAAS,QAAQ,CAC3B,CAuBA,MAAO,CApBH,KAAM,qBACN,MAAO,QACP,MAAM,UAAW,CACb,MAAMD,EAAe,CACzB,EACA,eAAe,EAAQ,CACnB,EAAOE,EAAO,MAAQ,QAAQ,IAAI,EAClC,EAAOA,EAAO,MAAQ,GAC1B,EACA,gBAAgB,EAAQ,CACfC,IAELC,EAAO,YAAY,KAAK,YAAe,KAAKJ,EAAe,CAAC,EAE5DI,EAAO,YAAY,IACf3B,EAAsB,CAAE,QAAO,cAAa,UAAS,aAAY,CAAC,CACtE,EACJ,CAGG,CACX,C"}
1
+ {"version":3,"file":"tempest-pwa-dev-sw.cjs","names":["basePrefix","resolve","toOutputFile","require_base_url","node_path","PWA_WORKER_PLUGIN_NAME","precacheVersionBanner","version","readManifestVersion","asset","bundle","fileName","source","parsed","createDevSwMiddleware","url","req","deps","res","error","next","tempestPwaDevSw","options","root","base","isBuild","mode","envDir","envPrefix","minify","logLevel","matches","target","prefix","context","pending","ensureContext","swSrc","buildWorker","run","text","clear","disposeContext","current","bundleWorkerForProduction","result","outputs","entry","output","item","config","enabled","server","build","swUrl","manifestUrl"],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix, toOutputFile } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n /**\n * Bundle `swSrc` into the production build as `swUrl`. Default `true`.\n *\n * Set `false` only when another step bundles the worker (a second\n * `vite build --config vite.sw.config.ts`, `vite-plugin-pwa`). That is also\n * what tells {@link tempestPwaManifest} the worker is external, so its\n * missing-worker check stands down.\n */\n build?: boolean;\n}\n\n/** Name of the plugin {@link tempestPwaDevSw} returns, looked up by the manifest plugin. */\nexport const PWA_WORKER_PLUGIN_NAME = \"tempest-pwa-dev-sw\";\n\n/**\n * What {@link tempestPwaDevSw} publishes on its plugin `api`, for sibling plugins.\n *\n * {@link tempestPwaManifest} reads it to know which file the worker lands at and\n * whether this build emits it at all.\n */\nexport interface TempestPwaWorkerApi {\n /** URL the worker is served and emitted at. */\n swUrl: string;\n /** Whether this plugin bundles the worker into the production build. */\n build: boolean;\n}\n\n/**\n * The comment prepended to the production worker, carrying the precache version.\n *\n * The browser installs a new service worker only when the bytes of `sw.js`\n * change. A deploy that touches the app but not `src/sw.ts` would otherwise ship\n * a byte-identical worker: it never reinstalls, `installPrecache` never re-runs,\n * and the offline shell keeps serving the previous deploy. The manifest\n * `version` is a hash of every precached URL, so it changes exactly when the\n * precache does.\n *\n * @param version - The `version` of the emitted precache manifest.\n * @returns A one-line block comment.\n */\nexport function precacheVersionBanner(version: string): string {\n return `/* tempest-precache-version: ${version} */\\n`;\n}\n\n/**\n * The `version` of the precache manifest this build emitted, when there is one.\n *\n * @param bundle - The output bundle, as Rollup/Rolldown hands it to `generateBundle`.\n * @param fileName - Where the manifest was emitted, relative to `outDir`.\n * @returns The version, or `undefined` when the manifest is absent or unreadable.\n */\nfunction readManifestVersion(\n bundle: Record<string, { type?: string; source?: unknown }>,\n fileName: string,\n): string | undefined {\n const asset = bundle[fileName];\n if (asset?.type !== \"asset\") return undefined;\n const source =\n typeof asset.source === \"string\"\n ? asset.source\n : new TextDecoder().decode(asset.source as Uint8Array);\n try {\n const parsed = JSON.parse(source) as { version?: unknown };\n return typeof parsed.version === \"string\" ? parsed.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The dev middleware: serve the compiled worker, and a placeholder manifest.\n *\n * Extracted from the plugin so the request handling reads on its own — the\n * plugin body is then only wiring. The manifest is deliberately empty in dev:\n * precaching a set of URLs Vite rewrites on every change would serve stale\n * modules, and the worker's runtime routes still work without it.\n *\n * @param deps - The two URLs, the path matcher and the incremental build.\n * @returns A connect-style middleware.\n */\nfunction createDevSwMiddleware(deps: {\n swUrl: string;\n manifestUrl: string;\n matches: (url: string, target: string) => boolean;\n buildWorker: () => Promise<string>;\n}): (\n req: { url?: string },\n res: {\n statusCode?: number;\n setHeader: (name: string, value: string) => void;\n end: (body: string) => void;\n },\n next: () => void,\n) => Promise<void> {\n return async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (deps.matches(url, deps.swUrl)) {\n try {\n const source = await deps.buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (deps.matches(url, deps.manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n };\n}\n\n/**\n * Service-worker plugin: serves `swSrc` under `npm run dev` and bundles it into\n * the production build.\n *\n * In dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the\n * fly with esbuild — through one incremental context, not a cold build per\n * request — and serves it as a classic worker, plus an empty\n * `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work).\n *\n * In `vite build` it bundles the same entry with Vite's own `build()` (IIFE, so\n * it registers as a classic worker, exactly what the old `vite.sw.config.ts`\n * produced) and emits it at `swUrl`. Before 0.83.0 it was serve-only: an app\n * with this plugin and {@link tempestPwaManifest} worked in dev and shipped a\n * production build with no `sw.js` and a zero exit code, so registration\n * 404'd and `navigator.serviceWorker.ready` never resolved. The emitted worker\n * starts with the precache `version` (see {@link precacheVersionBanner}), so\n * every deploy that changes the precache changes the worker bytes and the\n * browser installs it. Pass `build: false` when another step bundles the\n * worker.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaManifest(), tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n build = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n let isBuild = false;\n let mode = \"production\";\n let envDir: string | false | undefined;\n let envPrefix: string | string[] | undefined;\n let minify: boolean | string = true;\n let logLevel: \"silent\" | \"error\" | \"warn\" = \"warn\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n /**\n * Bundle `swSrc` for production with the host's own Vite.\n *\n * Vite's `build()` rather than esbuild: Vite 8 lists esbuild only as an\n * optional peer, so a fresh app may not have it, while the `vite` running\n * this plugin is always there. The config mirrors the `vite.sw.config.ts`\n * the PWA template used to ship — IIFE lib build, one entry, no app plugins —\n * plus the app's `mode`, `envDir` and `envPrefix`, so `import.meta.env`\n * inside the worker sees the same `VITE_*` values as the page. Its log level\n * is the app's, capped at `warn`, so the nested build adds no size table to\n * the output but a `silent` build stays silent.\n *\n * @returns The bundled worker source.\n */\n async function bundleWorkerForProduction(): Promise<string> {\n const vite = (await import(\"vite\")) as unknown as {\n build(config: Record<string, unknown>): Promise<unknown>;\n };\n const result = await vite.build({\n configFile: false,\n root,\n mode,\n envDir,\n envPrefix,\n publicDir: false,\n logLevel,\n build: {\n write: false,\n emptyOutDir: false,\n copyPublicDir: false,\n sourcemap: false,\n minify,\n lib: {\n entry: resolve(root, swSrc),\n formats: [\"iife\"],\n name: \"sw\",\n fileName: () => \"sw.js\",\n },\n },\n });\n const outputs = (Array.isArray(result) ? result : [result]) as {\n output?: { type: string; isEntry?: boolean; code?: string }[];\n }[];\n for (const { output = [] } of outputs) {\n const entry = output.find((item) => item.type === \"chunk\" && item.isEntry);\n if (entry?.code !== undefined) return entry.code;\n }\n throw new Error(\"the build produced no service-worker chunk\");\n }\n\n const api: TempestPwaWorkerApi = { swUrl, build };\n\n const plugin: Plugin = {\n name: PWA_WORKER_PLUGIN_NAME,\n api,\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n isBuild = config.command === \"build\" && !config.build?.ssr;\n mode = config.mode ?? \"production\";\n envDir = config.envDir;\n envPrefix = config.envPrefix;\n minify = config.build?.minify ?? true;\n logLevel =\n config.logLevel === \"silent\" || config.logLevel === \"error\"\n ? config.logLevel\n : \"warn\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(\n createDevSwMiddleware({ swUrl, manifestUrl, matches, buildWorker }),\n );\n },\n generateBundle: {\n order: \"post\",\n async handler(_outputOptions, bundle) {\n if (!isBuild || !build) return;\n let source: string;\n try {\n source = await bundleWorkerForProduction();\n } catch (error) {\n this.error(\n `[tempest-pwa-dev-sw] could not bundle the service worker \"${swSrc}\" ` +\n `into ${swUrl}: ${error instanceof Error ? error.message : String(error)}. ` +\n \"Fix the entry, point `swSrc` at it, or pass `build: false` if another \" +\n \"step bundles the worker.\",\n );\n }\n const version = readManifestVersion(\n bundle as Record<string, { type?: string; source?: unknown }>,\n toOutputFile(base, manifestUrl),\n );\n this.emitFile({\n type: \"asset\",\n fileName: toOutputFile(base, swUrl),\n source:\n version === undefined ? source : precacheVersionBanner(version) + source,\n });\n },\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"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"}
@@ -1,7 +1,22 @@
1
- import { basePrefix as e } from "./base-url.js";
2
- import { resolve as t } from "node:path";
1
+ import { basePrefix as e, toOutputFile as t } from "./base-url.js";
2
+ import { resolve as n } from "node:path";
3
3
  //#region src/vite/tempest-pwa-dev-sw.ts
4
- function n(e) {
4
+ var r = "tempest-pwa-dev-sw";
5
+ function i(e) {
6
+ return `/* tempest-precache-version: ${e} */\n`;
7
+ }
8
+ function a(e, t) {
9
+ let n = e[t];
10
+ if (n?.type !== "asset") return;
11
+ let r = typeof n.source == "string" ? n.source : new TextDecoder().decode(n.source);
12
+ try {
13
+ let e = JSON.parse(r);
14
+ return typeof e.version == "string" ? e.version : void 0;
15
+ } catch {
16
+ return;
17
+ }
18
+ }
19
+ function o(e) {
5
20
  return async (t, n, r) => {
6
21
  let i = (t.url ?? "").split("?")[0];
7
22
  if (e.matches(i, e.swUrl)) try {
@@ -16,63 +31,113 @@ function n(e) {
16
31
  }))) : r();
17
32
  };
18
33
  }
19
- function r(r = {}) {
20
- let { swSrc: i = "src/sw.ts", swUrl: a = "/sw.js", manifestUrl: o = "/precache-manifest.json", enabled: s = !0 } = r, c = process.cwd(), l = "/";
21
- function u(t, n) {
34
+ function s(s = {}) {
35
+ let { swSrc: c = "src/sw.ts", swUrl: l = "/sw.js", manifestUrl: u = "/precache-manifest.json", enabled: d = !0, build: f = !0 } = s, p = process.cwd(), m = "/", h = !1, g = "production", _, v, y = !0, b = "warn";
36
+ function x(t, n) {
22
37
  if (t === n) return !0;
23
- let r = e(l);
38
+ let r = e(m);
24
39
  return r !== "/" && t === `${r}${n.replace(/^\//, "")}`;
25
40
  }
26
- let d = null, f = null;
27
- async function p() {
28
- return d || (d = await (await import("esbuild")).context({
29
- entryPoints: [t(c, i)],
41
+ let S = null, C = null;
42
+ async function w() {
43
+ return S || (S = await (await import("esbuild")).context({
44
+ entryPoints: [n(p, c)],
30
45
  bundle: !0,
31
46
  format: "iife",
32
47
  platform: "browser",
33
48
  target: "es2020",
34
49
  write: !1,
35
- absWorkingDir: c,
50
+ absWorkingDir: p,
36
51
  logLevel: "silent"
37
- }), d);
52
+ }), S);
38
53
  }
39
- function m() {
40
- if (f) return f;
54
+ function T() {
55
+ if (C) return C;
41
56
  let e = (async () => {
42
- let e = (await (await p()).rebuild()).outputFiles?.[0]?.text;
57
+ let e = (await (await w()).rebuild()).outputFiles?.[0]?.text;
43
58
  if (e === void 0) throw Error("esbuild produced no service-worker output");
44
59
  return e;
45
60
  })();
46
- f = e;
61
+ C = e;
47
62
  let t = () => {
48
- f = null;
63
+ C = null;
49
64
  };
50
65
  return e.then(t, t), e;
51
66
  }
52
- async function h() {
53
- let e = d;
54
- d = null, f = null, await e?.dispose();
67
+ async function E() {
68
+ let e = S;
69
+ S = null, C = null, await e?.dispose();
70
+ }
71
+ async function D() {
72
+ let e = await (await import("vite")).build({
73
+ configFile: !1,
74
+ root: p,
75
+ mode: g,
76
+ envDir: _,
77
+ envPrefix: v,
78
+ publicDir: !1,
79
+ logLevel: b,
80
+ build: {
81
+ write: !1,
82
+ emptyOutDir: !1,
83
+ copyPublicDir: !1,
84
+ sourcemap: !1,
85
+ minify: y,
86
+ lib: {
87
+ entry: n(p, c),
88
+ formats: ["iife"],
89
+ name: "sw",
90
+ fileName: () => "sw.js"
91
+ }
92
+ }
93
+ }), t = Array.isArray(e) ? e : [e];
94
+ for (let { output: e = [] } of t) {
95
+ let t = e.find((e) => e.type === "chunk" && e.isEntry);
96
+ if (t?.code !== void 0) return t.code;
97
+ }
98
+ throw Error("the build produced no service-worker chunk");
55
99
  }
56
100
  return {
57
- name: "tempest-pwa-dev-sw",
58
- apply: "serve",
101
+ name: r,
102
+ api: {
103
+ swUrl: l,
104
+ build: f
105
+ },
59
106
  async buildEnd() {
60
- await h();
107
+ await E();
61
108
  },
62
109
  configResolved(e) {
63
- c = e.root ?? process.cwd(), l = e.base ?? "/";
110
+ p = e.root ?? process.cwd(), m = e.base ?? "/", h = e.command === "build" && !e.build?.ssr, g = e.mode ?? "production", _ = e.envDir, v = e.envPrefix, y = e.build?.minify ?? !0, b = e.logLevel === "silent" || e.logLevel === "error" ? e.logLevel : "warn";
64
111
  },
65
112
  configureServer(e) {
66
- s && (e.httpServer?.once("close", () => void h()), e.middlewares.use(n({
67
- swUrl: a,
68
- manifestUrl: o,
69
- matches: u,
70
- buildWorker: m
113
+ d && (e.httpServer?.once("close", () => void E()), e.middlewares.use(o({
114
+ swUrl: l,
115
+ manifestUrl: u,
116
+ matches: x,
117
+ buildWorker: T
71
118
  })));
119
+ },
120
+ generateBundle: {
121
+ order: "post",
122
+ async handler(e, n) {
123
+ if (!h || !f) return;
124
+ let r;
125
+ try {
126
+ r = await D();
127
+ } catch (e) {
128
+ this.error(`[tempest-pwa-dev-sw] could not bundle the service worker "${c}" into ${l}: ${e instanceof Error ? e.message : String(e)}. Fix the entry, point \`swSrc\` at it, or pass \`build: false\` if another step bundles the worker.`);
129
+ }
130
+ let o = a(n, t(m, u));
131
+ this.emitFile({
132
+ type: "asset",
133
+ fileName: t(m, l),
134
+ source: o === void 0 ? r : i(o) + r
135
+ });
136
+ }
72
137
  }
73
138
  };
74
139
  }
75
140
  //#endregion
76
- export { r as tempestPwaDevSw };
141
+ export { r as PWA_WORKER_PLUGIN_NAME, i as precacheVersionBanner, s as tempestPwaDevSw };
77
142
 
78
143
  //# sourceMappingURL=tempest-pwa-dev-sw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tempest-pwa-dev-sw.js","names":["createDevSwMiddleware","url","req","deps","source","res","error","next","tempestPwaDevSw","options","root","base","matches","target","prefix","basePrefix","context","pending","ensureContext","resolve","swSrc","buildWorker","run","text","clear","disposeContext","current","config","enabled","server"],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * The dev middleware: serve the compiled worker, and a placeholder manifest.\n *\n * Extracted from the plugin so the request handling reads on its own — the\n * plugin body is then only wiring. The manifest is deliberately empty in dev:\n * precaching a set of URLs Vite rewrites on every change would serve stale\n * modules, and the worker's runtime routes still work without it.\n *\n * @param deps - The two URLs, the path matcher and the incremental build.\n * @returns A connect-style middleware.\n */\nfunction createDevSwMiddleware(deps: {\n swUrl: string;\n manifestUrl: string;\n matches: (url: string, target: string) => boolean;\n buildWorker: () => Promise<string>;\n}): (\n req: { url?: string },\n res: {\n statusCode?: number;\n setHeader: (name: string, value: string) => void;\n end: (body: string) => void;\n },\n next: () => void,\n) => Promise<void> {\n return async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (deps.matches(url, deps.swUrl)) {\n try {\n const source = await deps.buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (deps.matches(url, deps.manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n };\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild — through one incremental context, not a cold build per request —\n * and serves it as a classic worker, plus an empty `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(\n createDevSwMiddleware({ swUrl, manifestUrl, matches, buildWorker }),\n );\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":";;;AAsCA,SAASA,EAAsB,GAaZ;CACf,OAAO,OAAO,GAAK,GAAK,MAAS;EAC7B,IAAMC,KAAOC,EAAI,OAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC;EAEvC,IAAIC,EAAK,QAAQF,GAAKE,EAAK,KAAK,GAC5B,IAAI;GACA,IAAMC,IAAS,MAAMD,EAAK,YAAY;GAItC,AAHAE,EAAI,UAAU,gBAAgB,wBAAwB,GACtDA,EAAI,UAAU,0BAA0B,GAAG,GAC3CA,EAAI,UAAU,iBAAiB,UAAU,GACzCA,EAAI,IAAID,CAAM;EAClB,SAASE,GAAO;GAEZ,AADA,EAAI,aAAa,KACjBD,EAAI,IAAI,+BAA+B,OAAOC,CAAK,GAAG;EAC1D;OAIJ,AAAIH,EAAK,QAAQF,GAAKE,EAAK,WAAW,KAClCE,EAAI,UAAU,gBAAgB,kBAAkB,GAChDA,EAAI,UAAU,iBAAiB,UAAU,GACzCA,EAAI,IAAI,KAAK,UAAU;GAAE,SAAS;GAAO,MAAM,CAAC;EAAE,CAAC,CAAC,KAIxDE,EAAK;CACT;AACJ;AAkBA,SAAgBC,EAAgB,IAAkC,CAAC,GAAsB;CACrF,IAAM,EACF,WAAQ,aACR,WAAQ,UACR,iBAAc,2BACd,aAAU,OACVC,GAEAC,IAAO,QAAQ,IAAI,GACnBC,IAAO;CAUX,SAASC,EAAQ,GAAa,GAAyB;EACnD,IAAIX,MAAQY,GAAQ,OAAO;EAC3B,IAAMC,IAASC,EAAWJ,CAAI;EAE9B,OADIG,MAAW,OACRb,MAAQ,GAAGa,IAASD,EAAO,QAAQ,OAAO,EAAE;CACvD;CAeA,IAAIG,IAA+B,MAC/BC,IAAkC;CAWtC,eAAeC,IAAuC;EAelD,OAdIF,MAIJ,IAAU,OAAM,MAHO,OAAO,WAAA,CAGN,QAAQ;GAC5B,aAAa,CAACG,EAAQT,GAAMU,CAAK,CAAC;GAClC,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,QAAQ;GACR,OAAO;GACP,eAAeV;GACf,UAAU;EACd,CAAC,GACMM;CACX;CAkBA,SAASK,IAA+B;EACpC,IAAIJ,GAAS,OAAOA;EAEpB,IAAMK,KAAO,YAA6B;GAGtC,IAAMC,KAAO,OADQ,MADHL,EAAc,EAAA,CACP,QAAQ,EAAA,CACb,cAAc,EAAE,EAAE;GACtC,IAAIK,MAAS,KAAA,GAAW,MAAU,MAAM,2CAA2C;GACnF,OAAOA;EACX,EAAA,CAAG;EAEH,IAAUD;EACV,IAAME,UAAoB;GACtB,IAAU;EACd;EAEA,OADAF,EAAI,KAAKE,GAAOA,CAAK,GACdF;CACX;CASA,eAAeG,IAAgC;EAC3C,IAAMC,IAAUV;EAGhB,AAFA,IAAU,MACV,IAAU,MACV,MAAMU,GAAS,QAAQ;CAC3B;CAuBA,OAAO;EApBH,MAAM;EACN,OAAO;EACP,MAAM,WAAW;GACb,MAAMD,EAAe;EACzB;EACA,eAAe,GAAQ;GAEnB,AADA,IAAOE,EAAO,QAAQ,QAAQ,IAAI,GAClC,IAAOA,EAAO,QAAQ;EAC1B;EACA,gBAAgB,GAAQ;GACpB,AAAKC,MAELC,EAAO,YAAY,KAAK,eAAe,KAAKJ,EAAe,CAAC,GAE5DI,EAAO,YAAY,IACf7B,EAAsB;IAAE;IAAO;IAAa;IAAS;GAAY,CAAC,CACtE;EACJ;CAGG;AACX"}
1
+ {"version":3,"file":"tempest-pwa-dev-sw.js","names":["PWA_WORKER_PLUGIN_NAME","precacheVersionBanner","version","readManifestVersion","asset","bundle","fileName","source","parsed","createDevSwMiddleware","url","req","deps","res","error","next","tempestPwaDevSw","options","root","base","isBuild","mode","envDir","envPrefix","minify","logLevel","matches","target","prefix","basePrefix","context","pending","ensureContext","resolve","swSrc","buildWorker","run","text","clear","disposeContext","current","bundleWorkerForProduction","result","outputs","entry","output","item","config","enabled","server","build","swUrl","toOutputFile","manifestUrl"],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix, toOutputFile } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n /**\n * Bundle `swSrc` into the production build as `swUrl`. Default `true`.\n *\n * Set `false` only when another step bundles the worker (a second\n * `vite build --config vite.sw.config.ts`, `vite-plugin-pwa`). That is also\n * what tells {@link tempestPwaManifest} the worker is external, so its\n * missing-worker check stands down.\n */\n build?: boolean;\n}\n\n/** Name of the plugin {@link tempestPwaDevSw} returns, looked up by the manifest plugin. */\nexport const PWA_WORKER_PLUGIN_NAME = \"tempest-pwa-dev-sw\";\n\n/**\n * What {@link tempestPwaDevSw} publishes on its plugin `api`, for sibling plugins.\n *\n * {@link tempestPwaManifest} reads it to know which file the worker lands at and\n * whether this build emits it at all.\n */\nexport interface TempestPwaWorkerApi {\n /** URL the worker is served and emitted at. */\n swUrl: string;\n /** Whether this plugin bundles the worker into the production build. */\n build: boolean;\n}\n\n/**\n * The comment prepended to the production worker, carrying the precache version.\n *\n * The browser installs a new service worker only when the bytes of `sw.js`\n * change. A deploy that touches the app but not `src/sw.ts` would otherwise ship\n * a byte-identical worker: it never reinstalls, `installPrecache` never re-runs,\n * and the offline shell keeps serving the previous deploy. The manifest\n * `version` is a hash of every precached URL, so it changes exactly when the\n * precache does.\n *\n * @param version - The `version` of the emitted precache manifest.\n * @returns A one-line block comment.\n */\nexport function precacheVersionBanner(version: string): string {\n return `/* tempest-precache-version: ${version} */\\n`;\n}\n\n/**\n * The `version` of the precache manifest this build emitted, when there is one.\n *\n * @param bundle - The output bundle, as Rollup/Rolldown hands it to `generateBundle`.\n * @param fileName - Where the manifest was emitted, relative to `outDir`.\n * @returns The version, or `undefined` when the manifest is absent or unreadable.\n */\nfunction readManifestVersion(\n bundle: Record<string, { type?: string; source?: unknown }>,\n fileName: string,\n): string | undefined {\n const asset = bundle[fileName];\n if (asset?.type !== \"asset\") return undefined;\n const source =\n typeof asset.source === \"string\"\n ? asset.source\n : new TextDecoder().decode(asset.source as Uint8Array);\n try {\n const parsed = JSON.parse(source) as { version?: unknown };\n return typeof parsed.version === \"string\" ? parsed.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The dev middleware: serve the compiled worker, and a placeholder manifest.\n *\n * Extracted from the plugin so the request handling reads on its own — the\n * plugin body is then only wiring. The manifest is deliberately empty in dev:\n * precaching a set of URLs Vite rewrites on every change would serve stale\n * modules, and the worker's runtime routes still work without it.\n *\n * @param deps - The two URLs, the path matcher and the incremental build.\n * @returns A connect-style middleware.\n */\nfunction createDevSwMiddleware(deps: {\n swUrl: string;\n manifestUrl: string;\n matches: (url: string, target: string) => boolean;\n buildWorker: () => Promise<string>;\n}): (\n req: { url?: string },\n res: {\n statusCode?: number;\n setHeader: (name: string, value: string) => void;\n end: (body: string) => void;\n },\n next: () => void,\n) => Promise<void> {\n return async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (deps.matches(url, deps.swUrl)) {\n try {\n const source = await deps.buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (deps.matches(url, deps.manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n };\n}\n\n/**\n * Service-worker plugin: serves `swSrc` under `npm run dev` and bundles it into\n * the production build.\n *\n * In dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the\n * fly with esbuild — through one incremental context, not a cold build per\n * request — and serves it as a classic worker, plus an empty\n * `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work).\n *\n * In `vite build` it bundles the same entry with Vite's own `build()` (IIFE, so\n * it registers as a classic worker, exactly what the old `vite.sw.config.ts`\n * produced) and emits it at `swUrl`. Before 0.83.0 it was serve-only: an app\n * with this plugin and {@link tempestPwaManifest} worked in dev and shipped a\n * production build with no `sw.js` and a zero exit code, so registration\n * 404'd and `navigator.serviceWorker.ready` never resolved. The emitted worker\n * starts with the precache `version` (see {@link precacheVersionBanner}), so\n * every deploy that changes the precache changes the worker bytes and the\n * browser installs it. Pass `build: false` when another step bundles the\n * worker.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaManifest(), tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n build = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n let isBuild = false;\n let mode = \"production\";\n let envDir: string | false | undefined;\n let envPrefix: string | string[] | undefined;\n let minify: boolean | string = true;\n let logLevel: \"silent\" | \"error\" | \"warn\" = \"warn\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n /**\n * Bundle `swSrc` for production with the host's own Vite.\n *\n * Vite's `build()` rather than esbuild: Vite 8 lists esbuild only as an\n * optional peer, so a fresh app may not have it, while the `vite` running\n * this plugin is always there. The config mirrors the `vite.sw.config.ts`\n * the PWA template used to ship — IIFE lib build, one entry, no app plugins —\n * plus the app's `mode`, `envDir` and `envPrefix`, so `import.meta.env`\n * inside the worker sees the same `VITE_*` values as the page. Its log level\n * is the app's, capped at `warn`, so the nested build adds no size table to\n * the output but a `silent` build stays silent.\n *\n * @returns The bundled worker source.\n */\n async function bundleWorkerForProduction(): Promise<string> {\n const vite = (await import(\"vite\")) as unknown as {\n build(config: Record<string, unknown>): Promise<unknown>;\n };\n const result = await vite.build({\n configFile: false,\n root,\n mode,\n envDir,\n envPrefix,\n publicDir: false,\n logLevel,\n build: {\n write: false,\n emptyOutDir: false,\n copyPublicDir: false,\n sourcemap: false,\n minify,\n lib: {\n entry: resolve(root, swSrc),\n formats: [\"iife\"],\n name: \"sw\",\n fileName: () => \"sw.js\",\n },\n },\n });\n const outputs = (Array.isArray(result) ? result : [result]) as {\n output?: { type: string; isEntry?: boolean; code?: string }[];\n }[];\n for (const { output = [] } of outputs) {\n const entry = output.find((item) => item.type === \"chunk\" && item.isEntry);\n if (entry?.code !== undefined) return entry.code;\n }\n throw new Error(\"the build produced no service-worker chunk\");\n }\n\n const api: TempestPwaWorkerApi = { swUrl, build };\n\n const plugin: Plugin = {\n name: PWA_WORKER_PLUGIN_NAME,\n api,\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n isBuild = config.command === \"build\" && !config.build?.ssr;\n mode = config.mode ?? \"production\";\n envDir = config.envDir;\n envPrefix = config.envPrefix;\n minify = config.build?.minify ?? true;\n logLevel =\n config.logLevel === \"silent\" || config.logLevel === \"error\"\n ? config.logLevel\n : \"warn\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(\n createDevSwMiddleware({ swUrl, manifestUrl, matches, buildWorker }),\n );\n },\n generateBundle: {\n order: \"post\",\n async handler(_outputOptions, bundle) {\n if (!isBuild || !build) return;\n let source: string;\n try {\n source = await bundleWorkerForProduction();\n } catch (error) {\n this.error(\n `[tempest-pwa-dev-sw] could not bundle the service worker \"${swSrc}\" ` +\n `into ${swUrl}: ${error instanceof Error ? error.message : String(error)}. ` +\n \"Fix the entry, point `swSrc` at it, or pass `build: false` if another \" +\n \"step bundles the worker.\",\n );\n }\n const version = readManifestVersion(\n bundle as Record<string, { type?: string; source?: unknown }>,\n toOutputFile(base, manifestUrl),\n );\n this.emitFile({\n type: \"asset\",\n fileName: toOutputFile(base, swUrl),\n source:\n version === undefined ? source : precacheVersionBanner(version) + source,\n });\n },\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- const e=require("./base-url.cjs");function t(t,n){return`${e.basePrefix(t)}${n}`.replace(/([^:]\/)\/+/g,`$1`)}function n(t,n){let r=e.basePrefix(t);return r===`/`?n.startsWith(`/`)?n:`/${n}`:n.startsWith(r)?n:`${r}${n.replace(/^\//,``)}`}function r(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)>>>0;return t.toString(16)}function i(e={}){let{fileName:i=`precache-manifest.json`,additionalUrls:a=[],exclude:o=/\.map$/,includeHtml:s=!0,appShell:c=`/index.html`}=e,l=`/`;return{name:`tempest-pwa-manifest`,apply:`build`,configResolved(e){l=e.base??`/`},generateBundle(e,u){let d=new Set(a.map(e=>n(l,e)));c&&d.add(n(l,c));for(let e of Object.keys(u))e!==i&&(o.test(e)||(s||!e.endsWith(`.html`))&&d.add(t(l,e)));let f=[...d].sort(),p=r(f.join(`
2
- `));this.emitFile({type:`asset`,fileName:i,source:JSON.stringify({version:p,urls:f})})}}}exports.tempestPwaManifest=i;
1
+ const e=require("./base-url.cjs"),t=require("./tempest-pwa-dev-sw.cjs");let n=require("node:path"),r=require("node:fs");function i(t,i,a,o){let s=e.toOutputFile(i,t);if(s in a)return!0;if(!o)return!1;let c=(0,n.join)(o,s);return(0,r.existsSync)(c)&&(0,r.statSync)(c).isFile()}function a(t,n){return`${e.basePrefix(t)}${n}`.replace(/([^:]\/)\/+/g,`$1`)}function o(t,n){let r=e.basePrefix(t);return r===`/`?n.startsWith(`/`)?n:`/${n}`:n.startsWith(r)?n:`${r}${n.replace(/^\//,``)}`}function s(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)>>>0;return t.toString(16)}function c(n={}){let{fileName:r=`precache-manifest.json`,additionalUrls:c=[],exclude:l=/\.map$/,includeHtml:u=!0,appShell:d=`/index.html`,serviceWorker:f}=n,p=`/`,m=``,h=`/sw.js`;return{name:`tempest-pwa-manifest`,apply:`build`,configResolved(e){p=e.base??`/`,m=e.publicDir??``;let n=(e.plugins??[]).find(e=>e.name===t.PWA_WORKER_PLUGIN_NAME)?.api;h=f===void 0?n?n.build?n.swUrl:!1:`/sw.js`:f},generateBundle(e,t){let n=new Set(c.map(e=>o(p,e)));d&&n.add(o(p,d));for(let e of Object.keys(t))e!==r&&(l.test(e)||(u||!e.endsWith(`.html`))&&n.add(a(p,e)));let i=[...n].sort(),f=s(i.join(`
2
+ `));this.emitFile({type:`asset`,fileName:r,source:JSON.stringify({version:f,urls:i})})},writeBundle(t,n){let a=c.filter(e=>!/^[a-z][a-z\d+.-]*:/i.test(e)&&!i(e,p,n,m));a.length>0&&this.error(`[tempest-pwa-manifest] additionalUrls lists ${a.join(`, `)}, which is neither in public/ nor in the build output. In production that URL answers with the SPA fallback or a 404, so the precache would store index.html under its name or fail the worker install. Move the file into public/, or drop the entry — a file Vite hashes into assets/ (a web manifest linked from index.html) is already precached.`),h!==!1&&(e.toOutputFile(p,h)in n||this.error(`[tempest-pwa-manifest] the build emitted ${r} but no service worker at ${h}. In production the registration 404s and navigator.serviceWorker.ready never resolves (push, updates and offline hang without an error). Add tempestPwaDevSw() — it bundles src/sw.ts into this build — or, if a separate step bundles the worker after this one, pass tempestPwaManifest({ serviceWorker: false }) or tempestPwaDevSw({ build: false }).`))}}}exports.tempestPwaManifest=c;
3
3
  //# sourceMappingURL=tempest-pwa-manifest.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tempest-pwa-manifest.cjs","names":["basePrefix","require_base_url","joinBase","base","file","withBase","prefix","url","hash","h","i","input","tempestPwaManifest","options","config","urls","additionalUrls","appShell","bundle","fileName","exclude","includeHtml","list","version"],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n /** Output file name (under the build root). Default `precache-manifest.json`. */\n fileName?: string;\n /**\n * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n * `public/` assets like the web manifest and icons. Default `[]`.\n *\n * Write these relative to the site root (`/icon.svg`); the resolved Vite\n * `base` is prefixed for you. An entry that already carries the base is\n * left alone, so spelling it out by hand is still correct.\n */\n additionalUrls?: string[];\n /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n exclude?: RegExp;\n /** Include emitted `.html` documents (the app shell). Default `true`. */\n includeHtml?: boolean;\n /**\n * App-shell document always added to the manifest, even if Vite emits it\n * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n *\n * Like {@link TempestPwaManifestOptions.additionalUrls}, this is prefixed\n * with the resolved Vite `base`.\n */\n appShell?: string | false;\n}\n\nfunction joinBase(base: string, file: string): string {\n return `${basePrefix(base)}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Prefix a caller-supplied, root-absolute URL with the resolved Vite `base`.\n *\n * Idempotent: a URL that already starts with the base is returned untouched,\n * so configs written before this was applied — spelling the base out by hand —\n * keep producing the same manifest instead of gaining a doubled prefix.\n */\nfunction withBase(base: string, url: string): string {\n const prefix = basePrefix(base);\n if (prefix === \"/\") return url.startsWith(\"/\") ? url : `/${url}`;\n if (url.startsWith(prefix)) return url;\n return `${prefix}${url.replace(/^\\//, \"\")}`;\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n }\n return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n * plugins: [tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] })],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n const {\n fileName = \"precache-manifest.json\",\n additionalUrls = [],\n exclude = /\\.map$/,\n includeHtml = true,\n appShell = \"/index.html\",\n } = options;\n\n let base = \"/\";\n\n const plugin: Plugin = {\n name: \"tempest-pwa-manifest\",\n apply: \"build\",\n configResolved(config) {\n base = config.base ?? \"/\";\n },\n generateBundle(_outputOptions, bundle) {\n const urls = new Set<string>(additionalUrls.map((url) => withBase(base, url)));\n // Vite may emit index.html after this hook, so guarantee the shell.\n if (appShell) urls.add(withBase(base, appShell));\n for (const file of Object.keys(bundle)) {\n if (file === fileName) continue;\n if (exclude.test(file)) continue;\n if (!includeHtml && file.endsWith(\".html\")) continue;\n urls.add(joinBase(base, file));\n }\n\n const list = [...urls].sort();\n const version = hash(list.join(\"\\n\"));\n this.emitFile({\n type: \"asset\",\n fileName,\n source: JSON.stringify({ version, urls: list }),\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"A,kCAsCA,SAASE,EAAS,EAAc,EAAsB,CAClD,MAAO,GAAGF,EAAAA,WAAWG,CAAI,IAAIC,IAAO,QAAQ,eAAgB,IAAI,CACpE,CASA,SAASC,EAAS,EAAc,EAAqB,CACjD,IAAMC,EAASN,EAAAA,WAAWG,CAAI,EAG9B,OAFIG,IAAW,IAAYC,EAAI,WAAW,GAAG,EAAIA,EAAM,IAAIA,IACvDA,EAAI,WAAWD,CAAM,EAAUC,EAC5B,GAAGD,IAASC,EAAI,QAAQ,MAAO,EAAE,GAC5C,CAGA,SAASC,EAAK,EAAuB,CACjC,IAAIC,EAAI,KACR,IAAK,IAAIC,EAAI,EAAGA,EAAIC,EAAM,OAAQ,IAC9B,GAAMF,GAAK,GAAKA,EAAIE,EAAM,WAAWD,CAAC,IAAO,EAEjD,OAAOD,EAAE,SAAS,EAAE,CACxB,CAiBA,SAAgBG,EAAmB,EAAqC,CAAC,EAAsB,CAC3F,GAAM,CACF,WAAW,yBACX,iBAAiB,CAAC,EAClB,UAAU,SACV,cAAc,GACd,WAAW,eACXC,EAEAV,EAAO,IA6BX,MAAO,CA1BH,KAAM,uBACN,MAAO,QACP,eAAe,EAAQ,CACnB,EAAOW,EAAO,MAAQ,GAC1B,EACA,eAAe,EAAgB,EAAQ,CACnC,IAAMC,EAAO,IAAI,IAAYC,EAAe,IAAK,GAAQX,EAASF,EAAMI,CAAG,CAAC,CAAC,EAEzEU,GAAUF,EAAK,IAAIV,EAASF,EAAMc,CAAQ,CAAC,EAC/C,IAAK,IAAMb,KAAQ,OAAO,KAAKc,CAAM,EAC7Bd,IAASe,IACTC,EAAQ,KAAKhB,CAAI,IAChBiB,GAAe,GAAK,SAAS,OAAO,IACzCN,EAAK,IAAIb,EAASC,EAAMC,CAAI,CAAC,GAGjC,IAAMkB,EAAO,CAAC,GAAGP,CAAI,CAAC,CAAC,KAAK,EACtBQ,EAAUf,EAAKc,EAAK,KAAK;CAAI,CAAC,EACpC,KAAK,SAAS,CACV,KAAM,QACN,WACA,OAAQ,KAAK,UAAU,CAAE,UAAS,KAAMA,CAAK,CAAC,CAClD,CAAC,CACL,CAGG,CACX,C"}
1
+ {"version":3,"file":"tempest-pwa-manifest.cjs","names":["toOutputFile","join","existsSync","statSync","basePrefix","PWA_WORKER_PLUGIN_NAME","require_base_url","require_tempest_pwa_dev_sw","node_path","node_fs","resolvesToFile","file","base","url","bundle","publicDir","path","joinBase","withBase","prefix","hash","h","i","input","tempestPwaManifest","options","expectedWorker","config","worker","item","serviceWorker","urls","additionalUrls","appShell","fileName","exclude","includeHtml","list","version","missing"],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix, toOutputFile } from \"./base-url\";\nimport { PWA_WORKER_PLUGIN_NAME } from \"./tempest-pwa-dev-sw\";\nimport type { TempestPwaWorkerApi } from \"./tempest-pwa-dev-sw\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n /** Output file name (under the build root). Default `precache-manifest.json`. */\n fileName?: string;\n /**\n * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n * `public/` assets like the web manifest and icons. Default `[]`.\n *\n * Write these relative to the site root (`/icon.svg`); the resolved Vite\n * `base` is prefixed for you. An entry that already carries the base is\n * left alone, so spelling it out by hand is still correct.\n *\n * Every same-origin entry must be a file in `public/` or in the bundle, or\n * the build fails. A URL that resolves to nothing is answered in production\n * by the SPA fallback (`index.html`) or a 404, so the precache would store\n * the app shell under that name, or fail the worker's install outright. The\n * usual culprit is a web manifest linked from `index.html` but kept outside\n * `public/`: Vite hashes it into `assets/`, where it is already precached.\n * Absolute `http(s):` URLs are not checked.\n */\n additionalUrls?: string[];\n /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n exclude?: RegExp;\n /** Include emitted `.html` documents (the app shell). Default `true`. */\n includeHtml?: boolean;\n /**\n * App-shell document always added to the manifest, even if Vite emits it\n * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n *\n * Like {@link TempestPwaManifestOptions.additionalUrls}, this is prefixed\n * with the resolved Vite `base`.\n */\n appShell?: string | false;\n /**\n * URL of the service worker that reads this manifest. The build fails when\n * no file is emitted at that URL. Default: the `swUrl` of\n * {@link tempestPwaDevSw} when it is in the plugin list, `/sw.js` otherwise.\n *\n * A precache manifest is only read by a worker, and a build that ships the\n * manifest without the worker exits 0 while registration 404s in\n * production — `navigator.serviceWorker.ready` never resolves and push,\n * updates and offline all hang without a message. Pass `false` when the\n * worker is bundled by a separate step after this build (a second\n * `vite build --config vite.sw.config.ts`); `tempestPwaDevSw({ build: false })`\n * already says the same and needs nothing here.\n */\n serviceWorker?: string | false;\n}\n\n/**\n * Whether `url` names something the build can serve: a bundle entry or a file\n * in the public directory.\n *\n * @param url - The URL as written in `additionalUrls`.\n * @param base - Vite's resolved `base`.\n * @param bundle - The final output bundle.\n * @param publicDir - The resolved public directory, or `\"\"` when disabled.\n * @returns `true` when the URL resolves to a real file.\n */\nfunction resolvesToFile(\n url: string,\n base: string,\n bundle: Record<string, unknown>,\n publicDir: string,\n): boolean {\n const file = toOutputFile(base, url);\n if (file in bundle) return true;\n if (!publicDir) return false;\n const path = join(publicDir, file);\n return existsSync(path) && statSync(path).isFile();\n}\n\nfunction joinBase(base: string, file: string): string {\n return `${basePrefix(base)}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Prefix a caller-supplied, root-absolute URL with the resolved Vite `base`.\n *\n * Idempotent: a URL that already starts with the base is returned untouched,\n * so configs written before this was applied — spelling the base out by hand —\n * keep producing the same manifest instead of gaining a doubled prefix.\n */\nfunction withBase(base: string, url: string): string {\n const prefix = basePrefix(base);\n if (prefix === \"/\") return url.startsWith(\"/\") ? url : `/${url}`;\n if (url.startsWith(prefix)) return url;\n return `${prefix}${url.replace(/^\\//, \"\")}`;\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n }\n return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * Two mistakes that used to ship in silence now fail the build, after the bundle\n * is written: no service worker emitted at\n * {@link TempestPwaManifestOptions.serviceWorker}, and an `additionalUrls`\n * entry that is neither in `public/` nor in the bundle. Pair it with\n * {@link tempestPwaDevSw}, which bundles `src/sw.ts` into the same build.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n * plugins: [\n * tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] }),\n * tempestPwaDevSw(),\n * ],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n const {\n fileName = \"precache-manifest.json\",\n additionalUrls = [],\n exclude = /\\.map$/,\n includeHtml = true,\n appShell = \"/index.html\",\n serviceWorker,\n } = options;\n\n let base = \"/\";\n let publicDir = \"\";\n let expectedWorker: string | false = \"/sw.js\";\n\n const plugin: Plugin = {\n name: \"tempest-pwa-manifest\",\n apply: \"build\",\n configResolved(config) {\n base = config.base ?? \"/\";\n publicDir = config.publicDir ?? \"\";\n const worker = (config.plugins ?? []).find(\n (item) => item.name === PWA_WORKER_PLUGIN_NAME,\n )?.api as TempestPwaWorkerApi | undefined;\n if (serviceWorker !== undefined) expectedWorker = serviceWorker;\n else if (worker) expectedWorker = worker.build ? worker.swUrl : false;\n else expectedWorker = \"/sw.js\";\n },\n generateBundle(_outputOptions, bundle) {\n const urls = new Set<string>(additionalUrls.map((url) => withBase(base, url)));\n // Vite may emit index.html after this hook, so guarantee the shell.\n if (appShell) urls.add(withBase(base, appShell));\n for (const file of Object.keys(bundle)) {\n if (file === fileName) continue;\n if (exclude.test(file)) continue;\n if (!includeHtml && file.endsWith(\".html\")) continue;\n urls.add(joinBase(base, file));\n }\n\n const list = [...urls].sort();\n const version = hash(list.join(\"\\n\"));\n this.emitFile({\n type: \"asset\",\n fileName,\n source: JSON.stringify({ version, urls: list }),\n });\n },\n writeBundle(_outputOptions, bundle) {\n const missing = additionalUrls.filter(\n (url) =>\n !/^[a-z][a-z\\d+.-]*:/i.test(url) &&\n !resolvesToFile(url, base, bundle, publicDir),\n );\n if (missing.length > 0) {\n this.error(\n `[tempest-pwa-manifest] additionalUrls lists ${missing.join(\", \")}, which is ` +\n \"neither in public/ nor in the build output. In production that URL \" +\n \"answers with the SPA fallback or a 404, so the precache would store \" +\n \"index.html under its name or fail the worker install. Move the file \" +\n \"into public/, or drop the entry — a file Vite hashes into assets/ \" +\n \"(a web manifest linked from index.html) is already precached.\",\n );\n }\n\n if (expectedWorker === false) return;\n if (toOutputFile(base, expectedWorker) in bundle) return;\n this.error(\n `[tempest-pwa-manifest] the build emitted ${fileName} but no service worker ` +\n `at ${expectedWorker}. In production the registration 404s and ` +\n \"navigator.serviceWorker.ready never resolves (push, updates and offline \" +\n \"hang without an error). Add tempestPwaDevSw() — it bundles src/sw.ts into \" +\n \"this build — or, if a separate step bundles the worker after this one, \" +\n \"pass tempestPwaManifest({ serviceWorker: false }) or \" +\n \"tempestPwaDevSw({ build: false }).\",\n );\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"A,wHA0EA,SAASU,EACL,EACA,EACA,EACA,EACO,CACP,IAAMC,EAAOX,EAAAA,aAAaY,EAAMC,CAAG,EACnC,GAAIF,KAAQG,EAAQ,MAAO,GAC3B,GAAI,CAACC,EAAW,MAAO,GACvB,IAAMC,GAAA,EAAOf,EAAAA,KAAAA,CAAKc,EAAWJ,CAAI,EACjC,OAAA,EAAOT,EAAAA,WAAAA,CAAWc,CAAI,IAAA,EAAKb,EAAAA,SAAAA,CAASa,CAAI,CAAC,CAAC,OAAO,CACrD,CAEA,SAASC,EAAS,EAAc,EAAsB,CAClD,MAAO,GAAGb,EAAAA,WAAWQ,CAAI,IAAID,IAAO,QAAQ,eAAgB,IAAI,CACpE,CASA,SAASO,EAAS,EAAc,EAAqB,CACjD,IAAMC,EAASf,EAAAA,WAAWQ,CAAI,EAG9B,OAFIO,IAAW,IAAYN,EAAI,WAAW,GAAG,EAAIA,EAAM,IAAIA,IACvDA,EAAI,WAAWM,CAAM,EAAUN,EAC5B,GAAGM,IAASN,EAAI,QAAQ,MAAO,EAAE,GAC5C,CAGA,SAASO,EAAK,EAAuB,CACjC,IAAIC,EAAI,KACR,IAAK,IAAIC,EAAI,EAAGA,EAAIC,EAAM,OAAQ,IAC9B,GAAMF,GAAK,GAAKA,EAAIE,EAAM,WAAWD,CAAC,IAAO,EAEjD,OAAOD,EAAE,SAAS,EAAE,CACxB,CA0BA,SAAgBG,EAAmB,EAAqC,CAAC,EAAsB,CAC3F,GAAM,CACF,WAAW,yBACX,iBAAiB,CAAC,EAClB,UAAU,SACV,cAAc,GACd,WAAW,cACX,iBACAC,EAEAb,EAAO,IACPG,EAAY,GACZW,EAAiC,SAiErC,MAAO,CA9DH,KAAM,uBACN,MAAO,QACP,eAAe,EAAQ,CACnB,EAAOC,EAAO,MAAQ,IACtB,EAAYA,EAAO,WAAa,GAChC,IAAMC,GAAUD,EAAO,SAAW,CAAC,EAAA,CAAG,KACjC,GAASE,EAAK,OAASxB,EAAAA,sBAC5B,CAAC,EAAE,IACH,AAAiCqB,EAA7BI,IAAkB,IAAA,GACbF,EAAyBA,EAAO,MAAQA,EAAO,MAAQ,GAC1C,SAF4BE,CAGtD,EACA,eAAe,EAAgB,EAAQ,CACnC,IAAMC,EAAO,IAAI,IAAYC,EAAe,IAAK,GAAQd,EAASN,EAAMC,CAAG,CAAC,CAAC,EAEzEoB,GAAUF,EAAK,IAAIb,EAASN,EAAMqB,CAAQ,CAAC,EAC/C,IAAK,IAAMtB,KAAQ,OAAO,KAAKG,CAAM,EAC7BH,IAASuB,IACTC,EAAQ,KAAKxB,CAAI,IAChByB,GAAe,GAAK,SAAS,OAAO,IACzCL,EAAK,IAAId,EAASL,EAAMD,CAAI,CAAC,GAGjC,IAAM0B,EAAO,CAAC,GAAGN,CAAI,CAAC,CAAC,KAAK,EACtBO,EAAUlB,EAAKiB,EAAK,KAAK;CAAI,CAAC,EACpC,KAAK,SAAS,CACV,KAAM,QACN,WACA,OAAQ,KAAK,UAAU,CAAE,UAAS,KAAMA,CAAK,CAAC,CAClD,CAAC,CACL,EACA,YAAY,EAAgB,EAAQ,CAChC,IAAME,EAAUP,EAAe,OAC1B,GACG,CAAC,sBAAsB,KAAKnB,CAAG,GAC/B,CAACH,EAAeG,EAAKD,EAAME,EAAQC,CAAS,CACpD,EACIwB,EAAQ,OAAS,GACjB,KAAK,MACD,+CAA+CA,EAAQ,KAAK,IAAI,EAAE,sVAMtE,EAGAb,IAAmB,KACnB1B,EAAAA,aAAaY,EAAMc,CAAc,IAAKZ,GAC1C,KAAK,MACD,4CAA4CoB,EAAS,4BAC3CR,EAAe,2VAM7B,EACJ,CAGG,CACX,C"}