From 47a82dbeb3c2818245457569be1e7f01e4b7327c Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 19 Oct 2025 20:58:15 +0200 Subject: [PATCH 01/20] feat: add `workbox-build` package --- TODO.md | 1 + alias.ts | 17 + package.json | 6 +- packages/workbox-background-sync/package.json | 10 +- packages/workbox-background-sync/src/Queue.ts | 6 +- packages/workbox-background-sync/src/types.ts | 1 + .../workbox-background-sync/tsdown.config.ts | 2 +- .../workbox-broadcast-update/package.json | 10 +- .../workbox-broadcast-update/src/index.ts | 17 +- .../workbox-broadcast-update/src/types.ts | 1 + .../workbox-broadcast-update/tsdown.config.ts | 2 +- packages/workbox-build/package.json | 66 ++ packages/workbox-build/src/generate-sw.ts | 5 + packages/workbox-build/src/index.ts | 0 packages/workbox-build/src/inject-manifest.ts | 5 + .../src/schema/GenerateSWOptions.json | 883 ++++++++++++++++ .../src/schema/GetManifestOptions.json | 758 ++++++++++++++ .../src/schema/InjectManifestOptions.json | 773 ++++++++++++++ packages/workbox-build/src/types.ts | 588 +++++++++++ .../src/validation/generate-sw.ts | 266 +++++ .../src/validation/get-manifest.ts | 87 ++ .../src/validation/inject-manifest.ts | 109 ++ .../src/validation/validation-helper.ts | 48 + .../workbox-build/test/validation.test.ts | 180 ++++ packages/workbox-build/tsdown.config.ts | 13 + .../workbox-cacheable-response/package.json | 10 +- .../workbox-cacheable-response/src/index.ts | 6 +- .../workbox-cacheable-response/src/types.ts | 1 + .../tsdown.config.ts | 2 +- packages/workbox-core/package.json | 4 + packages/workbox-core/src/types.ts | 4 +- packages/workbox-core/tsdown.config.ts | 2 +- packages/workbox-expiration/package.json | 11 +- packages/workbox-expiration/src/index.ts | 12 +- packages/workbox-expiration/src/types.ts | 1 + packages/workbox-expiration/tsdown.config.ts | 2 +- packages/workbox-precaching/src/_types.ts | 2 +- packages/workbox-routing/package.json | 11 +- packages/workbox-routing/src/index.ts | 31 +- packages/workbox-routing/src/types.ts | 2 + packages/workbox-routing/tsdown.config.ts | 2 +- pnpm-lock.yaml | 984 +++++++++++++++++- pnpm-workspace.yaml | 14 +- tsconfig.json | 40 +- vitest.config.ts | 17 + 45 files changed, 4924 insertions(+), 88 deletions(-) create mode 100644 alias.ts create mode 100644 packages/workbox-background-sync/src/types.ts create mode 100644 packages/workbox-broadcast-update/src/types.ts create mode 100644 packages/workbox-build/package.json create mode 100644 packages/workbox-build/src/generate-sw.ts create mode 100644 packages/workbox-build/src/index.ts create mode 100644 packages/workbox-build/src/inject-manifest.ts create mode 100644 packages/workbox-build/src/schema/GenerateSWOptions.json create mode 100644 packages/workbox-build/src/schema/GetManifestOptions.json create mode 100644 packages/workbox-build/src/schema/InjectManifestOptions.json create mode 100644 packages/workbox-build/src/types.ts create mode 100644 packages/workbox-build/src/validation/generate-sw.ts create mode 100644 packages/workbox-build/src/validation/get-manifest.ts create mode 100644 packages/workbox-build/src/validation/inject-manifest.ts create mode 100644 packages/workbox-build/src/validation/validation-helper.ts create mode 100644 packages/workbox-build/test/validation.test.ts create mode 100644 packages/workbox-build/tsdown.config.ts create mode 100644 packages/workbox-cacheable-response/src/types.ts create mode 100644 packages/workbox-expiration/src/types.ts create mode 100644 packages/workbox-routing/src/types.ts create mode 100644 vitest.config.ts diff --git a/TODO.md b/TODO.md index 7f91507..d8af0b6 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,7 @@ ## TODO - review workbox-sw package, maybe we need to move it to commonjs? +- review service worker (registration) globals ## Pending task diff --git a/alias.ts b/alias.ts new file mode 100644 index 0000000..02feb64 --- /dev/null +++ b/alias.ts @@ -0,0 +1,17 @@ +import type { TSConfig } from 'pkg-types' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' +import { resolve } from 'pathe' + +const require = createRequire(import.meta.url) + +const compilerOptions: TSConfig['compilerOptions'] = require('./tsconfig.json').compilerOptions + +function r(p: string) { + return resolve(fileURLToPath(new URL('.', import.meta.url)), p) +} + +export const alias = Array.from(Object.entries(compilerOptions!.paths)).reduce((acc, [name, paths]) => { + acc[name] = r((paths as string[])[0]) + return acc +}, {} as Record) diff --git a/package.json b/package.json index f6f54de..ff30901 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "build": "pnpm -r --filter=./packages/* run build", "lint": "eslint .", "lint:fix": "nr lint --fix", + "test": "vitest", + "test:ui": "vitest --ui", "update-deps": "taze minor -wIr", "release": "bumpp -r --excute=\"pnpm i\"" }, @@ -34,12 +36,14 @@ "@types/node": "catalog:types", "@types/trusted-types": "catalog:types", "@typescript-eslint/utils": "catalog:typescript", + "@vitest/ui": "catalog:testing", "bumpp": "catalog:build", "eslint": "catalog:eslint", "eslint-plugin-antfu": "catalog:eslint", "taze": "catalog:utils", "tsdown": "catalog:build", - "typescript": "catalog:typescript" + "typescript": "catalog:typescript", + "vitest": "catalog:testing" }, "resolutions": { "@typescript-eslint/utils": "catalog:typescript" diff --git a/packages/workbox-background-sync/package.json b/packages/workbox-background-sync/package.json index d10f0ba..4f1c3af 100644 --- a/packages/workbox-background-sync/package.json +++ b/packages/workbox-background-sync/package.json @@ -27,11 +27,19 @@ ], "sideEffects": false, "exports": { - ".": "./dist/index.js" + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + } }, "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"] + } + }, "files": [ "dist" ], diff --git a/packages/workbox-background-sync/src/Queue.ts b/packages/workbox-background-sync/src/Queue.ts index 85d3b73..cffc46c 100644 --- a/packages/workbox-background-sync/src/Queue.ts +++ b/packages/workbox-background-sync/src/Queue.ts @@ -15,11 +15,11 @@ import { StorableRequest } from './lib/StorableRequest' // Give TypeScript the correct global. declare let self: ServiceWorkerGlobalScope -interface OnSyncCallbackOptions { +export interface OnSyncCallbackOptions { queue: Queue } -interface OnSyncCallback { +export interface OnSyncCallback { (options: OnSyncCallbackOptions): void | Promise } @@ -29,7 +29,7 @@ export interface QueueOptions { onSync?: OnSyncCallback } -interface QueueEntry { +export interface QueueEntry { request: Request timestamp?: number // We could use Record as a type but that would be a breaking diff --git a/packages/workbox-background-sync/src/types.ts b/packages/workbox-background-sync/src/types.ts new file mode 100644 index 0000000..bead194 --- /dev/null +++ b/packages/workbox-background-sync/src/types.ts @@ -0,0 +1 @@ +export type { OnSyncCallback, OnSyncCallbackOptions, QueueEntry, QueueOptions } from './Queue' diff --git a/packages/workbox-background-sync/tsdown.config.ts b/packages/workbox-background-sync/tsdown.config.ts index 1ba9d65..cfa94bb 100644 --- a/packages/workbox-background-sync/tsdown.config.ts +++ b/packages/workbox-background-sync/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/index.ts', + entry: './src/{index,types}.ts', platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team diff --git a/packages/workbox-broadcast-update/package.json b/packages/workbox-broadcast-update/package.json index 26b5d28..de34023 100644 --- a/packages/workbox-broadcast-update/package.json +++ b/packages/workbox-broadcast-update/package.json @@ -25,11 +25,19 @@ ], "sideEffects": false, "exports": { - ".": "./dist/index.js" + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + } }, "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"] + } + }, "files": [ "dist" ], diff --git a/packages/workbox-broadcast-update/src/index.ts b/packages/workbox-broadcast-update/src/index.ts index df3ea54..63b3e92 100644 --- a/packages/workbox-broadcast-update/src/index.ts +++ b/packages/workbox-broadcast-update/src/index.ts @@ -6,16 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { - BroadcastCacheUpdate, - BroadcastCacheUpdateOptions, -} from './BroadcastCacheUpdate' -import { BroadcastUpdatePlugin } from './BroadcastUpdatePlugin' -import { responsesAreSame } from './responsesAreSame' - -export { - BroadcastCacheUpdate, - BroadcastCacheUpdateOptions, - BroadcastUpdatePlugin, - responsesAreSame, -} +export type { BroadcastCacheUpdateOptions } from './BroadcastCacheUpdate' +export { BroadcastCacheUpdate } from './BroadcastCacheUpdate' +export { BroadcastUpdatePlugin } from './BroadcastUpdatePlugin' +export { responsesAreSame } from './responsesAreSame' diff --git a/packages/workbox-broadcast-update/src/types.ts b/packages/workbox-broadcast-update/src/types.ts new file mode 100644 index 0000000..4ab276a --- /dev/null +++ b/packages/workbox-broadcast-update/src/types.ts @@ -0,0 +1 @@ +export type { BroadcastCacheUpdateOptions } from './BroadcastCacheUpdate' diff --git a/packages/workbox-broadcast-update/tsdown.config.ts b/packages/workbox-broadcast-update/tsdown.config.ts index 1ba9d65..cfa94bb 100644 --- a/packages/workbox-broadcast-update/tsdown.config.ts +++ b/packages/workbox-broadcast-update/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/index.ts', + entry: './src/{index,types}.ts', platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json new file mode 100644 index 0000000..d03878b --- /dev/null +++ b/packages/workbox-build/package.json @@ -0,0 +1,66 @@ +{ + "name": "@vite-pwa/workbox-build", + "type": "module", + "version": "0.0.0", + "description": "A module that integrates into your build process, helping you generate a manifest of local files that workbox-sw should precache.", + "author": "Google's Web DevRel Team, Vite PWA's Team", + "contributors": [ + "userquin " + ], + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/vite-pwa/workbox#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vite-pwa/workbox.git", + "directory": "packages/workbox-build" + }, + "bugs": "https://github.com/vite-pwa/workbox/issues", + "keywords": [ + "workbox", + "workboxjs", + "service worker", + "caching", + "fetch requests", + "offline", + "file manifest" + ], + "sideEffects": false, + "exports": { + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + }, + "./generate-sw": "./dist/generate-sw.js", + "./inject-manifest": "./dist/inject-manifest.js" + }, + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"], + "generate-sw": ["dist/generate-sw.d.ts"], + "inject-manifest": ["dist/inject-manifest.d.ts"] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsdown", + "prepublishOnly": "pnpm run build" + }, + "dependencies": { + "@vite-pwa/workbox-background-sync": "workspace:*", + "@vite-pwa/workbox-broadcast-update": "workspace:*", + "@vite-pwa/workbox-cacheable-response": "workspace:*", + "@vite-pwa/workbox-core": "workspace:*", + "@vite-pwa/workbox-expiration": "workspace:*", + "@vite-pwa/workbox-routing": "workspace:*", + "magicast": "catalog:utils", + "pathe": "catalog:utils", + "pkg-types": "catalog:utils", + "valibot": "catalog:utils" + } +} diff --git a/packages/workbox-build/src/generate-sw.ts b/packages/workbox-build/src/generate-sw.ts new file mode 100644 index 0000000..efa7324 --- /dev/null +++ b/packages/workbox-build/src/generate-sw.ts @@ -0,0 +1,5 @@ +import type { GenerateSWOptions } from './types' + +export async function generateSW(config: GenerateSWOptions) { + +} diff --git a/packages/workbox-build/src/index.ts b/packages/workbox-build/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/workbox-build/src/inject-manifest.ts b/packages/workbox-build/src/inject-manifest.ts new file mode 100644 index 0000000..3f062e8 --- /dev/null +++ b/packages/workbox-build/src/inject-manifest.ts @@ -0,0 +1,5 @@ +import type { InjectManifestOptions } from './types' + +export async function injectManifest(config: InjectManifestOptions) { + +} diff --git a/packages/workbox-build/src/schema/GenerateSWOptions.json b/packages/workbox-build/src/schema/GenerateSWOptions.json new file mode 100644 index 0000000..e58464b --- /dev/null +++ b/packages/workbox-build/src/schema/GenerateSWOptions.json @@ -0,0 +1,883 @@ +{ + "additionalProperties": false, + "type": "object", + "properties": { + "additionalManifestEntries": { + "description": "A list of entries to be precached, in addition to any entries that are\ngenerated as part of the build configuration.", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ManifestEntry" + }, + { + "type": "string" + } + ] + } + }, + "dontCacheBustURLsMatching": { + "description": "Assets that match this will be assumed to be uniquely versioned via their\nURL, and exempted from the normal HTTP cache-busting that's done when\npopulating the precache. While not required, it's recommended that if your\nexisting build process already inserts a `[hash]` value into each filename,\nyou provide a RegExp that will detect that, as it will reduce the bandwidth\nconsumed when precaching.", + "$ref": "#/definitions/RegExp" + }, + "manifestTransforms": { + "description": "One or more functions which will be applied sequentially against the\ngenerated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are\nalso specified, their corresponding transformations will be applied first.", + "type": "array", + "items": {} + }, + "maximumFileSizeToCacheInBytes": { + "description": "This value can be used to determine the maximum size of files that will be\nprecached. This prevents you from inadvertently precaching very large files\nthat might have accidentally matched one of your patterns.", + "default": 2097152, + "type": "number" + }, + "modifyURLPrefix": { + "description": "An object mapping string prefixes to replacement string values. This can be\nused to, e.g., remove or add a path prefix from a manifest entry if your\nweb hosting setup doesn't match your local filesystem setup. As an\nalternative with more flexibility, you can use the `manifestTransforms`\noption and provide a function that modifies the entries in the manifest\nusing whatever logic you provide.\n\nExample usage:\n\n```\n// Replace a '/dist/' prefix with '/', and also prepend\n// '/static' to every URL.\nmodifyURLPrefix: {\n '/dist/': '/',\n '': '/static',\n}\n```", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "globFollow": { + "description": "Determines whether or not symlinks are followed when generating the\nprecache manifest. For more information, see the definition of `follow` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).", + "default": true, + "type": "boolean" + }, + "globIgnores": { + "description": "A set of patterns matching files to always exclude when generating the\nprecache manifest. For more information, see the definition of `ignore` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).", + "default": [ + "**/node_modules/**/*" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "globPatterns": { + "description": "Files matching any of these patterns will be included in the precache\nmanifest. For more information, see the\n[`glob` primer](https://github.com/isaacs/node-glob#glob-primer).", + "default": [ + "**/*.{js,css,html}" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "globStrict": { + "description": "If true, an error reading a directory when generating a precache manifest\nwill cause the build to fail. If false, the problematic directory will be\nskipped. For more information, see the definition of `strict` in the `glob`\n[documentation](https://github.com/isaacs/node-glob#options).", + "default": true, + "type": "boolean" + }, + "templatedURLs": { + "description": "If a URL is rendered based on some server-side logic, its contents may\ndepend on multiple files or on some other unique string value. The keys in\nthis object are server-rendered URLs. If the values are an array of\nstrings, they will be interpreted as `glob` patterns, and the contents of\nany files matching the patterns will be used to uniquely version the URL.\nIf used with a single string, it will be interpreted as unique versioning\ninformation that you've generated for a given URL.", + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + } + }, + "babelPresetEnvTargets": { + "description": "The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass\nto `babel-preset-env` when transpiling the service worker bundle.", + "default": [ + "chrome >= 56" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "cacheId": { + "description": "An optional ID to be prepended to cache names. This is primarily useful for\nlocal development where multiple sites may be served from the same\n`http://localhost:port` origin.", + "type": [ + "null", + "string" + ] + }, + "cleanupOutdatedCaches": { + "description": "Whether or not Workbox should attempt to identify and delete any precaches\ncreated by older, incompatible versions.", + "default": false, + "type": "boolean" + }, + "clientsClaim": { + "description": "Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim)\nany existing clients as soon as it activates.", + "default": false, + "type": "boolean" + }, + "directoryIndex": { + "description": "If a navigation request for a URL ending in `/` fails to match a precached\nURL, this value will be appended to the URL and that will be checked for a\nprecache match. This should be set to what your web server is using for its\ndirectory index.", + "type": [ + "null", + "string" + ] + }, + "disableDevLogs": { + "default": false, + "type": "boolean" + }, + "ignoreURLParametersMatching": { + "description": "Any search parameter names that match against one of the RegExp in this\narray will be removed before looking for a precache match. This is useful\nif your users might request URLs that contain, for example, URL parameters\nused to track the source of the traffic. If not provided, the default value\nis `[/^utm_/, /^fbclid$/]`.", + "type": "array", + "items": { + "$ref": "#/definitions/RegExp" + } + }, + "importScripts": { + "description": "A list of JavaScript files that should be passed to\n[`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts)\ninside the generated service worker file. This is useful when you want to\nlet Workbox create your top-level service worker file, but want to include\nsome additional code, such as a push event listener.", + "type": "array", + "items": { + "type": "string" + } + }, + "inlineWorkboxRuntime": { + "description": "Whether the runtime code for the Workbox library should be included in the\ntop-level service worker, or split into a separate file that needs to be\ndeployed alongside the service worker. Keeping the runtime separate means\nthat users will not have to re-download the Workbox code each time your\ntop-level service worker changes.", + "default": false, + "type": "boolean" + }, + "mode": { + "description": "If set to 'production', then an optimized service worker bundle that\nexcludes debugging info will be produced. If not explicitly configured\nhere, the `process.env.NODE_ENV` value will be used, and failing that, it\nwill fall back to `'production'`.", + "default": "production", + "type": [ + "null", + "string" + ] + }, + "navigateFallback": { + "description": "If specified, all\n[navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests)\nfor URLs that aren't precached will be fulfilled with the HTML at the URL\nprovided. You must pass in the URL of an HTML document that is listed in\nyour precache manifest. This is meant to be used in a Single Page App\nscenario, in which you want all navigations to use common\n[App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell).", + "default": null, + "type": [ + "null", + "string" + ] + }, + "navigateFallbackAllowlist": { + "description": "An optional array of regular expressions that restricts which URLs the\nconfigured `navigateFallback` behavior applies to. This is useful if only a\nsubset of your site's URLs should be treated as being part of a\n[Single Page App](https://en.wikipedia.org/wiki/Single-page_application).\nIf both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are\nconfigured, the denylist takes precedent.\n\n*Note*: These RegExps may be evaluated against every destination URL during\na navigation. Avoid using\n[complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),\nor else your users may see delays when navigating your site.", + "type": "array", + "items": { + "$ref": "#/definitions/RegExp" + } + }, + "navigateFallbackDenylist": { + "description": "An optional array of regular expressions that restricts which URLs the\nconfigured `navigateFallback` behavior applies to. This is useful if only a\nsubset of your site's URLs should be treated as being part of a\n[Single Page App](https://en.wikipedia.org/wiki/Single-page_application).\nIf both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are\nconfigured, the denylist takes precedence.\n\n*Note*: These RegExps may be evaluated against every destination URL during\na navigation. Avoid using\n[complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),\nor else your users may see delays when navigating your site.", + "type": "array", + "items": { + "$ref": "#/definitions/RegExp" + } + }, + "navigationPreload": { + "description": "Whether or not to enable\n[navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload)\nin the generated service worker. When set to true, you must also use\n`runtimeCaching` to set up an appropriate response strategy that will match\nnavigation requests, and make use of the preloaded response.", + "default": false, + "type": "boolean" + }, + "offlineGoogleAnalytics": { + "description": "Controls whether or not to include support for\n[offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics).\nWhen `true`, the call to `workbox-google-analytics`'s `initialize()` will\nbe added to your generated service worker. When set to an `Object`, that\nobject will be passed in to the `initialize()` call, allowing you to\ncustomize the behavior.", + "default": false, + "anyOf": [ + { + "$ref": "#/definitions/GoogleAnalyticsInitializeOptions" + }, + { + "type": "boolean" + } + ] + }, + "runtimeCaching": { + "description": "When using Workbox's build tools to generate your service worker, you can\nspecify one or more runtime caching configurations. These are then\ntranslated to {@link workbox-routing.registerRoute} calls using the match\nand handler configuration you define.\n\nFor all of the options, see the {@link workbox-build.RuntimeCaching}\ndocumentation. The example below shows a typical configuration, with two\nruntime routes defined:", + "type": "array", + "items": { + "$ref": "#/definitions/RuntimeCaching" + } + }, + "skipWaiting": { + "description": "Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase)\nto the generated service worker. If `false`, then a `message` listener will\nbe added instead, allowing client pages to trigger `skipWaiting()` by\ncalling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker.", + "default": false, + "type": "boolean" + }, + "sourcemap": { + "description": "Whether to create a sourcemap for the generated service worker files.", + "default": true, + "type": "boolean" + }, + "swDest": { + "description": "The path and filename of the service worker file that will be created by\nthe build process, relative to the current working directory. It must end\nin '.js'.", + "type": "string" + }, + "globDirectory": { + "description": "The local directory you wish to match `globPatterns` against. The path is\nrelative to the current directory.", + "type": "string" + } + }, + "required": [ + "swDest" + ], + "definitions": { + "ManifestEntry": { + "type": "object", + "properties": { + "integrity": { + "type": "string" + }, + "revision": { + "type": [ + "null", + "string" + ] + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "revision", + "url" + ] + }, + "RegExp": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "global": { + "type": "boolean" + }, + "ignoreCase": { + "type": "boolean" + }, + "multiline": { + "type": "boolean" + }, + "lastIndex": { + "type": "number" + }, + "flags": { + "type": "string" + }, + "sticky": { + "type": "boolean" + }, + "unicode": { + "type": "boolean" + }, + "dotAll": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "dotAll", + "flags", + "global", + "ignoreCase", + "lastIndex", + "multiline", + "source", + "sticky", + "unicode" + ] + }, + "GoogleAnalyticsInitializeOptions": { + "type": "object", + "properties": { + "cacheName": { + "type": "string" + }, + "parameterOverrides": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "hitFilter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "RuntimeCaching": { + "type": "object", + "properties": { + "handler": { + "description": "This determines how the runtime route will generate a response.\nTo use one of the built-in {@link workbox-strategies}, provide its name,\nlike `'NetworkFirst'`.\nAlternatively, this can be a {@link workbox-core.RouteHandler} callback\nfunction with custom response logic.", + "anyOf": [ + { + "$ref": "#/definitions/RouteHandlerCallback" + }, + { + "$ref": "#/definitions/RouteHandlerObject" + }, + { + "enum": [ + "CacheFirst", + "CacheOnly", + "NetworkFirst", + "NetworkOnly", + "StaleWhileRevalidate" + ], + "type": "string" + } + ] + }, + "method": { + "description": "The HTTP method to match against. The default value of `'GET'` is normally\nsufficient, unless you explicitly need to match `'POST'`, `'PUT'`, or\nanother type of request.", + "default": "GET", + "enum": [ + "DELETE", + "GET", + "HEAD", + "PATCH", + "POST", + "PUT" + ], + "type": "string" + }, + "options": { + "type": "object", + "properties": { + "backgroundSync": { + "description": "Configuring this will add a\n{@link workbox-background-sync.BackgroundSyncPlugin} instance to the\n{@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "options": { + "$ref": "#/definitions/QueueOptions" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + "broadcastUpdate": { + "description": "Configuring this will add a\n{@link workbox-broadcast-update.BroadcastUpdatePlugin} instance to the\n{@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "channelName": { + "type": "string" + }, + "options": { + "$ref": "#/definitions/BroadcastCacheUpdateOptions" + } + }, + "additionalProperties": false, + "required": [ + "options" + ] + }, + "cacheableResponse": { + "description": "Configuring this will add a\n{@link workbox-cacheable-response.CacheableResponsePlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/CacheableResponseOptions" + }, + "cacheName": { + "description": "If provided, this will set the `cacheName` property of the\n{@link workbox-strategies} configured in `handler`.", + "type": [ + "null", + "string" + ] + }, + "expiration": { + "description": "Configuring this will add a\n{@link workbox-expiration.ExpirationPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/ExpirationPluginOptions" + }, + "networkTimeoutSeconds": { + "description": "If provided, this will set the `networkTimeoutSeconds` property of the\n{@link workbox-strategies} configured in `handler`. Note that only\n`'NetworkFirst'` and `'NetworkOnly'` support `networkTimeoutSeconds`.", + "type": "number" + }, + "plugins": { + "description": "Configuring this allows the use of one or more Workbox plugins that\ndon't have \"shortcut\" options (like `expiration` for\n{@link workbox-expiration.ExpirationPlugin}). The plugins provided here\nwill be added to the {@link workbox-strategies} configured in `handler`.", + "type": "array", + "items": { + "$ref": "#/definitions/WorkboxPlugin" + } + }, + "precacheFallback": { + "description": "Configuring this will add a\n{@link workbox-precaching.PrecacheFallbackPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "fallbackURL": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "fallbackURL" + ] + }, + "rangeRequests": { + "description": "Enabling this will add a\n{@link workbox-range-requests.RangeRequestsPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "type": "boolean" + }, + "fetchOptions": { + "description": "Configuring this will pass along the `fetchOptions` value to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/RequestInit" + }, + "matchOptions": { + "description": "Configuring this will pass along the `matchOptions` value to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/CacheQueryOptions" + } + }, + "additionalProperties": false + }, + "urlPattern": { + "description": "This match criteria determines whether the configured handler will\ngenerate a response for any requests that don't match one of the precached\nURLs. If multiple `RuntimeCaching` routes are defined, then the first one\nwhose `urlPattern` matches will be the one that responds.\n\nThis value directly maps to the first parameter passed to\n{@link workbox-routing.registerRoute}. It's recommended to use a\n{@link workbox-core.RouteMatchCallback} function for greatest flexibility.", + "anyOf": [ + { + "$ref": "#/definitions/RegExp" + }, + { + "$ref": "#/definitions/RouteMatchCallback" + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "handler", + "urlPattern" + ] + }, + "RouteHandlerCallback": {}, + "RouteHandlerObject": { + "description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).", + "type": "object", + "properties": { + "handle": { + "$ref": "#/definitions/RouteHandlerCallback" + } + }, + "additionalProperties": false, + "required": [ + "handle" + ] + }, + "QueueOptions": { + "type": "object", + "properties": { + "forceSyncFallback": { + "type": "boolean" + }, + "maxRetentionTime": { + "type": "number" + }, + "onSync": { + "$ref": "#/definitions/OnSyncCallback" + } + }, + "additionalProperties": false + }, + "OnSyncCallback": {}, + "BroadcastCacheUpdateOptions": { + "type": "object", + "properties": { + "headersToCheck": { + "type": "array", + "items": { + "type": "string" + } + }, + "generatePayload": { + "type": "object", + "additionalProperties": false + }, + "notifyAllClients": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CacheableResponseOptions": { + "type": "object", + "properties": { + "statuses": { + "type": "array", + "items": { + "type": "number" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "ExpirationPluginOptions": { + "type": "object", + "properties": { + "maxEntries": { + "type": "number" + }, + "maxAgeSeconds": { + "type": "number" + }, + "matchOptions": { + "$ref": "#/definitions/CacheQueryOptions" + }, + "purgeOnQuotaError": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CacheQueryOptions": { + "type": "object", + "properties": { + "ignoreMethod": { + "type": "boolean" + }, + "ignoreSearch": { + "type": "boolean" + }, + "ignoreVary": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "WorkboxPlugin": { + "description": "An object with optional lifecycle callback properties for the fetch and\ncache operations.", + "type": "object", + "properties": { + "cacheDidUpdate": {}, + "cachedResponseWillBeUsed": {}, + "cacheKeyWillBeUsed": {}, + "cacheWillUpdate": {}, + "fetchDidFail": {}, + "fetchDidSucceed": {}, + "handlerDidComplete": {}, + "handlerDidError": {}, + "handlerDidRespond": {}, + "handlerWillRespond": {}, + "handlerWillStart": {}, + "requestWillFetch": {} + }, + "additionalProperties": false + }, + "CacheDidUpdateCallback": { + "type": "object", + "additionalProperties": false + }, + "CachedResponseWillBeUsedCallback": { + "type": "object", + "additionalProperties": false + }, + "CacheKeyWillBeUsedCallback": { + "type": "object", + "additionalProperties": false + }, + "CacheWillUpdateCallback": { + "type": "object", + "additionalProperties": false + }, + "FetchDidFailCallback": { + "type": "object", + "additionalProperties": false + }, + "FetchDidSucceedCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidCompleteCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidErrorCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidRespondCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerWillRespondCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerWillStartCallback": { + "type": "object", + "additionalProperties": false + }, + "RequestWillFetchCallback": { + "type": "object", + "additionalProperties": false + }, + "RequestInit": { + "type": "object", + "properties": { + "body": { + "anyOf": [ + { + "$ref": "#/definitions/ArrayBuffer" + }, + { + "$ref": "#/definitions/ArrayBufferView" + }, + { + "$ref": "#/definitions/Blob" + }, + { + "$ref": "#/definitions/FormData" + }, + { + "$ref": "#/definitions/URLSearchParams" + }, + { + "$ref": "#/definitions/ReadableStream" + }, + { + "type": [ + "null", + "string" + ] + } + ] + }, + "cache": { + "enum": [ + "default", + "force-cache", + "no-cache", + "no-store", + "only-if-cached", + "reload" + ], + "type": "string" + }, + "credentials": { + "enum": [ + "include", + "omit", + "same-origin" + ], + "type": "string" + }, + "headers": { + "anyOf": [ + { + "$ref": "#/definitions/Headers" + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "$ref": "#/definitions/Record" + } + ] + }, + "integrity": { + "type": "string" + }, + "keepalive": { + "type": "boolean" + }, + "method": { + "type": "string" + }, + "mode": { + "enum": [ + "cors", + "navigate", + "no-cors", + "same-origin" + ], + "type": "string" + }, + "redirect": { + "enum": [ + "error", + "follow", + "manual" + ], + "type": "string" + }, + "referrer": { + "type": "string" + }, + "referrerPolicy": { + "enum": [ + "", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "same-origin", + "strict-origin", + "strict-origin-when-cross-origin", + "unsafe-url" + ], + "type": "string" + }, + "signal": { + "anyOf": [ + { + "$ref": "#/definitions/AbortSignal" + }, + { + "type": "null" + } + ] + }, + "window": {} + }, + "additionalProperties": false + }, + "ArrayBuffer": { + "type": "object", + "properties": { + "byteLength": { + "type": "number" + }, + "__@toStringTag": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "__@toStringTag", + "byteLength" + ] + }, + "ArrayBufferView": { + "type": "object", + "properties": { + "buffer": { + "anyOf": [ + { + "$ref": "#/definitions/ArrayBuffer" + }, + { + "$ref": "#/definitions/SharedArrayBuffer" + } + ] + }, + "byteLength": { + "type": "number" + }, + "byteOffset": { + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "buffer", + "byteLength", + "byteOffset" + ] + }, + "SharedArrayBuffer": { + "type": "object", + "properties": { + "byteLength": { + "type": "number" + }, + "__@species": { + "$ref": "#/definitions/SharedArrayBuffer" + }, + "__@toStringTag": { + "type": "string", + "enum": [ + "SharedArrayBuffer" + ] + } + }, + "additionalProperties": false, + "required": [ + "__@species", + "__@toStringTag", + "byteLength" + ] + }, + "Blob": { + "type": "object", + "properties": { + "size": { + "type": "number" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "size", + "type" + ] + }, + "FormData": { + "type": "object", + "additionalProperties": false + }, + "URLSearchParams": { + "type": "object", + "additionalProperties": false + }, + "ReadableStream": { + "type": "object", + "properties": { + "locked": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "locked" + ] + }, + "Headers": { + "type": "object", + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, + "AbortSignal": { + "type": "object", + "properties": { + "aborted": { + "type": "boolean" + }, + "onabort": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "aborted", + "onabort" + ] + }, + "RouteMatchCallback": {} + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} diff --git a/packages/workbox-build/src/schema/GetManifestOptions.json b/packages/workbox-build/src/schema/GetManifestOptions.json new file mode 100644 index 0000000..d813cb9 --- /dev/null +++ b/packages/workbox-build/src/schema/GetManifestOptions.json @@ -0,0 +1,758 @@ +{ + "additionalProperties": false, + "type": "object", + "properties": { + "additionalManifestEntries": { + "description": "A list of entries to be precached, in addition to any entries that are\ngenerated as part of the build configuration.", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ManifestEntry" + }, + { + "type": "string" + } + ] + } + }, + "dontCacheBustURLsMatching": { + "description": "Assets that match this will be assumed to be uniquely versioned via their\nURL, and exempted from the normal HTTP cache-busting that's done when\npopulating the precache. While not required, it's recommended that if your\nexisting build process already inserts a `[hash]` value into each filename,\nyou provide a RegExp that will detect that, as it will reduce the bandwidth\nconsumed when precaching.", + "$ref": "#/definitions/RegExp" + }, + "manifestTransforms": { + "description": "One or more functions which will be applied sequentially against the\ngenerated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are\nalso specified, their corresponding transformations will be applied first.", + "type": "array", + "items": {} + }, + "maximumFileSizeToCacheInBytes": { + "description": "This value can be used to determine the maximum size of files that will be\nprecached. This prevents you from inadvertently precaching very large files\nthat might have accidentally matched one of your patterns.", + "default": 2097152, + "type": "number" + }, + "modifyURLPrefix": { + "description": "An object mapping string prefixes to replacement string values. This can be\nused to, e.g., remove or add a path prefix from a manifest entry if your\nweb hosting setup doesn't match your local filesystem setup. As an\nalternative with more flexibility, you can use the `manifestTransforms`\noption and provide a function that modifies the entries in the manifest\nusing whatever logic you provide.\n\nExample usage:\n\n```\n// Replace a '/dist/' prefix with '/', and also prepend\n// '/static' to every URL.\nmodifyURLPrefix: {\n '/dist/': '/',\n '': '/static',\n}\n```", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "globFollow": { + "description": "Determines whether or not symlinks are followed when generating the\nprecache manifest. For more information, see the definition of `follow` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).", + "default": true, + "type": "boolean" + }, + "globIgnores": { + "description": "A set of patterns matching files to always exclude when generating the\nprecache manifest. For more information, see the definition of `ignore` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).", + "default": [ + "**/node_modules/**/*" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "globPatterns": { + "description": "Files matching any of these patterns will be included in the precache\nmanifest. For more information, see the\n[`glob` primer](https://github.com/isaacs/node-glob#glob-primer).", + "default": [ + "**/*.{js,css,html}" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "globStrict": { + "description": "If true, an error reading a directory when generating a precache manifest\nwill cause the build to fail. If false, the problematic directory will be\nskipped. For more information, see the definition of `strict` in the `glob`\n[documentation](https://github.com/isaacs/node-glob#options).", + "default": true, + "type": "boolean" + }, + "templatedURLs": { + "description": "If a URL is rendered based on some server-side logic, its contents may\ndepend on multiple files or on some other unique string value. The keys in\nthis object are server-rendered URLs. If the values are an array of\nstrings, they will be interpreted as `glob` patterns, and the contents of\nany files matching the patterns will be used to uniquely version the URL.\nIf used with a single string, it will be interpreted as unique versioning\ninformation that you've generated for a given URL.", + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + } + }, + "globDirectory": { + "description": "The local directory you wish to match `globPatterns` against. The path is\nrelative to the current directory.", + "type": "string" + } + }, + "required": [ + "globDirectory" + ], + "definitions": { + "ManifestEntry": { + "type": "object", + "properties": { + "integrity": { + "type": "string" + }, + "revision": { + "type": [ + "null", + "string" + ] + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "revision", + "url" + ] + }, + "RegExp": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "global": { + "type": "boolean" + }, + "ignoreCase": { + "type": "boolean" + }, + "multiline": { + "type": "boolean" + }, + "lastIndex": { + "type": "number" + }, + "flags": { + "type": "string" + }, + "sticky": { + "type": "boolean" + }, + "unicode": { + "type": "boolean" + }, + "dotAll": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "dotAll", + "flags", + "global", + "ignoreCase", + "lastIndex", + "multiline", + "source", + "sticky", + "unicode" + ] + }, + "GoogleAnalyticsInitializeOptions": { + "type": "object", + "properties": { + "cacheName": { + "type": "string" + }, + "parameterOverrides": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "hitFilter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "RuntimeCaching": { + "type": "object", + "properties": { + "handler": { + "description": "This determines how the runtime route will generate a response.\nTo use one of the built-in {@link workbox-strategies}, provide its name,\nlike `'NetworkFirst'`.\nAlternatively, this can be a {@link workbox-core.RouteHandler} callback\nfunction with custom response logic.", + "anyOf": [ + { + "$ref": "#/definitions/RouteHandlerCallback" + }, + { + "$ref": "#/definitions/RouteHandlerObject" + }, + { + "enum": [ + "CacheFirst", + "CacheOnly", + "NetworkFirst", + "NetworkOnly", + "StaleWhileRevalidate" + ], + "type": "string" + } + ] + }, + "method": { + "description": "The HTTP method to match against. The default value of `'GET'` is normally\nsufficient, unless you explicitly need to match `'POST'`, `'PUT'`, or\nanother type of request.", + "default": "GET", + "enum": [ + "DELETE", + "GET", + "HEAD", + "PATCH", + "POST", + "PUT" + ], + "type": "string" + }, + "options": { + "type": "object", + "properties": { + "backgroundSync": { + "description": "Configuring this will add a\n{@link workbox-background-sync.BackgroundSyncPlugin} instance to the\n{@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "options": { + "$ref": "#/definitions/QueueOptions" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + "broadcastUpdate": { + "description": "Configuring this will add a\n{@link workbox-broadcast-update.BroadcastUpdatePlugin} instance to the\n{@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "channelName": { + "type": "string" + }, + "options": { + "$ref": "#/definitions/BroadcastCacheUpdateOptions" + } + }, + "additionalProperties": false, + "required": [ + "options" + ] + }, + "cacheableResponse": { + "description": "Configuring this will add a\n{@link workbox-cacheable-response.CacheableResponsePlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/CacheableResponseOptions" + }, + "cacheName": { + "description": "If provided, this will set the `cacheName` property of the\n{@link workbox-strategies} configured in `handler`.", + "type": [ + "null", + "string" + ] + }, + "expiration": { + "description": "Configuring this will add a\n{@link workbox-expiration.ExpirationPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/ExpirationPluginOptions" + }, + "networkTimeoutSeconds": { + "description": "If provided, this will set the `networkTimeoutSeconds` property of the\n{@link workbox-strategies} configured in `handler`. Note that only\n`'NetworkFirst'` and `'NetworkOnly'` support `networkTimeoutSeconds`.", + "type": "number" + }, + "plugins": { + "description": "Configuring this allows the use of one or more Workbox plugins that\ndon't have \"shortcut\" options (like `expiration` for\n{@link workbox-expiration.ExpirationPlugin}). The plugins provided here\nwill be added to the {@link workbox-strategies} configured in `handler`.", + "type": "array", + "items": { + "$ref": "#/definitions/WorkboxPlugin" + } + }, + "precacheFallback": { + "description": "Configuring this will add a\n{@link workbox-precaching.PrecacheFallbackPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "fallbackURL": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "fallbackURL" + ] + }, + "rangeRequests": { + "description": "Enabling this will add a\n{@link workbox-range-requests.RangeRequestsPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "type": "boolean" + }, + "fetchOptions": { + "description": "Configuring this will pass along the `fetchOptions` value to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/RequestInit" + }, + "matchOptions": { + "description": "Configuring this will pass along the `matchOptions` value to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/CacheQueryOptions" + } + }, + "additionalProperties": false + }, + "urlPattern": { + "description": "This match criteria determines whether the configured handler will\ngenerate a response for any requests that don't match one of the precached\nURLs. If multiple `RuntimeCaching` routes are defined, then the first one\nwhose `urlPattern` matches will be the one that responds.\n\nThis value directly maps to the first parameter passed to\n{@link workbox-routing.registerRoute}. It's recommended to use a\n{@link workbox-core.RouteMatchCallback} function for greatest flexibility.", + "anyOf": [ + { + "$ref": "#/definitions/RegExp" + }, + { + "$ref": "#/definitions/RouteMatchCallback" + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "handler", + "urlPattern" + ] + }, + "RouteHandlerCallback": {}, + "RouteHandlerObject": { + "description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).", + "type": "object", + "properties": { + "handle": { + "$ref": "#/definitions/RouteHandlerCallback" + } + }, + "additionalProperties": false, + "required": [ + "handle" + ] + }, + "QueueOptions": { + "type": "object", + "properties": { + "forceSyncFallback": { + "type": "boolean" + }, + "maxRetentionTime": { + "type": "number" + }, + "onSync": { + "$ref": "#/definitions/OnSyncCallback" + } + }, + "additionalProperties": false + }, + "OnSyncCallback": {}, + "BroadcastCacheUpdateOptions": { + "type": "object", + "properties": { + "headersToCheck": { + "type": "array", + "items": { + "type": "string" + } + }, + "generatePayload": { + "type": "object", + "additionalProperties": false + }, + "notifyAllClients": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CacheableResponseOptions": { + "type": "object", + "properties": { + "statuses": { + "type": "array", + "items": { + "type": "number" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "ExpirationPluginOptions": { + "type": "object", + "properties": { + "maxEntries": { + "type": "number" + }, + "maxAgeSeconds": { + "type": "number" + }, + "matchOptions": { + "$ref": "#/definitions/CacheQueryOptions" + }, + "purgeOnQuotaError": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CacheQueryOptions": { + "type": "object", + "properties": { + "ignoreMethod": { + "type": "boolean" + }, + "ignoreSearch": { + "type": "boolean" + }, + "ignoreVary": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "WorkboxPlugin": { + "description": "An object with optional lifecycle callback properties for the fetch and\ncache operations.", + "type": "object", + "properties": { + "cacheDidUpdate": {}, + "cachedResponseWillBeUsed": {}, + "cacheKeyWillBeUsed": {}, + "cacheWillUpdate": {}, + "fetchDidFail": {}, + "fetchDidSucceed": {}, + "handlerDidComplete": {}, + "handlerDidError": {}, + "handlerDidRespond": {}, + "handlerWillRespond": {}, + "handlerWillStart": {}, + "requestWillFetch": {} + }, + "additionalProperties": false + }, + "CacheDidUpdateCallback": { + "type": "object", + "additionalProperties": false + }, + "CachedResponseWillBeUsedCallback": { + "type": "object", + "additionalProperties": false + }, + "CacheKeyWillBeUsedCallback": { + "type": "object", + "additionalProperties": false + }, + "CacheWillUpdateCallback": { + "type": "object", + "additionalProperties": false + }, + "FetchDidFailCallback": { + "type": "object", + "additionalProperties": false + }, + "FetchDidSucceedCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidCompleteCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidErrorCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidRespondCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerWillRespondCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerWillStartCallback": { + "type": "object", + "additionalProperties": false + }, + "RequestWillFetchCallback": { + "type": "object", + "additionalProperties": false + }, + "RequestInit": { + "type": "object", + "properties": { + "body": { + "anyOf": [ + { + "$ref": "#/definitions/ArrayBuffer" + }, + { + "$ref": "#/definitions/ArrayBufferView" + }, + { + "$ref": "#/definitions/Blob" + }, + { + "$ref": "#/definitions/FormData" + }, + { + "$ref": "#/definitions/URLSearchParams" + }, + { + "$ref": "#/definitions/ReadableStream" + }, + { + "type": [ + "null", + "string" + ] + } + ] + }, + "cache": { + "enum": [ + "default", + "force-cache", + "no-cache", + "no-store", + "only-if-cached", + "reload" + ], + "type": "string" + }, + "credentials": { + "enum": [ + "include", + "omit", + "same-origin" + ], + "type": "string" + }, + "headers": { + "anyOf": [ + { + "$ref": "#/definitions/Headers" + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "$ref": "#/definitions/Record" + } + ] + }, + "integrity": { + "type": "string" + }, + "keepalive": { + "type": "boolean" + }, + "method": { + "type": "string" + }, + "mode": { + "enum": [ + "cors", + "navigate", + "no-cors", + "same-origin" + ], + "type": "string" + }, + "redirect": { + "enum": [ + "error", + "follow", + "manual" + ], + "type": "string" + }, + "referrer": { + "type": "string" + }, + "referrerPolicy": { + "enum": [ + "", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "same-origin", + "strict-origin", + "strict-origin-when-cross-origin", + "unsafe-url" + ], + "type": "string" + }, + "signal": { + "anyOf": [ + { + "$ref": "#/definitions/AbortSignal" + }, + { + "type": "null" + } + ] + }, + "window": {} + }, + "additionalProperties": false + }, + "ArrayBuffer": { + "type": "object", + "properties": { + "byteLength": { + "type": "number" + }, + "__@toStringTag": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "__@toStringTag", + "byteLength" + ] + }, + "ArrayBufferView": { + "type": "object", + "properties": { + "buffer": { + "anyOf": [ + { + "$ref": "#/definitions/ArrayBuffer" + }, + { + "$ref": "#/definitions/SharedArrayBuffer" + } + ] + }, + "byteLength": { + "type": "number" + }, + "byteOffset": { + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "buffer", + "byteLength", + "byteOffset" + ] + }, + "SharedArrayBuffer": { + "type": "object", + "properties": { + "byteLength": { + "type": "number" + }, + "__@species": { + "$ref": "#/definitions/SharedArrayBuffer" + }, + "__@toStringTag": { + "type": "string", + "enum": [ + "SharedArrayBuffer" + ] + } + }, + "additionalProperties": false, + "required": [ + "__@species", + "__@toStringTag", + "byteLength" + ] + }, + "Blob": { + "type": "object", + "properties": { + "size": { + "type": "number" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "size", + "type" + ] + }, + "FormData": { + "type": "object", + "additionalProperties": false + }, + "URLSearchParams": { + "type": "object", + "additionalProperties": false + }, + "ReadableStream": { + "type": "object", + "properties": { + "locked": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "locked" + ] + }, + "Headers": { + "type": "object", + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, + "AbortSignal": { + "type": "object", + "properties": { + "aborted": { + "type": "boolean" + }, + "onabort": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "aborted", + "onabort" + ] + }, + "RouteMatchCallback": {} + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} diff --git a/packages/workbox-build/src/schema/InjectManifestOptions.json b/packages/workbox-build/src/schema/InjectManifestOptions.json new file mode 100644 index 0000000..313a6e9 --- /dev/null +++ b/packages/workbox-build/src/schema/InjectManifestOptions.json @@ -0,0 +1,773 @@ +{ + "additionalProperties": false, + "type": "object", + "properties": { + "additionalManifestEntries": { + "description": "A list of entries to be precached, in addition to any entries that are\ngenerated as part of the build configuration.", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/ManifestEntry" + }, + { + "type": "string" + } + ] + } + }, + "dontCacheBustURLsMatching": { + "description": "Assets that match this will be assumed to be uniquely versioned via their\nURL, and exempted from the normal HTTP cache-busting that's done when\npopulating the precache. While not required, it's recommended that if your\nexisting build process already inserts a `[hash]` value into each filename,\nyou provide a RegExp that will detect that, as it will reduce the bandwidth\nconsumed when precaching.", + "$ref": "#/definitions/RegExp" + }, + "manifestTransforms": { + "description": "One or more functions which will be applied sequentially against the\ngenerated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are\nalso specified, their corresponding transformations will be applied first.", + "type": "array", + "items": {} + }, + "maximumFileSizeToCacheInBytes": { + "description": "This value can be used to determine the maximum size of files that will be\nprecached. This prevents you from inadvertently precaching very large files\nthat might have accidentally matched one of your patterns.", + "default": 2097152, + "type": "number" + }, + "modifyURLPrefix": { + "description": "An object mapping string prefixes to replacement string values. This can be\nused to, e.g., remove or add a path prefix from a manifest entry if your\nweb hosting setup doesn't match your local filesystem setup. As an\nalternative with more flexibility, you can use the `manifestTransforms`\noption and provide a function that modifies the entries in the manifest\nusing whatever logic you provide.\n\nExample usage:\n\n```\n// Replace a '/dist/' prefix with '/', and also prepend\n// '/static' to every URL.\nmodifyURLPrefix: {\n '/dist/': '/',\n '': '/static',\n}\n```", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "globFollow": { + "description": "Determines whether or not symlinks are followed when generating the\nprecache manifest. For more information, see the definition of `follow` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).", + "default": true, + "type": "boolean" + }, + "globIgnores": { + "description": "A set of patterns matching files to always exclude when generating the\nprecache manifest. For more information, see the definition of `ignore` in\nthe `glob` [documentation](https://github.com/isaacs/node-glob#options).", + "default": [ + "**/node_modules/**/*" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "globPatterns": { + "description": "Files matching any of these patterns will be included in the precache\nmanifest. For more information, see the\n[`glob` primer](https://github.com/isaacs/node-glob#glob-primer).", + "default": [ + "**/*.{js,css,html}" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "globStrict": { + "description": "If true, an error reading a directory when generating a precache manifest\nwill cause the build to fail. If false, the problematic directory will be\nskipped. For more information, see the definition of `strict` in the `glob`\n[documentation](https://github.com/isaacs/node-glob#options).", + "default": true, + "type": "boolean" + }, + "templatedURLs": { + "description": "If a URL is rendered based on some server-side logic, its contents may\ndepend on multiple files or on some other unique string value. The keys in\nthis object are server-rendered URLs. If the values are an array of\nstrings, they will be interpreted as `glob` patterns, and the contents of\nany files matching the patterns will be used to uniquely version the URL.\nIf used with a single string, it will be interpreted as unique versioning\ninformation that you've generated for a given URL.", + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + } + }, + "injectionPoint": { + "description": "The string to find inside of the `swSrc` file. Once found, it will be\nreplaced by the generated precache manifest.", + "default": "self.__WB_MANIFEST", + "type": "string" + }, + "swSrc": { + "description": "The path and filename of the service worker file that will be read during\nthe build process, relative to the current working directory.", + "type": "string" + }, + "swDest": { + "description": "The path and filename of the service worker file that will be created by\nthe build process, relative to the current working directory. It must end\nin '.js'.", + "type": "string" + }, + "globDirectory": { + "description": "The local directory you wish to match `globPatterns` against. The path is\nrelative to the current directory.", + "type": "string" + } + }, + "required": [ + "globDirectory", + "swDest", + "swSrc" + ], + "definitions": { + "ManifestEntry": { + "type": "object", + "properties": { + "integrity": { + "type": "string" + }, + "revision": { + "type": [ + "null", + "string" + ] + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "revision", + "url" + ] + }, + "RegExp": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "global": { + "type": "boolean" + }, + "ignoreCase": { + "type": "boolean" + }, + "multiline": { + "type": "boolean" + }, + "lastIndex": { + "type": "number" + }, + "flags": { + "type": "string" + }, + "sticky": { + "type": "boolean" + }, + "unicode": { + "type": "boolean" + }, + "dotAll": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "dotAll", + "flags", + "global", + "ignoreCase", + "lastIndex", + "multiline", + "source", + "sticky", + "unicode" + ] + }, + "GoogleAnalyticsInitializeOptions": { + "type": "object", + "properties": { + "cacheName": { + "type": "string" + }, + "parameterOverrides": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "hitFilter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "RuntimeCaching": { + "type": "object", + "properties": { + "handler": { + "description": "This determines how the runtime route will generate a response.\nTo use one of the built-in {@link workbox-strategies}, provide its name,\nlike `'NetworkFirst'`.\nAlternatively, this can be a {@link workbox-core.RouteHandler} callback\nfunction with custom response logic.", + "anyOf": [ + { + "$ref": "#/definitions/RouteHandlerCallback" + }, + { + "$ref": "#/definitions/RouteHandlerObject" + }, + { + "enum": [ + "CacheFirst", + "CacheOnly", + "NetworkFirst", + "NetworkOnly", + "StaleWhileRevalidate" + ], + "type": "string" + } + ] + }, + "method": { + "description": "The HTTP method to match against. The default value of `'GET'` is normally\nsufficient, unless you explicitly need to match `'POST'`, `'PUT'`, or\nanother type of request.", + "default": "GET", + "enum": [ + "DELETE", + "GET", + "HEAD", + "PATCH", + "POST", + "PUT" + ], + "type": "string" + }, + "options": { + "type": "object", + "properties": { + "backgroundSync": { + "description": "Configuring this will add a\n{@link workbox-background-sync.BackgroundSyncPlugin} instance to the\n{@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "options": { + "$ref": "#/definitions/QueueOptions" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + "broadcastUpdate": { + "description": "Configuring this will add a\n{@link workbox-broadcast-update.BroadcastUpdatePlugin} instance to the\n{@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "channelName": { + "type": "string" + }, + "options": { + "$ref": "#/definitions/BroadcastCacheUpdateOptions" + } + }, + "additionalProperties": false, + "required": [ + "options" + ] + }, + "cacheableResponse": { + "description": "Configuring this will add a\n{@link workbox-cacheable-response.CacheableResponsePlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/CacheableResponseOptions" + }, + "cacheName": { + "description": "If provided, this will set the `cacheName` property of the\n{@link workbox-strategies} configured in `handler`.", + "type": [ + "null", + "string" + ] + }, + "expiration": { + "description": "Configuring this will add a\n{@link workbox-expiration.ExpirationPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/ExpirationPluginOptions" + }, + "networkTimeoutSeconds": { + "description": "If provided, this will set the `networkTimeoutSeconds` property of the\n{@link workbox-strategies} configured in `handler`. Note that only\n`'NetworkFirst'` and `'NetworkOnly'` support `networkTimeoutSeconds`.", + "type": "number" + }, + "plugins": { + "description": "Configuring this allows the use of one or more Workbox plugins that\ndon't have \"shortcut\" options (like `expiration` for\n{@link workbox-expiration.ExpirationPlugin}). The plugins provided here\nwill be added to the {@link workbox-strategies} configured in `handler`.", + "type": "array", + "items": { + "$ref": "#/definitions/WorkboxPlugin" + } + }, + "precacheFallback": { + "description": "Configuring this will add a\n{@link workbox-precaching.PrecacheFallbackPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "type": "object", + "properties": { + "fallbackURL": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "fallbackURL" + ] + }, + "rangeRequests": { + "description": "Enabling this will add a\n{@link workbox-range-requests.RangeRequestsPlugin} instance to\nthe {@link workbox-strategies} configured in `handler`.", + "type": "boolean" + }, + "fetchOptions": { + "description": "Configuring this will pass along the `fetchOptions` value to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/RequestInit" + }, + "matchOptions": { + "description": "Configuring this will pass along the `matchOptions` value to\nthe {@link workbox-strategies} configured in `handler`.", + "$ref": "#/definitions/CacheQueryOptions" + } + }, + "additionalProperties": false + }, + "urlPattern": { + "description": "This match criteria determines whether the configured handler will\ngenerate a response for any requests that don't match one of the precached\nURLs. If multiple `RuntimeCaching` routes are defined, then the first one\nwhose `urlPattern` matches will be the one that responds.\n\nThis value directly maps to the first parameter passed to\n{@link workbox-routing.registerRoute}. It's recommended to use a\n{@link workbox-core.RouteMatchCallback} function for greatest flexibility.", + "anyOf": [ + { + "$ref": "#/definitions/RegExp" + }, + { + "$ref": "#/definitions/RouteMatchCallback" + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "handler", + "urlPattern" + ] + }, + "RouteHandlerCallback": {}, + "RouteHandlerObject": { + "description": "An object with a `handle` method of type `RouteHandlerCallback`.\n\nA `Route` object can be created with either an `RouteHandlerCallback`\nfunction or this `RouteHandler` object. The benefit of the `RouteHandler`\nis it can be extended (as is done by the `workbox-strategies` package).", + "type": "object", + "properties": { + "handle": { + "$ref": "#/definitions/RouteHandlerCallback" + } + }, + "additionalProperties": false, + "required": [ + "handle" + ] + }, + "QueueOptions": { + "type": "object", + "properties": { + "forceSyncFallback": { + "type": "boolean" + }, + "maxRetentionTime": { + "type": "number" + }, + "onSync": { + "$ref": "#/definitions/OnSyncCallback" + } + }, + "additionalProperties": false + }, + "OnSyncCallback": {}, + "BroadcastCacheUpdateOptions": { + "type": "object", + "properties": { + "headersToCheck": { + "type": "array", + "items": { + "type": "string" + } + }, + "generatePayload": { + "type": "object", + "additionalProperties": false + }, + "notifyAllClients": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CacheableResponseOptions": { + "type": "object", + "properties": { + "statuses": { + "type": "array", + "items": { + "type": "number" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "ExpirationPluginOptions": { + "type": "object", + "properties": { + "maxEntries": { + "type": "number" + }, + "maxAgeSeconds": { + "type": "number" + }, + "matchOptions": { + "$ref": "#/definitions/CacheQueryOptions" + }, + "purgeOnQuotaError": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CacheQueryOptions": { + "type": "object", + "properties": { + "ignoreMethod": { + "type": "boolean" + }, + "ignoreSearch": { + "type": "boolean" + }, + "ignoreVary": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "WorkboxPlugin": { + "description": "An object with optional lifecycle callback properties for the fetch and\ncache operations.", + "type": "object", + "properties": { + "cacheDidUpdate": {}, + "cachedResponseWillBeUsed": {}, + "cacheKeyWillBeUsed": {}, + "cacheWillUpdate": {}, + "fetchDidFail": {}, + "fetchDidSucceed": {}, + "handlerDidComplete": {}, + "handlerDidError": {}, + "handlerDidRespond": {}, + "handlerWillRespond": {}, + "handlerWillStart": {}, + "requestWillFetch": {} + }, + "additionalProperties": false + }, + "CacheDidUpdateCallback": { + "type": "object", + "additionalProperties": false + }, + "CachedResponseWillBeUsedCallback": { + "type": "object", + "additionalProperties": false + }, + "CacheKeyWillBeUsedCallback": { + "type": "object", + "additionalProperties": false + }, + "CacheWillUpdateCallback": { + "type": "object", + "additionalProperties": false + }, + "FetchDidFailCallback": { + "type": "object", + "additionalProperties": false + }, + "FetchDidSucceedCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidCompleteCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidErrorCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerDidRespondCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerWillRespondCallback": { + "type": "object", + "additionalProperties": false + }, + "HandlerWillStartCallback": { + "type": "object", + "additionalProperties": false + }, + "RequestWillFetchCallback": { + "type": "object", + "additionalProperties": false + }, + "RequestInit": { + "type": "object", + "properties": { + "body": { + "anyOf": [ + { + "$ref": "#/definitions/ArrayBuffer" + }, + { + "$ref": "#/definitions/ArrayBufferView" + }, + { + "$ref": "#/definitions/Blob" + }, + { + "$ref": "#/definitions/FormData" + }, + { + "$ref": "#/definitions/URLSearchParams" + }, + { + "$ref": "#/definitions/ReadableStream" + }, + { + "type": [ + "null", + "string" + ] + } + ] + }, + "cache": { + "enum": [ + "default", + "force-cache", + "no-cache", + "no-store", + "only-if-cached", + "reload" + ], + "type": "string" + }, + "credentials": { + "enum": [ + "include", + "omit", + "same-origin" + ], + "type": "string" + }, + "headers": { + "anyOf": [ + { + "$ref": "#/definitions/Headers" + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "$ref": "#/definitions/Record" + } + ] + }, + "integrity": { + "type": "string" + }, + "keepalive": { + "type": "boolean" + }, + "method": { + "type": "string" + }, + "mode": { + "enum": [ + "cors", + "navigate", + "no-cors", + "same-origin" + ], + "type": "string" + }, + "redirect": { + "enum": [ + "error", + "follow", + "manual" + ], + "type": "string" + }, + "referrer": { + "type": "string" + }, + "referrerPolicy": { + "enum": [ + "", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "same-origin", + "strict-origin", + "strict-origin-when-cross-origin", + "unsafe-url" + ], + "type": "string" + }, + "signal": { + "anyOf": [ + { + "$ref": "#/definitions/AbortSignal" + }, + { + "type": "null" + } + ] + }, + "window": {} + }, + "additionalProperties": false + }, + "ArrayBuffer": { + "type": "object", + "properties": { + "byteLength": { + "type": "number" + }, + "__@toStringTag": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "__@toStringTag", + "byteLength" + ] + }, + "ArrayBufferView": { + "type": "object", + "properties": { + "buffer": { + "anyOf": [ + { + "$ref": "#/definitions/ArrayBuffer" + }, + { + "$ref": "#/definitions/SharedArrayBuffer" + } + ] + }, + "byteLength": { + "type": "number" + }, + "byteOffset": { + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "buffer", + "byteLength", + "byteOffset" + ] + }, + "SharedArrayBuffer": { + "type": "object", + "properties": { + "byteLength": { + "type": "number" + }, + "__@species": { + "$ref": "#/definitions/SharedArrayBuffer" + }, + "__@toStringTag": { + "type": "string", + "enum": [ + "SharedArrayBuffer" + ] + } + }, + "additionalProperties": false, + "required": [ + "__@species", + "__@toStringTag", + "byteLength" + ] + }, + "Blob": { + "type": "object", + "properties": { + "size": { + "type": "number" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "size", + "type" + ] + }, + "FormData": { + "type": "object", + "additionalProperties": false + }, + "URLSearchParams": { + "type": "object", + "additionalProperties": false + }, + "ReadableStream": { + "type": "object", + "properties": { + "locked": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "locked" + ] + }, + "Headers": { + "type": "object", + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, + "AbortSignal": { + "type": "object", + "properties": { + "aborted": { + "type": "boolean" + }, + "onabort": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "aborted", + "onabort" + ] + }, + "RouteMatchCallback": {} + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts new file mode 100644 index 0000000..404d567 --- /dev/null +++ b/packages/workbox-build/src/types.ts @@ -0,0 +1,588 @@ +import type { QueueOptions } from '@vite-pwa/workbox-background-sync/types' +import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-broadcast-update/types' +import type { CacheableResponseOptions } from '@vite-pwa/workbox-cacheable-response/types' +import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-core/types' +import type { ExpirationPluginOptions } from '@vite-pwa/workbox-expiration/types' +// import type { GoogleAnalyticsInitializeOptions } from 'workbox-google-analytics/initialize' +import type { HTTPMethod } from '@vite-pwa/workbox-routing/types' +import type { PackageJson } from 'pkg-types' + +export interface ManifestEntry { + integrity?: string + revision: string | null + url: string +} + +export type StrategyName + = | 'CacheFirst' + | 'CacheOnly' + | 'NetworkFirst' + | 'NetworkOnly' + | 'StaleWhileRevalidate' + +export interface RuntimeCaching { + /** + * This determines how the runtime route will generate a response. + * To use one of the built-in {@link workbox-strategies}, provide its name, + * like `'NetworkFirst'`. + * Alternatively, this can be a {@link workbox-core.RouteHandler} callback + * function with custom response logic. + */ + handler: RouteHandler | StrategyName + /** + * The HTTP method to match against. The default value of `'GET'` is normally + * sufficient, unless you explicitly need to match `'POST'`, `'PUT'`, or + * another type of request. + * @default "GET" + */ + method?: HTTPMethod + options?: { + /** + * Configuring this will add a + * {@link workbox-background-sync.BackgroundSyncPlugin} instance to the + * {@link workbox-strategies} configured in `handler`. + */ + backgroundSync?: { + name: string + options?: QueueOptions + } + /** + * Configuring this will add a + * {@link workbox-broadcast-update.BroadcastUpdatePlugin} instance to the + * {@link workbox-strategies} configured in `handler`. + */ + broadcastUpdate?: { + // TODO: This option is ignored since we switched to using postMessage(). + // Remove it in the next major release. + channelName?: string + options: BroadcastCacheUpdateOptions + } + /** + * Configuring this will add a + * {@link workbox-cacheable-response.CacheableResponsePlugin} instance to + * the {@link workbox-strategies} configured in `handler`. + */ + cacheableResponse?: CacheableResponseOptions + /** + * If provided, this will set the `cacheName` property of the + * {@link workbox-strategies} configured in `handler`. + */ + cacheName?: string | null + /** + * Configuring this will add a + * {@link workbox-expiration.ExpirationPlugin} instance to + * the {@link workbox-strategies} configured in `handler`. + */ + expiration?: ExpirationPluginOptions + /** + * If provided, this will set the `networkTimeoutSeconds` property of the + * {@link workbox-strategies} configured in `handler`. Note that only + * `'NetworkFirst'` and `'NetworkOnly'` support `networkTimeoutSeconds`. + */ + networkTimeoutSeconds?: number + /** + * Configuring this allows the use of one or more Workbox plugins that + * don't have "shortcut" options (like `expiration` for + * {@link workbox-expiration.ExpirationPlugin}). The plugins provided here + * will be added to the {@link workbox-strategies} configured in `handler`. + */ + plugins?: Array + /** + * Configuring this will add a + * {@link workbox-precaching.PrecacheFallbackPlugin} instance to + * the {@link workbox-strategies} configured in `handler`. + */ + precacheFallback?: { + fallbackURL: string + } + /** + * Enabling this will add a + * {@link workbox-range-requests.RangeRequestsPlugin} instance to + * the {@link workbox-strategies} configured in `handler`. + */ + rangeRequests?: boolean + /** + * Configuring this will pass along the `fetchOptions` value to + * the {@link workbox-strategies} configured in `handler`. + */ + fetchOptions?: RequestInit + /** + * Configuring this will pass along the `matchOptions` value to + * the {@link workbox-strategies} configured in `handler`. + */ + matchOptions?: CacheQueryOptions + } + /** + * This match criteria determines whether the configured handler will + * generate a response for any requests that don't match one of the precached + * URLs. If multiple `RuntimeCaching` routes are defined, then the first one + * whose `urlPattern` matches will be the one that responds. + * + * This value directly maps to the first parameter passed to + * {@link workbox-routing.registerRoute}. It's recommended to use a + * {@link workbox-core.RouteMatchCallback} function for greatest flexibility. + */ + urlPattern: RegExp | string | RouteMatchCallback +} + +export interface ManifestTransformResult { + manifest: Array + warnings?: Array +} + +export type ManifestTransform = ( + manifestEntries: Array, + compilation?: unknown, +) => Promise | ManifestTransformResult + +export interface BasePartial { + /** + * A list of entries to be precached, in addition to any entries that are + * generated as part of the build configuration. + */ + additionalManifestEntries?: Array + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching?: RegExp + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms?: Array + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2097152 + */ + maximumFileSizeToCacheInBytes?: number + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + * + * Example usage: + * + * ``` + * // Replace a '/dist/' prefix with '/', and also prepend + * // '/static' to every URL. + * modifyURLPrefix: { + * '/dist/': '/', + * '': '/static', + * } + * ``` + */ + modifyURLPrefix?: { + [key: string]: string + } +} + +export interface GeneratePartial { + /** + * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass + * to `babel-preset-env` when transpiling the service worker bundle. + * @default ["chrome >= 56"] + */ + babelPresetEnvTargets?: Array + /** + * An optional ID to be prepended to cache names. This is primarily useful for + * local development where multiple sites may be served from the same + * `http://localhost:port` origin. + */ + cacheId?: string | null + /** + * Whether or not Workbox should attempt to identify and delete any precaches + * created by older, incompatible versions. + * @default false + */ + cleanupOutdatedCaches?: boolean + /** + * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) + * any existing clients as soon as it activates. + * @default false + */ + clientsClaim?: boolean + /** + * If a navigation request for a URL ending in `/` fails to match a precached + * URL, this value will be appended to the URL and that will be checked for a + * precache match. This should be set to what your web server is using for its + * directory index. + */ + directoryIndex?: string | null + /** + * @default false + */ + disableDevLogs?: boolean + // We can't use the @default annotation here to assign the value via AJV, as + // an Array can't be serialized into JSON. + /** + * Any search parameter names that match against one of the RegExp in this + * array will be removed before looking for a precache match. This is useful + * if your users might request URLs that contain, for example, URL parameters + * used to track the source of the traffic. If not provided, the default value + * is `[/^utm_/, /^fbclid$/]`. + * + */ + ignoreURLParametersMatching?: Array + /** + * A list of JavaScript files that should be passed to + * [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) + * inside the generated service worker file. This is useful when you want to + * let Workbox create your top-level service worker file, but want to include + * some additional code, such as a push event listener. + */ + importScripts?: Array + /** + * Whether the runtime code for the Workbox library should be included in the + * top-level service worker, or split into a separate file that needs to be + * deployed alongside the service worker. Keeping the runtime separate means + * that users will not have to re-download the Workbox code each time your + * top-level service worker changes. + * @default false + */ + inlineWorkboxRuntime?: boolean + /** + * If set to 'production', then an optimized service worker bundle that + * excludes debugging info will be produced. If not explicitly configured + * here, the `process.env.NODE_ENV` value will be used, and failing that, it + * will fall back to `'production'`. + * @default "production" + */ + mode?: string | null + /** + * If specified, all + * [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) + * for URLs that aren't precached will be fulfilled with the HTML at the URL + * provided. You must pass in the URL of an HTML document that is listed in + * your precache manifest. This is meant to be used in a Single Page App + * scenario, in which you want all navigations to use common + * [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). + * @default null + */ + navigateFallback?: string | null + /** + * An optional array of regular expressions that restricts which URLs the + * configured `navigateFallback` behavior applies to. This is useful if only a + * subset of your site's URLs should be treated as being part of a + * [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). + * If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are + * configured, the denylist takes precedent. + * + * Note*: These RegExps may be evaluated against every destination URL during + * a navigation. Avoid using + * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077), + * or else your users may see delays when navigating your site. + */ + navigateFallbackAllowlist?: Array + /** + * An optional array of regular expressions that restricts which URLs the + * configured `navigateFallback` behavior applies to. This is useful if only a + * subset of your site's URLs should be treated as being part of a + * [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). + * If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are + * configured, the denylist takes precedence. + * + * Note*: These RegExps may be evaluated against every destination URL during + * a navigation. Avoid using + * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077), + * or else your users may see delays when navigating your site. + */ + navigateFallbackDenylist?: Array + /** + * Whether or not to enable + * [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) + * in the generated service worker. When set to true, you must also use + * `runtimeCaching` to set up an appropriate response strategy that will match + * navigation requests, and make use of the preloaded response. + * @default false + */ + navigationPreload?: boolean + /** + * Controls whether or not to include support for + * [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics). + * When `true`, the call to `workbox-google-analytics`'s `initialize()` will + * be added to your generated service worker. When set to an `Object`, that + * object will be passed in to the `initialize()` call, allowing you to + * customize the behavior. + * @default false + */ + // offlineGoogleAnalytics?: boolean | GoogleAnalyticsInitializeOptions + /** + * When using Workbox's build tools to generate your service worker, you can + * specify one or more runtime caching configurations. These are then + * translated to {@link workbox-routing.registerRoute} calls using the match + * and handler configuration you define. + * + * For all of the options, see the {@link workbox-build.RuntimeCaching} + * documentation. The example below shows a typical configuration, with two + * runtime routes defined: + * + * @example + * runtimeCaching: [{ + * urlPattern: ({url}) => url.origin === 'https://api.example.com', + * handler: 'NetworkFirst', + * options: { + * cacheName: 'api-cache', + * }, + * }, { + * urlPattern: ({request}) => request.destination === 'image', + * handler: 'StaleWhileRevalidate', + * options: { + * cacheName: 'images-cache', + * expiration: { + * maxEntries: 10, + * }, + * }, + * }] + */ + runtimeCaching?: Array + /** + * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) + * to the generated service worker. If `false`, then a `message` listener will + * be added instead, allowing client pages to trigger `skipWaiting()` by + * calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. + * @default false + */ + skipWaiting?: boolean + /** + * Whether to create a sourcemap for the generated service worker files. + * @default true + */ + sourcemap?: boolean +} + +// This needs to be set when using GetManifest or InjectManifest, but is +// optional when using GenerateSW if runtimeCaching is also used. This is +// enforced via runtime validation, and needs to be documented. +export interface RequiredGlobDirectoryPartial { + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: string +} + +export interface OptionalGlobDirectoryPartial { + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory?: string +} + +export interface GlobPartial { + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + * @default true + */ + globFollow?: boolean + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + * @default ["**\/node_modules\/**\/*"] + */ + globIgnores?: Array + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + * @default ["**\/*.{js,css,html}"] + */ + globPatterns?: Array + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + * @default true + */ + globStrict?: boolean + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs?: { + [key: string]: string | Array + } +} + +export interface InjectPartial { + /** + * The string to find inside of the `swSrc` file. Once found, it will be + * replaced by the generated precache manifest. + * @default "self.__WB_MANIFEST" + */ + injectionPoint?: string + /** + * The path and filename of the service worker file that will be read during + * the build process, relative to the current working directory. + */ + swSrc: string +} + +export interface WebpackPartial { + /** + * One or more chunk names whose corresponding output files should be included + * in the precache manifest. + */ + chunks?: Array + // We can't use the @default annotation here to assign the value via AJV, as + // an Array can't be serialized into JSON. + // The default value of [/\.map$/, /^manifest.*\.js$/] will be assigned by + // the validation function, and we need to reflect that in the docs. + /** + * One or more specifiers used to exclude assets from the precache manifest. + * This is interpreted following + * [the same rules](https://webpack.js.org/configuration/module/#condition) + * as `webpack`'s standard `exclude` option. + * If not provided, the default value is `[/\.map$/, /^manifest.*\.js$]`. + */ + exclude?: Array boolean)> + /** + * One or more chunk names whose corresponding output files should be excluded + * from the precache manifest. + */ + excludeChunks?: Array + /** + * One or more specifiers used to include assets in the precache manifest. + * This is interpreted following + * [the same rules](https://webpack.js.org/configuration/module/#condition) + * as `webpack`'s standard `include` option. + */ + include?: Array boolean)> + /** + * If set to 'production', then an optimized service worker bundle that + * excludes debugging info will be produced. If not explicitly configured + * here, the `mode` value configured in the current `webpack` compilation + * will be used. + */ + mode?: string | null +} + +export interface RequiredSWDestPartial { + /** + * The path and filename of the service worker file that will be created by + * the build process, relative to the current working directory. It must end + * in '.js'. + */ + swDest: string +} + +export interface WebpackGenerateSWPartial { + /** + * One or more names of webpack chunks. The content of those chunks will be + * included in the generated service worker, via a call to `importScripts()`. + */ + importScriptsViaChunks?: Array + /** + * The asset name of the service worker file created by this plugin. + * @default "service-worker.js" + */ + swDest?: string +} + +export interface WebpackInjectManifestPartial { + /** + * When `true` (the default), the `swSrc` file will be compiled by webpack. + * When `false`, compilation will not occur (and `webpackCompilationPlugins` + * can't be used.) Set to `false` if you want to inject the manifest into, + * e.g., a JSON file. + * @default true + */ + compileSrc?: boolean + // This doesn't have a hardcoded default value; instead, the default will be + // set at runtime to the swSrc basename, with the hardcoded extension .js. + /** + * The asset name of the service worker file that will be created by this + * plugin. If omitted, the name will be based on the `swSrc` name. + */ + swDest?: string + // This can only be set if compileSrc is true, but that restriction can't be + // represented in TypeScript. It's enforced via custom runtime validation + // logic and needs to be documented. + /** + * Optional `webpack` plugins that will be used when compiling the `swSrc` + * input file. Only valid if `compileSrc` is `true`. + */ + webpackCompilationPlugins?: Array +} + +export type GenerateSWOptions = BasePartial + & GlobPartial + & GeneratePartial + & RequiredSWDestPartial + & OptionalGlobDirectoryPartial + +export type GetManifestOptions = BasePartial + & GlobPartial + & RequiredGlobDirectoryPartial + +export type InjectManifestOptions = BasePartial + & GlobPartial + & InjectPartial + & RequiredSWDestPartial + & RequiredGlobDirectoryPartial + +export type WebpackGenerateSWOptions = BasePartial + & WebpackPartial + & GeneratePartial + & WebpackGenerateSWPartial + +export type WebpackInjectManifestOptions = BasePartial + & WebpackPartial + & InjectPartial + & WebpackInjectManifestPartial + +export interface GetManifestResult { + count: number + manifestEntries: Array + size: number + warnings: Array +} + +export type BuildResult = Omit & { + filePaths: Array +} + +/** + * @private + */ +export interface FileDetails { + file: string + hash: string + size: number +} + +/** + * @private + */ +export type BuildType = 'dev' | 'prod' + +/** + * @private + */ +export interface WorkboxPackageJSON extends PackageJson { + workbox?: { + browserNamespace?: string + packageType?: string + prodOnly?: boolean + } +} diff --git a/packages/workbox-build/src/validation/generate-sw.ts b/packages/workbox-build/src/validation/generate-sw.ts new file mode 100644 index 0000000..9789c2f --- /dev/null +++ b/packages/workbox-build/src/validation/generate-sw.ts @@ -0,0 +1,266 @@ +import * as v from 'valibot' + +// Reused schema +const CacheQueryOptionsSchema = v.object({ + ignoreMethod: v.optional(v.boolean()), + ignoreSearch: v.optional(v.boolean()), + ignoreVary: v.optional(v.boolean()), +}) + +export type GenerateSWOptionsSchemaType = v.InferInput + +export const GenerateSWOptionsSchema = v.pipe( + v.strictObject({ + /** + * A list of entries to be precached, in addition to any entries that are generated as part of the build configuration. + */ + additionalManifestEntries: v.optional(v.array(v.union([ + v.object({ // Inlined ManifestEntrySchema + integrity: v.optional(v.string()), + revision: v.nullable(v.string()), + url: v.string(), + }), + v.string(), + ]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their URL, and exempted from the normal HTTP cache-busting that's done when populating the precache. While not required, it's recommended that if your existing build process already inserts a `[hash]` value into each filename, you provide a RegExp that will detect that, as it will reduce the bandwidth consumed when precaching. + */ + dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optional(v.array(v.function())), + /** + * This value can be used to determine the maximum size of files that will be precached. This prevents you from inadvertently precaching very large files that might have accidentally matched one of your patterns. + */ + maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be used to, e.g., remove or add a path prefix from a manifest entry if your web hosting setup doesn't match your local filesystem setup. As an alternative with more flexibility, you can use the `manifestTransforms` option and provide a function that modifies the entries in the manifest using whatever logic you provide. + */ + modifyURLPrefix: v.optional(v.record(v.string(), v.string())), + /** + * Determines whether or not symlinks are followed when generating the precache manifest. For more information, see the definition of `follow` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globFollow: v.optional(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the precache manifest. For more information, see the definition of `ignore` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest will cause the build to fail. If false, the problematic directory will be skipped. For more information, see the definition of `strict` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optional(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may depend on multiple files or on some other unique string value. The keys in this object are server-rendered URLs. If the values are an array of strings, they will be interpreted as `glob` patterns, and the contents of any files matching the patterns will be used to uniquely version the URL. If used with a single string, it will be interpreted as unique versioning information that you've generated for a given URL. + */ + templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), + /** + * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. + */ + babelPresetEnvTargets: v.optional(v.array(v.string()), ['chrome >= 56']), + /** + * An optional ID to be prepended to cache names. This is primarily useful for local development where multiple sites may be served from the same `http://localhost:port` origin. + */ + cacheId: v.optional(v.nullable(v.string())), + /** + * Whether or not Workbox should attempt to identify and delete any precaches created by older, incompatible versions. + */ + cleanupOutdatedCaches: v.optional(v.boolean(), false), + /** + * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) any existing clients as soon as it activates. + */ + clientsClaim: v.optional(v.boolean(), false), + /** + * If a navigation request for a URL ending in `/` fails to match a precached URL, this value will be appended to the URL and that will be checked for a precache match. This should be set to what your web server is using for its directory index. + */ + directoryIndex: v.optional(v.nullable(v.string())), + disableDevLogs: v.optional(v.boolean(), false), + /** + * Any search parameter names that match against one of the RegExp in this array will be removed before looking for a precache match. This is useful if your users might request URLs that contain, for example, URL parameters used to track the source of the traffic. If not provided, the default value is `[/^utm_/, /^fbclid$/]`. + */ + ignoreURLParametersMatching: v.optional(v.array(v.instance(RegExp))), + /** + * A list of JavaScript files that should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) inside the generated service worker file. This is useful when you want to let Workbox create your top-level service worker file, but want to include some additional code, such as a push event listener. + */ + importScripts: v.optional(v.array(v.string())), + /** + * Whether the runtime code for the Workbox library should be included in the top-level service worker, or split into a separate file that needs to be deployed alongside the service worker. Keeping the runtime separate means that users will not have to re-download the Workbox code each time your top-level service worker changes. + */ + inlineWorkboxRuntime: v.optional(v.boolean(), false), + /** + * If set to 'production', then an optimized service worker bundle that excludes debugging info will be produced. If not explicitly configured here, the `process.env.NODE_ENV` value will be used, and failing that, it will fall back to `'production'`. + */ + mode: v.optional(v.nullable(v.string()), 'production'), + /** + * If specified, all [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). + */ + navigateFallback: v.optional(v.nullable(v.string()), null), + /** + * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. + */ + navigateFallbackAllowlist: v.optional(v.array(v.instance(RegExp))), + /** + * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. + */ + navigateFallbackDenylist: v.optional(v.array(v.instance(RegExp))), + /** + * Whether or not to enable [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) in the generated service worker. When set to true, you must also use `runtimeCaching` to set up an appropriate response strategy that will match navigation requests, and make use of the preloaded response. + */ + navigationPreload: v.optional(v.boolean(), false), + /** + * Controls whether or not to include support for [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics). When `true`, the call to `workbox-google-analytics`'s `initialize()` will be added to your generated service worker. When set to an `Object`, that object will be passed in to the `initialize()` call, allowing you to customize the behavior. + */ + /* offlineGoogleAnalytics: v.optional(v.union([ + v.object({ // Inlined GoogleAnalyticsInitializeOptionsSchema + cacheName: v.optional(v.string()), + parameterOverrides: v.optional(v.record(v.string(), v.string())), + hitFilter: v.optional(v.function()), + }), + v.boolean(), + ]), false), */ + /** + * When using Workbox's build tools to generate your service worker, you can specify one or more runtime caching configurations. These are then translated to {@link workbox-routing.registerRoute} calls using the match and handler configuration you define. + */ + runtimeCaching: v.optional(v.array(v.object({ + /** + * This determines how the runtime route will generate a response. + * To use one of the built-in workbox-strategies, provide its name, like 'NetworkFirst'. + * Alternatively, this can be a workbox-core.RouteHandler callback function with custom response logic. + */ + handler: v.union([ + v.function(), // RouteHandlerCallback + v.object({ handle: v.function() }), // Inlined RouteHandlerObjectSchema + v.picklist([ + 'CacheFirst', + 'CacheOnly', + 'NetworkFirst', + 'NetworkOnly', + 'StaleWhileRevalidate', + ]), + ]), + /** + * The HTTP method to match against. The default value of 'GET' is normally sufficient, unless you explicitly need to match 'POST', 'PUT', or another type of request. + */ + method: v.optional(v.picklist([ + 'DELETE', + 'GET', + 'HEAD', + 'PATCH', + 'POST', + 'PUT', + ]), 'GET'), + options: v.optional(v.object({ + /** + * Configuring this will add a workbox-background-sync.BackgroundSyncPlugin instance to the workbox-strategies configured in `handler`. + */ + backgroundSync: v.optional(v.object({ + name: v.string(), + options: v.optional(v.object({ // Inlined QueueOptionsSchema + forceSyncFallback: v.optional(v.boolean()), + maxRetentionTime: v.optional(v.number()), + onSync: v.optional(v.function()), + })), + })), + /** + * Configuring this will add a workbox-broadcast-update.BroadcastUpdatePlugin instance to the workbox-strategies configured in `handler`. + */ + broadcastUpdate: v.optional(v.object({ + channelName: v.string(), + options: v.object({ // Inlined BroadcastCacheUpdateOptionsSchema + headersToCheck: v.optional(v.array(v.string())), + generatePayload: v.optional(v.function()), + notifyAllClients: v.optional(v.boolean()), + }), + })), + /** + * Configuring this will add a workbox-cacheable-response.CacheableResponsePlugin instance to the workbox-strategies configured in `handler`. + */ + cacheableResponse: v.optional(v.object({ // Inlined CacheableResponseOptionsSchema + statuses: v.optional(v.array(v.number())), + headers: v.optional(v.record(v.string(), v.string())), + })), + /** + * If provided, this will set the `cacheName` property of the workbox-strategies configured in `handler`. + */ + cacheName: v.optional(v.nullable(v.string())), + /** + * Configuring this will add a workbox-expiration.ExpirationPlugin instance to the workbox-strategies configured in `handler`. + */ + expiration: v.optional(v.object({ // Inlined ExpirationPluginOptionsSchema + maxEntries: v.optional(v.number()), + maxAgeSeconds: v.optional(v.number()), + matchOptions: v.optional(CacheQueryOptionsSchema), // Reused + purgeOnQuotaError: v.optional(v.boolean()), + })), + /** + * If provided, this will set the `networkTimeoutSeconds` property of the workbox-strategies configured in `handler`. Note that only 'NetworkFirst' and 'NetworkOnly' support `networkTimeoutSeconds`. + */ + networkTimeoutSeconds: v.optional(v.number()), + /** + * Configuring this allows the use of one or more Workbox plugins that don't have "shortcut" options (like `expiration` for workbox-expiration.ExpirationPlugin). The plugins provided here will be added to the workbox-strategies configured in `handler`. + */ + plugins: v.optional(v.array(v.object({ // Inlined WorkboxPluginSchema + cacheDidUpdate: v.optional(v.function()), + cachedResponseWillBeUsed: v.optional(v.function()), + cacheKeyWillBeUsed: v.optional(v.function()), + cacheWillUpdate: v.optional(v.function()), + fetchDidFail: v.optional(v.function()), + fetchDidSucceed: v.optional(v.function()), + handlerDidComplete: v.optional(v.function()), + handlerDidError: v.optional(v.function()), + handlerDidRespond: v.optional(v.function()), + handlerWillRespond: v.optional(v.function()), + handlerWillStart: v.optional(v.function()), + requestWillFetch: v.optional(v.function()), + }))), + /** + * Configuring this will add a workbox-precaching.PrecacheFallbackPlugin instance to the workbox-strategies configured in `handler`. + */ + precacheFallback: v.optional(v.object({ fallbackURL: v.string() })), + /** + * Enabling this will add a workbox-range-requests.RangeRequestsPlugin instance to the workbox-strategies configured in `handler`. + */ + rangeRequests: v.optional(v.boolean()), + /** + * Configuring this will pass along the `fetchOptions` value to the workbox-strategies configured in `handler`. + */ + fetchOptions: v.optional(v.any()), // RequestInit is too complex to define for now + /** + * Configuring this will pass along the `matchOptions` value to the workbox-strategies configured in `handler`. + */ + matchOptions: v.optional(CacheQueryOptionsSchema), // Reused + })), + /** + * This match criteria determines whether the configured handler will generate a response for any requests that don't match one of the precached URLs. If multiple `RuntimeCaching` routes are defined, then the first one whose `urlPattern` matches will be the one that responds. + * This value directly maps to the first parameter passed to workbox-routing.registerRoute. It's recommended to use a workbox-core.RouteMatchCallback function for greatest flexibility. + */ + urlPattern: v.union([v.instance(RegExp), v.function(), v.string()]), + }))), + /** + * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) to the generated service worker. If `false`, then a `message` listener will be added instead, allowing client pages to trigger `skipWaiting()` by calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. + */ + skipWaiting: v.optional(v.boolean(), false), + /** + * Whether to create a sourcemap for the generated service worker files. + */ + sourcemap: v.optional(v.boolean(), true), + /** + * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. + */ + swDest: v.pipe(v.string(), v.endsWith('.js', 'swDest must end with .js')), + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.optional(v.string()), + }), + v.check( + input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), + 'globDirectory is required when runtimeCaching is not provided', + ), +) diff --git a/packages/workbox-build/src/validation/get-manifest.ts b/packages/workbox-build/src/validation/get-manifest.ts new file mode 100644 index 0000000..edade23 --- /dev/null +++ b/packages/workbox-build/src/validation/get-manifest.ts @@ -0,0 +1,87 @@ +import * as v from 'valibot' + +// ManifestEntry is used in additionalManifestEntries +const ManifestEntrySchema = v.strictObject({ + integrity: v.optional(v.string()), + revision: v.nullable(v.string()), + url: v.string(), +}) + +export const GetManifestOptionsSchema = v.strictObject({ + /** + * A list of entries to be precached, in addition to any entries that are + * generated as part of the build configuration. + */ + additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optional(v.array(v.function())), + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2097152 (2MiB) + */ + maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optional(v.record(v.string(), v.string())), + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globFollow: v.optional(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optional(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.string(), +}) diff --git a/packages/workbox-build/src/validation/inject-manifest.ts b/packages/workbox-build/src/validation/inject-manifest.ts new file mode 100644 index 0000000..a9190aa --- /dev/null +++ b/packages/workbox-build/src/validation/inject-manifest.ts @@ -0,0 +1,109 @@ +import * as v from 'valibot' + +// ManifestEntry is used in additionalManifestEntries +const ManifestEntrySchema = v.strictObject({ + integrity: v.optional(v.string()), + revision: v.nullable(v.string()), + url: v.string(), +}) + +export const InjectManifestOptionsSchema = v.strictObject({ + /** + * A list of entries to be precached, in addition to any entries that are + * generated as part of the build configuration. + */ + additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optional(v.array(v.function())), + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2097152 + */ + maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optional(v.record(v.string(), v.string())), + + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + * @default true + */ + globFollow: v.optional(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optional(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), + + /** + * The string to find inside of the `swSrc` file. Once found, it will be + * replaced by the generated precache manifest. + * @default "self.__WB_MANIFEST" + */ + injectionPoint: v.optional(v.string(), 'self.__WB_MANIFEST'), + /** + * The path and filename of the service worker file that will be read during + * the build process, relative to the current working directory. + */ + swSrc: v.string(), + + /** + * The path and filename of the service worker file that will be created by + * the build process, relative to the current working directory. It must end + * in '.js'. + */ + swDest: v.pipe(v.string(), v.endsWith('.js', 'swDest must end with .js')), + + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.string(), +}) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts new file mode 100644 index 0000000..47d21c0 --- /dev/null +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -0,0 +1,48 @@ +import type { BaseIssue, BaseSchema } from 'valibot' +import { safeParse, setGlobalMessage } from 'valibot' + +// Set a global configuration for Valibot's error messages to make them +// more user-friendly for Workbox consumers. +setGlobalMessage((issue: BaseIssue) => { + // The path provides context to which field has the error. + // We get the last key in the path for the most specific field name. + const key = issue.path?.[issue.path.length - 1]?.key + + // When a required key is missing, Valibot's `issue.received` is 'undefined'. + // This is a robust way to create a user-friendly "missing option" message. + if (issue.received === 'undefined') { + return `The required option "${key}" is missing.` + } + + // For unknown keys in strict objects, `issue.expected` is 'never'. + // This allows us to create a clear "unknown option" message. + if (issue.expected === 'never') { + return `The option "${key}" is unknown or has been deprecated.` + } + + // For all other errors, including our custom pipes (e.g., `endsWith`), + // the message provided directly to the validation function is used. + // We fall back to this to ensure custom messages are always respected. + return issue.message +}) + +/** + * A wrapper around Valibot's `safeParse` that throws a user-friendly error + * if validation fails. + * @param schema The Valibot schema to use. + * @param options The options object to validate. + * @param methodName The name of the Workbox method being validated, for context. + */ +export function validate>( + schema: TSchema, + options: unknown, + methodName: string, +): void { + const result = safeParse(schema, options) + if (!result.success) { + const errorMessages = result.issues.map(issue => issue.message) + throw new Error( + `${methodName}() options validation failed: \n- ${errorMessages.join('\n- ')}`, + ) + } +} diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts new file mode 100644 index 0000000..27cfab4 --- /dev/null +++ b/packages/workbox-build/test/validation.test.ts @@ -0,0 +1,180 @@ +import type * as v from 'valibot' +import type { GenerateSWOptions, GetManifestOptions, InjectManifestOptions } from '../src/types' +import { describe, expect, expectTypeOf, it } from 'vitest' +import { GenerateSWOptionsSchema } from '../src/validation/generate-sw' +import { GetManifestOptionsSchema } from '../src/validation/get-manifest' +import { InjectManifestOptionsSchema } from '../src/validation/inject-manifest' +import { validate } from '../src/validation/validation-helper' + +type GenerateSWOptionsSchemaType = v.InferInput +type GetManifestOptionsSchemaType = v.InferInput +type InjectManifestOptionsSchemaType = v.InferInput + +describe('schema Type Inference Validation', () => { + it('should correctly infer types that are compatible with the original Workbox types', () => { + // @ts-expect-error - This is a type-level test. The IDE may complain because the nominal + // types are different, but `expectTypeOf` checks for structural compatibility, which is what we care about. + // This test will fail at COMPILE time if the schema's inferred type is not assignable to the original Workbox type. + expectTypeOf().toEqualTypeOf() + // @ts-expect-error - See comment above. + expectTypeOf().toEqualTypeOf() + // @ts-expect-error - See comment above. + expectTypeOf().toEqualTypeOf() + }) +}) + +describe('generateSWOptions Schema Validation', () => { + it('should pass with valid minimal options', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + } satisfies GenerateSWOptions + // @ts-expect-error - See comment at schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + }) + + it('should fail if swDest is missing', () => { + const options = { + globDirectory: './', + } satisfies Partial + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- The required option "swDest" is missing.', + ) + }) + + it('should pass with a function for manifestTransforms', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + manifestTransforms: [ + (manifest) => { + // dummy transform + return { manifest, warnings: [] } + }, + ], + } satisfies GenerateSWOptions + // @ts-expect-error - See comment at schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + }) + + it('should pass if globDirectory is missing but runtimeCaching is present', () => { + const options = { + swDest: 'sw.js', + runtimeCaching: [ + { + urlPattern: /.*/, + handler: 'NetworkFirst' as const, + }, + ], + } satisfies GenerateSWOptions + // @ts-expect-error - See comment at schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + }) + + it('should fail if an unknown property is present', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + importWorkboxFrom: 'cdn', + } satisfies GenerateSWOptions & { importWorkboxFrom?: string } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- The option "importWorkboxFrom" is unknown or has been deprecated.', + ) + }) + + it('should fail if swDest does not end with .js', () => { + const options = { + swDest: 'sw.txt', + globDirectory: './', + } satisfies GenerateSWOptions + // @ts-expect-error - See comment at schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- swDest must end with .js', + ) + }) +}) + +describe('getManifestOptions Schema Validation', () => { + it('should pass with valid minimal options', () => { + const options = { + globDirectory: './', + } satisfies GetManifestOptions + // @ts-expect-error - See comment at schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(GetManifestOptionsSchema, options, 'getManifest')).not.toThrow() + }) + + it('should fail if globDirectory is missing', () => { + const options = {} satisfies Partial + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + 'getManifest() options validation failed: \n- The required option "globDirectory" is missing.', + ) + }) + + it('should fail if an unknown property is present', () => { + const options = { + globDirectory: './', + anotherUnknown: 'noop', + } satisfies GetManifestOptions & { anotherUnknown?: string } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + 'getManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', + ) + }) +}) + +describe('injectManifestOptions Schema Validation', () => { + it('should pass with valid minimal options', () => { + const options = { + swSrc: 'sw.js', + swDest: 'sw-injected.js', + globDirectory: './', + } satisfies InjectManifestOptions + // @ts-expect-error - See comment at schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).not.toThrow() + }) + + it('should fail if swSrc is missing', () => { + const options = { + swDest: 'sw-injected.js', + globDirectory: './', + } satisfies Partial + expectTypeOf().not.toMatchObjectType() + expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + 'injectManifest() options validation failed: \n- The required option "swSrc" is missing.', + ) + }) + + it('should fail if swDest does not end with .js', () => { + const options = { + swSrc: 'sw.js', + swDest: 'sw-injected.txt', + globDirectory: './', + } satisfies InjectManifestOptions + expectTypeOf().not.toMatchObjectType() + expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).toThrow( + 'injectManifest() options validation failed: \n- swDest must end with .js', + ) + }) + + it('should fail if an unknown property is present', () => { + const options = { + swSrc: 'sw.js', + swDest: 'sw-injected.js', + globDirectory: './', + anotherUnknown: 'noop', + } satisfies InjectManifestOptions & { anotherUnknown?: string } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).toThrow( + 'injectManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', + ) + }) +}) diff --git a/packages/workbox-build/tsdown.config.ts b/packages/workbox-build/tsdown.config.ts new file mode 100644 index 0000000..403a67e --- /dev/null +++ b/packages/workbox-build/tsdown.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: './src/{index,types,generate-sw,inject-manifest}.ts', + platform: 'node', + banner: `/* + Copyright 2019 Google LLC, Vite PWA's Team + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/`, +}) diff --git a/packages/workbox-cacheable-response/package.json b/packages/workbox-cacheable-response/package.json index 8eb9cef..084c230 100644 --- a/packages/workbox-cacheable-response/package.json +++ b/packages/workbox-cacheable-response/package.json @@ -25,11 +25,19 @@ ], "sideEffects": false, "exports": { - ".": "./dist/index.js" + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + } }, "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"] + } + }, "files": [ "dist" ], diff --git a/packages/workbox-cacheable-response/src/index.ts b/packages/workbox-cacheable-response/src/index.ts index 7ce9202..cdbd6b7 100644 --- a/packages/workbox-cacheable-response/src/index.ts +++ b/packages/workbox-cacheable-response/src/index.ts @@ -6,8 +6,6 @@ https://opensource.org/licenses/MIT. */ -import { CacheableResponse } from './CacheableResponse' -import { CacheableResponsePlugin } from './CacheableResponsePlugin' - +export { CacheableResponse } from './CacheableResponse' export type { CacheableResponseOptions } from './CacheableResponse' -export { CacheableResponse, CacheableResponsePlugin } +export { CacheableResponsePlugin } from './CacheableResponsePlugin' diff --git a/packages/workbox-cacheable-response/src/types.ts b/packages/workbox-cacheable-response/src/types.ts new file mode 100644 index 0000000..6a77371 --- /dev/null +++ b/packages/workbox-cacheable-response/src/types.ts @@ -0,0 +1 @@ +export type { CacheableResponseOptions } from './CacheableResponse' diff --git a/packages/workbox-cacheable-response/tsdown.config.ts b/packages/workbox-cacheable-response/tsdown.config.ts index 1ba9d65..cfa94bb 100644 --- a/packages/workbox-cacheable-response/tsdown.config.ts +++ b/packages/workbox-cacheable-response/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/index.ts', + entry: './src/{index,types}.ts', platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team diff --git a/packages/workbox-core/package.json b/packages/workbox-core/package.json index 8e20a85..2ee9d8d 100644 --- a/packages/workbox-core/package.json +++ b/packages/workbox-core/package.json @@ -25,6 +25,9 @@ "sideEffects": false, "exports": { ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + }, "./internals": "./dist/internals.js" }, "main": "dist/index.js", @@ -32,6 +35,7 @@ "types": "dist/index.d.ts", "typesVersions": { "*": { + "types": ["dist/types.d.ts"], "internals": ["dist/internals.d.ts"] } }, diff --git a/packages/workbox-core/src/types.ts b/packages/workbox-core/src/types.ts index 4fedb71..c616c9a 100644 --- a/packages/workbox-core/src/types.ts +++ b/packages/workbox-core/src/types.ts @@ -44,7 +44,7 @@ export interface RouteMatchCallback { /** * Options passed to a `RouteHandlerCallback` function. */ -export declare interface RouteHandlerCallbackOptions { +export interface RouteHandlerCallbackOptions { event: ExtendableEvent request: Request url: URL @@ -249,7 +249,7 @@ export interface HandlerDidCompleteCallback { * An object with optional lifecycle callback properties for the fetch and * cache operations. */ -export declare interface WorkboxPlugin { +export interface WorkboxPlugin { cacheDidUpdate?: CacheDidUpdateCallback cachedResponseWillBeUsed?: CachedResponseWillBeUsedCallback cacheKeyWillBeUsed?: CacheKeyWillBeUsedCallback diff --git a/packages/workbox-core/tsdown.config.ts b/packages/workbox-core/tsdown.config.ts index dc388bf..0f71965 100644 --- a/packages/workbox-core/tsdown.config.ts +++ b/packages/workbox-core/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/{index,internals}.ts', + entry: './src/{index,types,internals}.ts', platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team diff --git a/packages/workbox-expiration/package.json b/packages/workbox-expiration/package.json index b53566d..dc3776f 100644 --- a/packages/workbox-expiration/package.json +++ b/packages/workbox-expiration/package.json @@ -25,11 +25,20 @@ ], "sideEffects": false, "exports": { - ".": "./dist/index.js" + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + } }, "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"], + "internals": ["dist/internals.d.ts"] + } + }, "files": [ "dist" ], diff --git a/packages/workbox-expiration/src/index.ts b/packages/workbox-expiration/src/index.ts index 4d372be..b08158c 100644 --- a/packages/workbox-expiration/src/index.ts +++ b/packages/workbox-expiration/src/index.ts @@ -6,12 +6,6 @@ https://opensource.org/licenses/MIT. */ -import type { ExpirationPluginOptions } from './ExpirationPlugin' -import { CacheExpiration } from './CacheExpiration' -import { ExpirationPlugin } from './ExpirationPlugin' - -/** - * @module workbox-expiration - */ - -export { CacheExpiration, ExpirationPlugin, ExpirationPluginOptions } +export { CacheExpiration } from './CacheExpiration' +export type { ExpirationPluginOptions } from './ExpirationPlugin' +export { ExpirationPlugin } from './ExpirationPlugin' diff --git a/packages/workbox-expiration/src/types.ts b/packages/workbox-expiration/src/types.ts new file mode 100644 index 0000000..159c1a8 --- /dev/null +++ b/packages/workbox-expiration/src/types.ts @@ -0,0 +1 @@ +export type { ExpirationPluginOptions } from './ExpirationPlugin' diff --git a/packages/workbox-expiration/tsdown.config.ts b/packages/workbox-expiration/tsdown.config.ts index 1ba9d65..cfa94bb 100644 --- a/packages/workbox-expiration/tsdown.config.ts +++ b/packages/workbox-expiration/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/index.ts', + entry: './src/{index,types}.ts', platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team diff --git a/packages/workbox-precaching/src/_types.ts b/packages/workbox-precaching/src/_types.ts index 0029344..7f84651 100644 --- a/packages/workbox-precaching/src/_types.ts +++ b/packages/workbox-precaching/src/_types.ts @@ -15,7 +15,7 @@ export interface CleanupResult { deletedCacheRequests: string[] } -export declare interface PrecacheEntry { +export interface PrecacheEntry { integrity?: string url: string revision?: string | null diff --git a/packages/workbox-routing/package.json b/packages/workbox-routing/package.json index 19f3d07..367beeb 100644 --- a/packages/workbox-routing/package.json +++ b/packages/workbox-routing/package.json @@ -26,11 +26,20 @@ ], "sideEffects": false, "exports": { - ".": "./dist/index.js" + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + } }, "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"], + "internals": ["dist/internals.d.ts"] + } + }, "files": [ "dist" ], diff --git a/packages/workbox-routing/src/index.ts b/packages/workbox-routing/src/index.ts index c83954c..ec6a29b 100644 --- a/packages/workbox-routing/src/index.ts +++ b/packages/workbox-routing/src/index.ts @@ -6,26 +6,11 @@ https://opensource.org/licenses/MIT. */ -import type { NavigationRouteMatchOptions } from './NavigationRoute' -import { NavigationRoute } from './NavigationRoute' -import { RegExpRoute } from './RegExpRoute' -import { registerRoute } from './registerRoute' -import { Route } from './Route' -import { Router } from './Router' -import { setCatchHandler } from './setCatchHandler' -import { setDefaultHandler } from './setDefaultHandler' - -/** - * @module workbox-routing - */ - -export { - NavigationRoute, - NavigationRouteMatchOptions, - RegExpRoute, - registerRoute, - Route, - Router, - setCatchHandler, - setDefaultHandler, -} +export type { NavigationRouteMatchOptions } from './NavigationRoute' +export { NavigationRoute } from './NavigationRoute' +export { RegExpRoute } from './RegExpRoute' +export { registerRoute } from './registerRoute' +export { Route } from './Route' +export { Router } from './Router' +export { setCatchHandler } from './setCatchHandler' +export { setDefaultHandler } from './setDefaultHandler' diff --git a/packages/workbox-routing/src/types.ts b/packages/workbox-routing/src/types.ts new file mode 100644 index 0000000..f765a40 --- /dev/null +++ b/packages/workbox-routing/src/types.ts @@ -0,0 +1,2 @@ +export type { NavigationRouteMatchOptions } from './NavigationRoute' +export type { HTTPMethod } from './utils/constants' diff --git a/packages/workbox-routing/tsdown.config.ts b/packages/workbox-routing/tsdown.config.ts index 1ba9d65..cfa94bb 100644 --- a/packages/workbox-routing/tsdown.config.ts +++ b/packages/workbox-routing/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/index.ts', + entry: './src/{index,types}.ts', platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9890c4a..ebe9221 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,13 @@ catalogs: eslint-plugin-antfu: specifier: ^3.1.1 version: 3.1.1 + testing: + '@vitest/ui': + specifier: ^3.2.4 + version: 3.2.4 + vitest: + specifier: ^3.2.4 + version: 3.2.4 types: '@types/node': specifier: ^22.9.3 @@ -43,9 +50,21 @@ catalogs: idb: specifier: ^8.0.3 version: 8.0.3 + magicast: + specifier: ^0.3.5 + version: 0.3.5 + pathe: + specifier: ^2.0.3 + version: 2.0.3 + pkg-types: + specifier: ^2.3.0 + version: 2.3.0 taze: specifier: ^19.7.0 version: 19.7.0 + valibot: + specifier: ^1.1.0 + version: 1.1.0 overrides: '@typescript-eslint/utils': ^8.45.0 @@ -56,7 +75,7 @@ importers: devDependencies: '@antfu/eslint-config': specifier: catalog:eslint - version: 6.0.0(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + version: 6.0.0(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4) '@antfu/ni': specifier: catalog:utils version: 25.0.0 @@ -72,9 +91,12 @@ importers: '@typescript-eslint/utils': specifier: ^8.45.0 version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@vitest/ui': + specifier: catalog:testing + version: 3.2.4(vitest@3.2.4) bumpp: specifier: catalog:build - version: 10.3.1 + version: 10.3.1(magicast@0.3.5) eslint: specifier: catalog:eslint version: 9.37.0(jiti@2.6.1) @@ -90,6 +112,9 @@ importers: typescript: specifier: catalog:typescript version: 5.9.3 + vitest: + specifier: catalog:testing + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1) packages/workbox-background-sync: dependencies: @@ -106,6 +131,39 @@ importers: specifier: workspace:* version: link:../workbox-core + packages/workbox-build: + dependencies: + '@vite-pwa/workbox-background-sync': + specifier: workspace:* + version: link:../workbox-background-sync + '@vite-pwa/workbox-broadcast-update': + specifier: workspace:* + version: link:../workbox-broadcast-update + '@vite-pwa/workbox-cacheable-response': + specifier: workspace:* + version: link:../workbox-cacheable-response + '@vite-pwa/workbox-core': + specifier: workspace:* + version: link:../workbox-core + '@vite-pwa/workbox-expiration': + specifier: workspace:* + version: link:../workbox-expiration + '@vite-pwa/workbox-routing': + specifier: workspace:* + version: link:../workbox-routing + magicast: + specifier: catalog:utils + version: 0.3.5 + pathe: + specifier: catalog:utils + version: 2.0.3 + pkg-types: + specifier: catalog:utils + version: 2.3.0 + valibot: + specifier: catalog:utils + version: 1.1.0(typescript@5.9.3) + packages/workbox-cacheable-response: dependencies: '@vite-pwa/workbox-core': @@ -329,6 +387,162 @@ packages: resolution: {integrity: sha512-smMc5pDht/UVsCD3hhw/a/e/p8m0RdRYiluXToVfd+d4yaQQh7nn9bACjkk6nXJvat7EWPAxuFkMEFfrxeGa3Q==} engines: {node: '>=20.11.0'} + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-plugin-eslint-comments@4.5.0': resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -448,6 +662,9 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} @@ -537,6 +754,116 @@ packages: '@rolldown/pluginutils@1.0.0-beta.43': resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==} + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + cpu: [x64] + os: [win32] + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -550,9 +877,15 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -645,6 +978,40 @@ packages: vitest: optional: true + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/ui@3.2.4': + resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} + peerDependencies: + vitest: 3.2.4 + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vue/compiler-core@3.5.22': resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==} @@ -706,6 +1073,10 @@ packages: args-tokenizer@0.3.0: resolution: {integrity: sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-kit@2.1.3: resolution: {integrity: sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g==} engines: {node: '>=20.19.0'} @@ -769,6 +1140,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -787,6 +1162,10 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -869,6 +1248,10 @@ packages: decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -931,6 +1314,14 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1140,10 +1531,17 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} @@ -1205,6 +1603,11 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fzf@0.5.2: resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==} @@ -1311,6 +1714,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1377,12 +1783,18 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -1542,6 +1954,10 @@ packages: mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1644,6 +2060,10 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + perfect-debounce@2.0.0: resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} @@ -1759,6 +2179,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -1779,10 +2204,17 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -1803,6 +2235,12 @@ packages: spdx-license-ids@3.0.22: resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1819,6 +2257,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1846,6 +2287,12 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} @@ -1853,6 +2300,18 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1861,6 +2320,10 @@ packages: resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -1956,10 +2419,91 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + valibot@1.1.0: + resolution: {integrity: sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + validate-npm-package-name@5.0.1: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@7.1.10: + resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vue-eslint-parser@10.2.0: resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1971,6 +2515,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2015,7 +2564,7 @@ snapshots: '@andrewbranch/untar.js@1.0.3': {} - '@antfu/eslint-config@6.0.0(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + '@antfu/eslint-config@6.0.0(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4)': dependencies: '@antfu/install-pkg': 1.1.0 '@clack/prompts': 0.11.0 @@ -2024,7 +2573,7 @@ snapshots: '@stylistic/eslint-plugin': 5.4.0(eslint@9.37.0(jiti@2.6.1)) '@typescript-eslint/eslint-plugin': 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@vitest/eslint-plugin': 1.3.20(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@vitest/eslint-plugin': 1.3.20(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4) ansis: 4.2.0 cac: 6.7.14 eslint: 9.37.0(jiti@2.6.1) @@ -2171,6 +2720,84 @@ snapshots: esquery: 1.6.0 jsdoc-type-pratt-parser: 5.4.0 + '@esbuild/aix-ppc64@0.25.11': + optional: true + + '@esbuild/android-arm64@0.25.11': + optional: true + + '@esbuild/android-arm@0.25.11': + optional: true + + '@esbuild/android-x64@0.25.11': + optional: true + + '@esbuild/darwin-arm64@0.25.11': + optional: true + + '@esbuild/darwin-x64@0.25.11': + optional: true + + '@esbuild/freebsd-arm64@0.25.11': + optional: true + + '@esbuild/freebsd-x64@0.25.11': + optional: true + + '@esbuild/linux-arm64@0.25.11': + optional: true + + '@esbuild/linux-arm@0.25.11': + optional: true + + '@esbuild/linux-ia32@0.25.11': + optional: true + + '@esbuild/linux-loong64@0.25.11': + optional: true + + '@esbuild/linux-mips64el@0.25.11': + optional: true + + '@esbuild/linux-ppc64@0.25.11': + optional: true + + '@esbuild/linux-riscv64@0.25.11': + optional: true + + '@esbuild/linux-s390x@0.25.11': + optional: true + + '@esbuild/linux-x64@0.25.11': + optional: true + + '@esbuild/netbsd-arm64@0.25.11': + optional: true + + '@esbuild/netbsd-x64@0.25.11': + optional: true + + '@esbuild/openbsd-arm64@0.25.11': + optional: true + + '@esbuild/openbsd-x64@0.25.11': + optional: true + + '@esbuild/openharmony-arm64@0.25.11': + optional: true + + '@esbuild/sunos-x64@0.25.11': + optional: true + + '@esbuild/win32-arm64@0.25.11': + optional: true + + '@esbuild/win32-ia32@0.25.11': + optional: true + + '@esbuild/win32-x64@0.25.11': + optional: true + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.37.0(jiti@2.6.1))': dependencies: escape-string-regexp: 4.0.0 @@ -2304,6 +2931,8 @@ snapshots: '@pkgr/core@0.2.9': {} + '@polka/url@1.0.0-next.29': {} + '@quansync/fs@0.1.5': dependencies: quansync: 0.2.11 @@ -2354,6 +2983,72 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.43': {} + '@rollup/rollup-android-arm-eabi@4.52.5': + optional: true + + '@rollup/rollup-android-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-x64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.52.5': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.5': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.5': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.5': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.5': + optional: true + '@sindresorhus/is@4.6.0': {} '@stylistic/eslint-plugin@5.4.0(eslint@9.37.0(jiti@2.6.1))': @@ -2371,10 +3066,16 @@ snapshots: tslib: 2.8.1 optional: true + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -2486,16 +3187,70 @@ snapshots: '@typescript-eslint/types': 8.46.1 eslint-visitor-keys: 4.2.1 - '@vitest/eslint-plugin@1.3.20(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + '@vitest/eslint-plugin@1.3.20(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4)': dependencies: '@typescript-eslint/scope-manager': 8.46.1 '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) eslint: 9.37.0(jiti@2.6.1) optionalDependencies: typescript: 5.9.3 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.19 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/ui@3.2.4(vitest@3.2.4)': + dependencies: + '@vitest/utils': 3.2.4 + fflate: 0.8.2 + flatted: 3.3.3 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1) + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + '@vue/compiler-core@3.5.22': dependencies: '@babel/parser': 7.28.4 @@ -2563,6 +3318,8 @@ snapshots: args-tokenizer@0.3.0: {} + assertion-error@2.0.1: {} + ast-kit@2.1.3: dependencies: '@babel/parser': 7.28.4 @@ -2599,11 +3356,11 @@ snapshots: builtin-modules@5.0.0: {} - bumpp@10.3.1: + bumpp@10.3.1(magicast@0.3.5): dependencies: ansis: 4.2.0 args-tokenizer: 0.3.0 - c12: 3.3.0 + c12: 3.3.0(magicast@0.3.5) cac: 6.7.14 escalade: 3.2.0 jsonc-parser: 3.3.1 @@ -2615,7 +3372,7 @@ snapshots: transitivePeerDependencies: - magicast - c12@3.3.0: + c12@3.3.0(magicast@0.3.5): dependencies: chokidar: 4.0.3 confbox: 0.2.2 @@ -2629,6 +3386,8 @@ snapshots: perfect-debounce: 2.0.0 pkg-types: 2.3.0 rc9: 2.1.2 + optionalDependencies: + magicast: 0.3.5 cac@6.7.14: {} @@ -2638,6 +3397,14 @@ snapshots: ccount@2.0.1: {} + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -2651,6 +3418,8 @@ snapshots: character-entities@2.0.2: {} + check-error@2.1.1: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -2726,6 +3495,8 @@ snapshots: dependencies: character-entities: 2.0.2 + deep-eql@5.0.2: {} + deep-is@0.1.4: {} defu@6.1.4: {} @@ -2763,6 +3534,37 @@ snapshots: environment@1.1.0: {} + es-module-lexer@1.7.0: {} + + esbuild@0.25.11: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -3048,8 +3850,14 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} + expect-type@1.2.2: {} + exsolve@1.0.7: {} fast-deep-equal@3.1.3: {} @@ -3104,6 +3912,9 @@ snapshots: format@0.2.2: {} + fsevents@2.3.3: + optional: true + fzf@0.5.2: {} get-caller-file@2.0.5: {} @@ -3182,6 +3993,8 @@ snapshots: jiti@2.6.1: {} + js-tokens@9.0.1: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -3236,12 +4049,20 @@ snapshots: longest-streak@3.1.0: {} + loupe@3.2.1: {} + lru-cache@10.4.3: {} magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + source-map-js: 1.2.1 + markdown-table@3.0.4: {} marked-terminal@7.3.0(marked@9.1.6): @@ -3592,6 +4413,8 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 + mrmime@2.0.1: {} + ms@2.1.3: {} mz@2.7.0: @@ -3692,6 +4515,8 @@ snapshots: pathe@2.0.3: {} + pathval@2.0.1: {} + perfect-debounce@2.0.0: {} picocolors@1.1.1: {} @@ -3811,6 +4636,34 @@ snapshots: '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.43 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.43 + rollup@4.52.5: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -3829,8 +4682,16 @@ snapshots: shebang-regex@3.0.0: {} + siginfo@2.0.0: {} + signal-exit@4.1.0: {} + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + sisteransi@1.0.5: {} skin-tone@2.0.0: @@ -3848,6 +4709,10 @@ snapshots: spdx-license-ids@3.0.22: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -3862,6 +4727,10 @@ snapshots: strip-json-comments@3.1.1: {} + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3899,6 +4768,10 @@ snapshots: dependencies: any-promise: 1.3.0 + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + tinyexec@1.0.1: {} tinyglobby@0.2.15: @@ -3906,6 +4779,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -3914,6 +4793,8 @@ snapshots: dependencies: eslint-visitor-keys: 3.4.3 + totalist@3.0.1: {} + tree-kill@1.2.2: {} ts-api-utils@2.1.0(typescript@5.9.3): @@ -4007,8 +4888,90 @@ snapshots: util-deprecate@1.0.2: {} + valibot@1.1.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + validate-npm-package-name@5.0.1: {} + vite-node@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.18.10 + fsevents: 2.3.3 + jiti: 2.6.1 + yaml: 2.8.1 + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.2.2 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.18.10 + '@vitest/ui': 3.2.4(vitest@3.2.4) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vue-eslint-parser@10.2.0(eslint@9.37.0(jiti@2.6.1)): dependencies: debug: 4.4.3 @@ -4025,6 +4988,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2cc24f9..0e73bad 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,30 +7,36 @@ catalogs: build: bumpp: ^10.3.1 tsdown: ^0.15.7 - eslint: '@antfu/eslint-config': ^6.0.0 eslint: ^9.37.0 eslint-plugin-antfu: ^3.1.1 - + testing: + '@vitest/ui': ^3.2.4 + vitest: ^3.2.4 types: '@types/node': ^22.9.3 '@types/trusted-types': ^2.0.7 - typescript: '@typescript-eslint/utils': ^8.45.0 typescript: ~5.9.3 - utils: '@antfu/ni': ^25.0.0 '@arethetypeswrong/cli': ^0.17.4 idb: ^8.0.3 + magicast: ^0.3.5 + pathe: ^2.0.3 + pkg-types: ^2.3.0 taze: ^19.7.0 + valibot: ^1.1.0 cleanupUnusedCatalogs: true ignoreWorkspaceRootCheck: true +onlyBuiltDependencies: + - esbuild + shamefullyHoist: true shell-emulator: true diff --git a/tsconfig.json b/tsconfig.json index 9c2cdc2..867b7ac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,22 +6,40 @@ "moduleResolution": "Bundler", "paths": { "@vite-pwa/workbox-background-sync": [ - "./packages/workbox-background-sync/src/" + "./packages/workbox-background-sync/src/index.ts" + ], + "@vite-pwa/workbox-background-sync/*": [ + "./packages/workbox-background-sync/src/*.ts" ], "@vite-pwa/workbox-broadcast-update": [ - "./packages/workbox-broadcast-update/src/" + "./packages/workbox-broadcast-update/src/index.ts" + ], + "@vite-pwa/workbox-broadcast-update/*": [ + "./packages/workbox-broadcast-update/src/*.ts" + ], + "@vite-pwa/workbox-build": [ + "./packages/workbox-build/src/index.ts" + ], + "@vite-pwa/workbox-build/*": [ + "./packages/workbox-build/src/*.ts" ], "@vite-pwa/workbox-cacheable-response": [ - "./packages/workbox-cacheable-response/src/" + "./packages/workbox-cacheable-response/src/index.ts" + ], + "@vite-pwa/workbox-cacheable-response/*": [ + "./packages/workbox-cacheable-response/src/*.ts" ], "@vite-pwa/workbox-core": [ - "./packages/workbox-core/src/" + "./packages/workbox-core/src/index.ts" ], - "@vite-pwa/workbox-core/internals": [ - "./packages/workbox-core/src/internals.ts" + "@vite-pwa/workbox-core/*": [ + "./packages/workbox-core/src/*.ts" ], "@vite-pwa/workbox-expiration": [ - "./packages/workbox-expiration/src/" + "./packages/workbox-expiration/src/index.ts" + ], + "@vite-pwa/workbox-expiration/*": [ + "./packages/workbox-expiration/src/*.index" ], "@vite-pwa/workbox-navigation-preload": [ "./packages/workbox-navigation-preload/src/" @@ -36,7 +54,10 @@ "./packages/workbox-recipes/src/" ], "@vite-pwa/workbox-routing": [ - "./packages/workbox-routing/src/" + "./packages/workbox-routing/src/index.ts" + ], + "@vite-pwa/workbox-routing/*": [ + "./packages/workbox-routing/src/*.ts" ], "@vite-pwa/workbox-strategies": [ "./packages/workbox-strategies/src/" @@ -53,7 +74,8 @@ }, "resolveJsonModule": true, "types": [ - "node" + "node", + "vitest/globals" ], "strict": true, "strictNullChecks": true, diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..d66d3ff --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vitest/config' +import { alias } from './alias' + +export default defineConfig({ + optimizeDeps: { + entries: [], + }, + resolve: { + alias, + }, + test: { + globals: true, + environment: 'node', + testTimeout: 30_000, + name: 'unit', + }, +}) From 461701d6536ba3422f669715b181b6ece5d079c0 Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 19 Oct 2025 22:59:53 +0200 Subject: [PATCH 02/20] chore: change validation logic --- .../workbox-build/test/validation.test.ts | 95 +++++++++++++++++-- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index 27cfab4..ad271a5 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -1,6 +1,7 @@ import type * as v from 'valibot' import type { GenerateSWOptions, GetManifestOptions, InjectManifestOptions } from '../src/types' import { describe, expect, expectTypeOf, it } from 'vitest' + import { GenerateSWOptionsSchema } from '../src/validation/generate-sw' import { GetManifestOptionsSchema } from '../src/validation/get-manifest' import { InjectManifestOptionsSchema } from '../src/validation/inject-manifest' @@ -29,7 +30,7 @@ describe('generateSWOptions Schema Validation', () => { swDest: 'sw.js', globDirectory: './', } satisfies GenerateSWOptions - // @ts-expect-error - See comment at schema Type Inference Validation. + // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() }) @@ -49,17 +50,40 @@ describe('generateSWOptions Schema Validation', () => { swDest: 'sw.js', globDirectory: './', manifestTransforms: [ - (manifest) => { - // dummy transform + (manifest: any) => { return { manifest, warnings: [] } }, ], } satisfies GenerateSWOptions - // @ts-expect-error - See comment at schema Type Inference Validation. + // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() }) + it('should fail if manifestTransforms contains a non-function', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + manifestTransforms: ['not-a-function'], + } satisfies Omit & { manifestTransforms: string[] } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- Each item in the "manifestTransforms" array must be a function.', + ) + }) + + it('should fail if manifestTransforms is a function instead of an array', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), + } satisfies Omit & { manifestTransforms: (manifest: any) => void } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array.', + ) + }) + it('should pass if globDirectory is missing but runtimeCaching is present', () => { const options = { swDest: 'sw.js', @@ -70,7 +94,7 @@ describe('generateSWOptions Schema Validation', () => { }, ], } satisfies GenerateSWOptions - // @ts-expect-error - See comment at schema Type Inference Validation. + // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() }) @@ -92,7 +116,8 @@ describe('generateSWOptions Schema Validation', () => { swDest: 'sw.txt', globDirectory: './', } satisfies GenerateSWOptions - // @ts-expect-error - See comment at schema Type Inference Validation. + // The shape is valid, but the content is not. + // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( 'generateSW() options validation failed: \n- swDest must end with .js', @@ -105,7 +130,7 @@ describe('getManifestOptions Schema Validation', () => { const options = { globDirectory: './', } satisfies GetManifestOptions - // @ts-expect-error - See comment at schema Type Inference Validation. + // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(GetManifestOptionsSchema, options, 'getManifest')).not.toThrow() }) @@ -118,6 +143,28 @@ describe('getManifestOptions Schema Validation', () => { ) }) + it('should fail if manifestTransforms contains a non-function', () => { + const options = { + globDirectory: './', + manifestTransforms: ['not-a-function'], + } satisfies Omit & { manifestTransforms: string[] } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + 'getManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function.', + ) + }) + + it('should fail if manifestTransforms is a function instead of an array', () => { + const options = { + globDirectory: './', + manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), + } satisfies Omit & { manifestTransforms: (manifest: any) => void } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', + ) + }) + it('should fail if an unknown property is present', () => { const options = { globDirectory: './', @@ -137,7 +184,7 @@ describe('injectManifestOptions Schema Validation', () => { swDest: 'sw-injected.js', globDirectory: './', } satisfies InjectManifestOptions - // @ts-expect-error - See comment at schema Type Inference Validation. + // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).not.toThrow() }) @@ -153,13 +200,41 @@ describe('injectManifestOptions Schema Validation', () => { ) }) + it('should fail if manifestTransforms contains a non-function', () => { + const options = { + swSrc: 'sw.js', + swDest: 'sw-injected.js', + globDirectory: './', + manifestTransforms: ['not-a-function'], + } satisfies Omit & { manifestTransforms: string[] } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + 'injectManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function.', + ) + }) + + it('should fail if manifestTransforms is a function instead of an array', () => { + const options = { + swSrc: 'sw.js', + swDest: 'sw-injected.js', + globDirectory: './', + manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), + } satisfies Omit & { manifestTransforms: (manifest: any) => void } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', + ) + }) + it('should fail if swDest does not end with .js', () => { const options = { swSrc: 'sw.js', swDest: 'sw-injected.txt', globDirectory: './', } satisfies InjectManifestOptions - expectTypeOf().not.toMatchObjectType() + // The shape is valid, but the content is not. + // @ts-expect-error - schema Type Inference Validation. + expectTypeOf().toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).toThrow( 'injectManifest() options validation failed: \n- swDest must end with .js', ) @@ -173,7 +248,7 @@ describe('injectManifestOptions Schema Validation', () => { anotherUnknown: 'noop', } satisfies InjectManifestOptions & { anotherUnknown?: string } expectTypeOf().not.toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).toThrow( + expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( 'injectManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', ) }) From 8b21b573bcfd53f53e97ac40fb09099f36904eec Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 19 Oct 2025 23:00:30 +0200 Subject: [PATCH 03/20] chore: add manifestTransforms tests --- .../src/validation/validation-helper.ts | 67 +++++++++++++------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 47d21c0..4bfcdf5 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -1,30 +1,55 @@ -import type { BaseIssue, BaseSchema } from 'valibot' -import { safeParse, setGlobalMessage } from 'valibot' - -// Set a global configuration for Valibot's error messages to make them -// more user-friendly for Workbox consumers. -setGlobalMessage((issue: BaseIssue) => { - // The path provides context to which field has the error. - // We get the last key in the path for the most specific field name. - const key = issue.path?.[issue.path.length - 1]?.key - - // When a required key is missing, Valibot's `issue.received` is 'undefined'. - // This is a robust way to create a user-friendly "missing option" message. +import type { BaseIssue, BaseSchema, IssuePathItem } from 'valibot' +import { safeParse } from 'valibot' + +// This helper function traverses the issue's path to find the top-level object key +// that contains the error. This is crucial for errors nested inside arrays. +function getTopLevelKey(path: IssuePathItem[] | undefined): string | undefined { + if (!path || path.length === 0) { + return undefined + } + // The path is ordered from the outside in. The first item is the outermost. + // Its key is what we need to identify the top-level option. + return path[0].key as string | undefined +} + +// see [Path Key not Available in safeParse](https://github.com/fabian-hiller/valibot/discussions/696). +// custom Valibot's message mapping +function extractIssueMessage(issue: BaseIssue) { + const topLevelKey = getTopLevelKey(issue.path) + const lastKey = issue.path?.[issue.path.length - 1]?.key + + // Case 1: Missing required key. The `key` is on the last path item. if (issue.received === 'undefined') { - return `The required option "${key}" is missing.` + return `The required option "${lastKey}" is missing.` } - // For unknown keys in strict objects, `issue.expected` is 'never'. - // This allows us to create a clear "unknown option" message. + // Case 2: Unknown key in strict object. The `key` is on the last path item. if (issue.expected === 'never') { - return `The option "${key}" is unknown or has been deprecated.` + return `The option "${lastKey}" is unknown or has been deprecated.` } - // For all other errors, including our custom pipes (e.g., `endsWith`), - // the message provided directly to the validation function is used. - // We fall back to this to ensure custom messages are always respected. + // Case 3: Specific, human-friendly message for manifestTransforms + if (topLevelKey === 'manifestTransforms') { + // This error happens when the value is not an array (e.g., a function) + if (issue.expected?.includes('Array')) { + return 'The "manifestTransforms" option must be an array of functions.' + } + // This error happens for items within the array + return 'Each item in the "manifestTransforms" array must be a function.' + } + + // Case 3.5: Specific, human-friendly message for swDest endsWith pipe + if (topLevelKey === 'swDest' && issue.requirement === '.js') { + return issue.message + } + + // Case 4: Generic type mismatch for other fields + if (issue.expected && typeof lastKey === 'string') + return `Invalid type for option "${lastKey}". Expected ${issue.expected} but received ${typeof issue.input}.` + + // Fallback for our custom pipes (e.g., `endsWith`) and any other unhandled case return issue.message -}) +} /** * A wrapper around Valibot's `safeParse` that throws a user-friendly error @@ -40,7 +65,7 @@ export function validate>( ): void { const result = safeParse(schema, options) if (!result.success) { - const errorMessages = result.issues.map(issue => issue.message) + const errorMessages = result.issues.map(extractIssueMessage) throw new Error( `${methodName}() options validation failed: \n- ${errorMessages.join('\n- ')}`, ) From 593b307ee9f581c862ad0536fcf161a96a155ea3 Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 00:30:02 +0200 Subject: [PATCH 04/20] chore: update validation and tests --- .../src/validation/validation-helper.ts | 25 +++++- .../workbox-build/test/validation.test.ts | 84 +++++++++++++++++-- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 4bfcdf5..3b8ff7b 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -19,7 +19,8 @@ function extractIssueMessage(issue: BaseIssue) { const lastKey = issue.path?.[issue.path.length - 1]?.key // Case 1: Missing required key. The `key` is on the last path item. - if (issue.received === 'undefined') { + // We exclude array-based options here to let their specific handlers take over. + if (issue.received === 'undefined' && topLevelKey !== 'runtimeCaching' && topLevelKey !== 'manifestTransforms') { return `The required option "${lastKey}" is missing.` } @@ -34,11 +35,27 @@ function extractIssueMessage(issue: BaseIssue) { if (issue.expected?.includes('Array')) { return 'The "manifestTransforms" option must be an array of functions.' } - // This error happens for items within the array - return 'Each item in the "manifestTransforms" array must be a function.' + const index = issue.path?.find(p => p.type === 'array')?.key + return `Each item in the "manifestTransforms" array must be a function (error at index ${index}).` } - // Case 3.5: Specific, human-friendly message for swDest endsWith pipe + // Case 3.2: Specific, human-friendly message for runtimeCaching + if (topLevelKey === 'runtimeCaching') { + // This error happens when the value is not an array (e.g., a function) + if (issue.expected?.includes('Array')) { + return 'The "runtimeCaching" option must be an array of handlers.' + } + if (lastKey === 'handler') { + if (issue.type === 'union') { + return `Invalid "handler" option in runtimeCaching[${issue.path?.[1]?.key}]. ${issue.message}.` + } + if (issue.type === 'object') { + return `Invalid "handler" option in runtimeCaching[${issue.path?.[1]?.key}]. The required option "handler" is missing.` + } + } + } + + // Case 3.3: Specific, human-friendly message for swDest endsWith pipe if (topLevelKey === 'swDest' && issue.requirement === '.js') { return issue.message } diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index ad271a5..9ffb7e8 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -68,7 +68,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: string[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- Each item in the "manifestTransforms" array must be a function.', + 'generateSW() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) }) @@ -80,7 +80,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array.', + 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array of functions.', ) }) @@ -123,6 +123,78 @@ describe('generateSWOptions Schema Validation', () => { 'generateSW() options validation failed: \n- swDest must end with .js', ) }) + + describe('runtimeCaching validation', () => { + it('should pass with a valid runtimeCaching entry', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + runtimeCaching: [{ + urlPattern: /.*/, + handler: 'NetworkFirst', + }], + } satisfies GenerateSWOptions + // @ts-expect-error - schema Type Inference Validation. + expectTypeOf().toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + }) + + it('should fail if runtimeCaching is not an array (string)', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + runtimeCaching: 'a-string-not-an-array', + } satisfies Omit & { runtimeCaching: string } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- The "runtimeCaching" option must be an array of handlers.', + ) + }) + + it('should fail if a runtimeCaching entry is missing a handler', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + runtimeCaching: [{ + urlPattern: /.*/, + }], + } satisfies Omit & { runtimeCaching: { urlPattern: RegExp }[] } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. The required option "handler" is missing.', + ) + }) + + it('should fail if a runtimeCaching entry has an invalid handler type', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + runtimeCaching: [{ + urlPattern: /.*/, + handler: 123, + }], + } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: number }[] } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received 123.', + ) + }) + + it('should fail if a runtimeCaching entry has an invalid handler string value', () => { + const options = { + swDest: 'sw.js', + globDirectory: './', + runtimeCaching: [{ + urlPattern: /.*/, + handler: 'InvalidStrategy', + }], + } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: 'InvalidStrategy' }[] } + expectTypeOf().not.toMatchObjectType() + expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received "InvalidStrategy".', + ) + }) + }) }) describe('getManifestOptions Schema Validation', () => { @@ -150,7 +222,7 @@ describe('getManifestOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: string[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( - 'getManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function.', + 'getManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) }) @@ -161,7 +233,7 @@ describe('getManifestOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( - 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', + 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array of functions.', ) }) @@ -209,7 +281,7 @@ describe('injectManifestOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: string[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( - 'injectManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function.', + 'injectManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) }) @@ -222,7 +294,7 @@ describe('injectManifestOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( - 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', + 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array of functions.', ) }) From 1d559822e5801eb6d58a956a23963a9da9c29acd Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 00:58:36 +0200 Subject: [PATCH 05/20] chore: refactor validation helper --- .../src/validation/validation-helper.ts | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 3b8ff7b..83b0fb4 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -1,5 +1,5 @@ import type { BaseIssue, BaseSchema, IssuePathItem } from 'valibot' -import { safeParse } from 'valibot' +import { getDotPath, safeParse } from 'valibot' // This helper function traverses the issue's path to find the top-level object key // that contains the error. This is crucial for errors nested inside arrays. @@ -15,56 +15,46 @@ function getTopLevelKey(path: IssuePathItem[] | undefined): string | undefined { // see [Path Key not Available in safeParse](https://github.com/fabian-hiller/valibot/discussions/696). // custom Valibot's message mapping function extractIssueMessage(issue: BaseIssue) { + const path = getDotPath(issue) const topLevelKey = getTopLevelKey(issue.path) - const lastKey = issue.path?.[issue.path.length - 1]?.key - // Case 1: Missing required key. The `key` is on the last path item. - // We exclude array-based options here to let their specific handlers take over. - if (issue.received === 'undefined' && topLevelKey !== 'runtimeCaching' && topLevelKey !== 'manifestTransforms') { - return `The required option "${lastKey}" is missing.` - } + // Priority 1: Custom messages from pipes (e.g., endsWith). + // These are the most specific and should always be shown. + if (issue.type === 'custom') + return issue.message - // Case 2: Unknown key in strict object. The `key` is on the last path item. - if (issue.expected === 'never') { - return `The option "${lastKey}" is unknown or has been deprecated.` - } + // Priority 2: Missing required key. + if (issue.kind === 'schema' && issue.received === 'undefined') + return `The required option "${path}" is missing.` - // Case 3: Specific, human-friendly message for manifestTransforms - if (topLevelKey === 'manifestTransforms') { - // This error happens when the value is not an array (e.g., a function) - if (issue.expected?.includes('Array')) { - return 'The "manifestTransforms" option must be an array of functions.' - } + // Priority 3: Unknown key in strict object. + if (issue.type === 'strict_object') + return `The option "${path}" is unknown or has been deprecated.` + + // Priority 4: Human-readable message for array-based options + if (topLevelKey === 'manifestTransforms' || topLevelKey === 'runtimeCaching') { + // This error happens when the value itself is not an array + if (issue.expected?.includes('Array')) + return `The "${topLevelKey}" option must be an array.` + + // This error happens for items within the array const index = issue.path?.find(p => p.type === 'array')?.key - return `Each item in the "manifestTransforms" array must be a function (error at index ${index}).` - } + if (topLevelKey === 'manifestTransforms') + return `Each item in the "manifestTransforms" array must be a function (error at index ${index}).` - // Case 3.2: Specific, human-friendly message for runtimeCaching - if (topLevelKey === 'runtimeCaching') { - // This error happens when the value is not an array (e.g., a function) - if (issue.expected?.includes('Array')) { - return 'The "runtimeCaching" option must be an array of handlers.' - } - if (lastKey === 'handler') { - if (issue.type === 'union') { - return `Invalid "handler" option in runtimeCaching[${issue.path?.[1]?.key}]. ${issue.message}.` - } - if (issue.type === 'object') { - return `Invalid "handler" option in runtimeCaching[${issue.path?.[1]?.key}]. The required option "handler" is missing.` - } + if (topLevelKey === 'runtimeCaching') { + if (issue.path?.some(p => p.key === 'handler')) + return `Invalid "handler" option in runtimeCaching[${index}]. Expected one of (string, function, object) but received ${typeof issue.input}.` + return `Invalid item at index ${index} in the "runtimeCaching" array.` } } - // Case 3.3: Specific, human-friendly message for swDest endsWith pipe - if (topLevelKey === 'swDest' && issue.requirement === '.js') { - return issue.message + // Priority 5: Generic type mismatch for other fields. + if (issue.kind === 'schema') { + return `Invalid type for option "${path}". Expected ${issue.expected} but received ${issue.received}.` } - // Case 4: Generic type mismatch for other fields - if (issue.expected && typeof lastKey === 'string') - return `Invalid type for option "${lastKey}". Expected ${issue.expected} but received ${typeof issue.input}.` - - // Fallback for our custom pipes (e.g., `endsWith`) and any other unhandled case + // Fallback for any other unhandled case. return issue.message } From 18a013e3f25818b557542b171a46b8fece525f1d Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 00:58:46 +0200 Subject: [PATCH 06/20] chore: update validation messages --- packages/workbox-build/test/validation.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index 9ffb7e8..adc0db7 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -80,7 +80,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array of functions.', + 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array.', ) }) @@ -147,7 +147,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: string } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- The "runtimeCaching" option must be an array of handlers.', + 'generateSW() options validation failed: \n- The "runtimeCaching" option must be an array.', ) }) @@ -161,7 +161,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: { urlPattern: RegExp }[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. The required option "handler" is missing.', + 'generateSW() options validation failed: \n- The required option "runtimeCaching.0.handler" is missing.', ) }) @@ -176,7 +176,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: number }[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received 123.', + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Expected one of (string, function, object) but received number.', ) }) @@ -191,7 +191,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: 'InvalidStrategy' }[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received "InvalidStrategy".', + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Expected one of (string, function, object) but received string.', ) }) }) @@ -233,7 +233,7 @@ describe('getManifestOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( - 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array of functions.', + 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', ) }) @@ -294,7 +294,7 @@ describe('injectManifestOptions Schema Validation', () => { } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( - 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array of functions.', + 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', ) }) From fea9686373d21d43fd90386d8550083c6764f916 Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 01:07:34 +0200 Subject: [PATCH 07/20] chore: refactor again the validation helper and the tests message --- packages/workbox-build/src/validation/validation-helper.ts | 2 +- packages/workbox-build/test/validation.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 83b0fb4..05bed50 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -44,7 +44,7 @@ function extractIssueMessage(issue: BaseIssue) { if (topLevelKey === 'runtimeCaching') { if (issue.path?.some(p => p.key === 'handler')) - return `Invalid "handler" option in runtimeCaching[${index}]. Expected one of (string, function, object) but received ${typeof issue.input}.` + return `Invalid "handler" option in runtimeCaching[${index}]. ${issue.message}.` return `Invalid item at index ${index} in the "runtimeCaching" array.` } } diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index adc0db7..4dd7a56 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -176,7 +176,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: number }[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Expected one of (string, function, object) but received number.', + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received 123.', ) }) @@ -191,7 +191,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: 'InvalidStrategy' }[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Expected one of (string, function, object) but received string.', + 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received "InvalidStrategy"', ) }) }) From bc7f746ed07bc0f868824800886ffb2c587ada72 Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 01:28:49 +0200 Subject: [PATCH 08/20] chore: handle `catch-all` case --- .../src/validation/validation-helper.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 05bed50..ff59673 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -17,6 +17,7 @@ function getTopLevelKey(path: IssuePathItem[] | undefined): string | undefined { function extractIssueMessage(issue: BaseIssue) { const path = getDotPath(issue) const topLevelKey = getTopLevelKey(issue.path) + const lastKey = issue.path?.[issue.path.length - 1]?.key // Priority 1: Custom messages from pipes (e.g., endsWith). // These are the most specific and should always be shown. @@ -43,9 +44,28 @@ function extractIssueMessage(issue: BaseIssue) { return `Each item in the "manifestTransforms" array must be a function (error at index ${index}).` if (topLevelKey === 'runtimeCaching') { - if (issue.path?.some(p => p.key === 'handler')) - return `Invalid "handler" option in runtimeCaching[${index}]. ${issue.message}.` - return `Invalid item at index ${index} in the "runtimeCaching" array.` + // This error happens when the value is not an array (e.g., a function) + if (issue.expected?.includes('Array')) { + return 'The "runtimeCaching" option must be an array of handlers.' + } + + const index = issue.path?.find(p => p.type === 'array')?.key + + if (lastKey === 'handler') { + if (issue.type === 'union') { + if (issue.input === 'InvalidStrategy') { + return `Invalid "handler" option in runtimeCaching[${index}]. ${issue.message}.` + } + return `Invalid "handler" option in runtimeCaching[${index}]. ${issue.message}.` + } + if (issue.type === 'object') { + return `Invalid "handler" option in runtimeCaching[${index}]. The required option "handler" is missing.` + } + } + + // This is the catch-all for any other error inside a runtimeCaching item. + // It handles cases where an item is not an object, or is missing urlPattern, etc. + return `Invalid item at index ${index} in the "runtimeCaching" array. ${issue.message}.` } } From 9ae22412d45c9e2eda0f84e91b6f519f038c23d9 Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 01:31:42 +0200 Subject: [PATCH 09/20] chore: remove webpack types --- packages/workbox-build/src/types.ts | 49 ----------------------------- 1 file changed, 49 deletions(-) diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts index 404d567..f5f7d21 100644 --- a/packages/workbox-build/src/types.ts +++ b/packages/workbox-build/src/types.ts @@ -486,45 +486,6 @@ export interface RequiredSWDestPartial { swDest: string } -export interface WebpackGenerateSWPartial { - /** - * One or more names of webpack chunks. The content of those chunks will be - * included in the generated service worker, via a call to `importScripts()`. - */ - importScriptsViaChunks?: Array - /** - * The asset name of the service worker file created by this plugin. - * @default "service-worker.js" - */ - swDest?: string -} - -export interface WebpackInjectManifestPartial { - /** - * When `true` (the default), the `swSrc` file will be compiled by webpack. - * When `false`, compilation will not occur (and `webpackCompilationPlugins` - * can't be used.) Set to `false` if you want to inject the manifest into, - * e.g., a JSON file. - * @default true - */ - compileSrc?: boolean - // This doesn't have a hardcoded default value; instead, the default will be - // set at runtime to the swSrc basename, with the hardcoded extension .js. - /** - * The asset name of the service worker file that will be created by this - * plugin. If omitted, the name will be based on the `swSrc` name. - */ - swDest?: string - // This can only be set if compileSrc is true, but that restriction can't be - // represented in TypeScript. It's enforced via custom runtime validation - // logic and needs to be documented. - /** - * Optional `webpack` plugins that will be used when compiling the `swSrc` - * input file. Only valid if `compileSrc` is `true`. - */ - webpackCompilationPlugins?: Array -} - export type GenerateSWOptions = BasePartial & GlobPartial & GeneratePartial @@ -541,16 +502,6 @@ export type InjectManifestOptions = BasePartial & RequiredSWDestPartial & RequiredGlobDirectoryPartial -export type WebpackGenerateSWOptions = BasePartial - & WebpackPartial - & GeneratePartial - & WebpackGenerateSWPartial - -export type WebpackInjectManifestOptions = BasePartial - & WebpackPartial - & InjectPartial - & WebpackInjectManifestPartial - export interface GetManifestResult { count: number manifestEntries: Array From ed5554d3a384f762e35436922fedf3c84cf0596f Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 01:48:39 +0200 Subject: [PATCH 10/20] chore: remove last webpack type --- packages/workbox-build/src/types.ts | 39 ----------------------------- 1 file changed, 39 deletions(-) diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts index f5f7d21..776a59b 100644 --- a/packages/workbox-build/src/types.ts +++ b/packages/workbox-build/src/types.ts @@ -438,45 +438,6 @@ export interface InjectPartial { swSrc: string } -export interface WebpackPartial { - /** - * One or more chunk names whose corresponding output files should be included - * in the precache manifest. - */ - chunks?: Array - // We can't use the @default annotation here to assign the value via AJV, as - // an Array can't be serialized into JSON. - // The default value of [/\.map$/, /^manifest.*\.js$/] will be assigned by - // the validation function, and we need to reflect that in the docs. - /** - * One or more specifiers used to exclude assets from the precache manifest. - * This is interpreted following - * [the same rules](https://webpack.js.org/configuration/module/#condition) - * as `webpack`'s standard `exclude` option. - * If not provided, the default value is `[/\.map$/, /^manifest.*\.js$]`. - */ - exclude?: Array boolean)> - /** - * One or more chunk names whose corresponding output files should be excluded - * from the precache manifest. - */ - excludeChunks?: Array - /** - * One or more specifiers used to include assets in the precache manifest. - * This is interpreted following - * [the same rules](https://webpack.js.org/configuration/module/#condition) - * as `webpack`'s standard `include` option. - */ - include?: Array boolean)> - /** - * If set to 'production', then an optimized service worker bundle that - * excludes debugging info will be produced. If not explicitly configured - * here, the `mode` value configured in the current `webpack` compilation - * will be used. - */ - mode?: string | null -} - export interface RequiredSWDestPartial { /** * The path and filename of the service worker file that will be created by From abbecda230d624199df7e0fa222b3179ff772610 Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 01:54:28 +0200 Subject: [PATCH 11/20] chore: add `get-manifest` subpackage export --- packages/workbox-build/package.json | 1 + packages/workbox-build/src/get-manifest.ts | 5 +++++ packages/workbox-build/tsdown.config.ts | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 packages/workbox-build/src/get-manifest.ts diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index d03878b..fd8c613 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -41,6 +41,7 @@ "*": { "types": ["dist/types.d.ts"], "generate-sw": ["dist/generate-sw.d.ts"], + "get-manifest": ["dist/get-manifest.d.ts"], "inject-manifest": ["dist/inject-manifest.d.ts"] } }, diff --git a/packages/workbox-build/src/get-manifest.ts b/packages/workbox-build/src/get-manifest.ts new file mode 100644 index 0000000..5d83455 --- /dev/null +++ b/packages/workbox-build/src/get-manifest.ts @@ -0,0 +1,5 @@ +import type { GetManifestOptions } from './types' + +export async function injectManifest(config: GetManifestOptions) { + +} diff --git a/packages/workbox-build/tsdown.config.ts b/packages/workbox-build/tsdown.config.ts index 403a67e..84e6a58 100644 --- a/packages/workbox-build/tsdown.config.ts +++ b/packages/workbox-build/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/{index,types,generate-sw,inject-manifest}.ts', + entry: './src/{index,types,generate-sw,get-manifest,inject-manifest}.ts', platform: 'node', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team From 68ad54f17c90587c23138667a87415f9c9017b99 Mon Sep 17 00:00:00 2001 From: userquin Date: Mon, 20 Oct 2025 19:36:19 +0200 Subject: [PATCH 12/20] chore: fix valibot schemas --- package.json | 2 + packages/workbox-build/package.json | 4 +- packages/workbox-build/src/generate-sw.ts | 35 +++- .../workbox-build/src/validation/errors.ts | 126 +++++++++++ .../src/validation/generate-sw.ts | 27 ++- .../src/validation/get-manifest.ts | 150 +++++++------- .../src/validation/inject-manifest.ts | 190 ++++++++--------- .../src/validation/validation-helper.ts | 45 +++- .../src/validation/validation-options.ts | 49 +++++ .../workbox-build/test/validation.test.ts | 16 +- pnpm-lock.yaml | 195 ++++++++++-------- pnpm-workspace.yaml | 1 + 12 files changed, 571 insertions(+), 269 deletions(-) create mode 100644 packages/workbox-build/src/validation/errors.ts create mode 100644 packages/workbox-build/src/validation/validation-options.ts diff --git a/package.json b/package.json index ff30901..a4819b0 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "lint": "eslint .", "lint:fix": "nr lint --fix", "test": "vitest", + "test:run": "vitest run", "test:ui": "vitest --ui", "update-deps": "taze minor -wIr", "release": "bumpp -r --excute=\"pnpm i\"" @@ -42,6 +43,7 @@ "eslint-plugin-antfu": "catalog:eslint", "taze": "catalog:utils", "tsdown": "catalog:build", + "tsx": "^4.20.6", "typescript": "catalog:typescript", "vitest": "catalog:testing" }, diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index fd8c613..b78dfbc 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -50,7 +50,8 @@ ], "scripts": { "build": "tsdown", - "prepublishOnly": "pnpm run build" + "prepublishOnly": "pnpm run build", + "node:test": "node ./dist/generate-sw.js" }, "dependencies": { "@vite-pwa/workbox-background-sync": "workspace:*", @@ -62,6 +63,7 @@ "magicast": "catalog:utils", "pathe": "catalog:utils", "pkg-types": "catalog:utils", + "proper-tags": "catalog:utils", "valibot": "catalog:utils" } } diff --git a/packages/workbox-build/src/generate-sw.ts b/packages/workbox-build/src/generate-sw.ts index efa7324..fb29138 100644 --- a/packages/workbox-build/src/generate-sw.ts +++ b/packages/workbox-build/src/generate-sw.ts @@ -1,5 +1,38 @@ +import type { ProxifiedModule, ProxifiedObject } from 'magicast' import type { GenerateSWOptions } from './types' +import { generateCode, parseModule } from 'magicast' +import { getDefaultExportOptions } from 'magicast/helpers' +import { GenerateSWOptionsSchema } from './validation/generate-sw' +import { validate } from './validation/validation-helper' -export async function generateSW(config: GenerateSWOptions) { +export async function generateSW( + options: ProxifiedModule, +): Promise { + const config = getDefaultExportOptions(options) as ProxifiedObject + console.log(config.swDest) + console.log(generateCode(config).code) + validate( + GenerateSWOptionsSchema, + config, + 'generateSW', + ) + // ensureValidNavigationPreloadConfig(config) + // ensureValidCacheExpiration(config) + // ensureValidRuntimeCachingOrGlobDirectory(config) +} +async function main() { + const module = parseModule(`export default { + swDest: 'sw.js', + runtimeCaching: [ + { + urlPattern: /.*!/, + handler: 'NetworkFirst', + }, + ], +} +`) + await generateSW(module) } + +main() diff --git a/packages/workbox-build/src/validation/errors.ts b/packages/workbox-build/src/validation/errors.ts new file mode 100644 index 0000000..ef0b626 --- /dev/null +++ b/packages/workbox-build/src/validation/errors.ts @@ -0,0 +1,126 @@ +/* + Copyright 2018 Google LLC + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import { oneLine as ol } from 'proper-tags' + +export const errors = { + 'unable-to-get-rootdir': `Unable to get the root directory of your web app.`, + 'no-extension': ol`Unable to detect a usable extension for a file in your web + app directory.`, + 'invalid-file-manifest-name': ol`The File Manifest Name must have at least one + character.`, + 'unable-to-get-file-manifest-name': 'Unable to get a file manifest name.', + 'missing-sw-dest': `The 'swDest' option is required.`, + 'invalid-sw-dest': `The 'swDest' value must be a valid path.`, + 'invalid-sw-dest-js-ext': `The 'swDest' must end with .js.`, + 'unable-to-get-sw-name': 'Unable to get a service worker file name.', + 'unable-to-get-save-config': ol`An error occurred when asking to save details + in a config file.`, + 'unable-to-get-file-hash': ol`An error occurred when attempting to create a + file hash.`, + 'unable-to-get-file-size': ol`An error occurred when attempting to get a file + size.`, + 'unable-to-glob-files': 'An error occurred when globbing for files.', + 'unable-to-make-manifest-directory': ol`Unable to make output directory for + file manifest.`, + 'read-manifest-template-failure': 'Unable to read template for file manifest', + 'populating-manifest-tmpl-failed': ol`An error occurred when populating the + file manifest template.`, + 'manifest-file-write-failure': 'Unable to write the file manifest.', + 'unable-to-make-sw-directory': ol`Unable to make the directories to output + the service worker path.`, + 'read-sw-template-failure': ol`Unable to read the service worker template + file.`, + 'sw-write-failure': 'Unable to write the service worker file.', + 'sw-write-failure-directory': ol`Unable to write the service worker file; + 'swDest' should be a full path to the file, not a path to a directory.`, + 'unable-to-copy-workbox-libraries': ol`One or more of the Workbox libraries + could not be copied over to the destination directory: `, + 'invalid-generate-sw-input': ol`The input to generateSW() must be an object.`, + 'invalid-glob-directory': ol`The supplied globDirectory must be a path as a + string.`, + 'invalid-dont-cache-bust': ol`The supplied 'dontCacheBustURLsMatching' + parameter must be a RegExp.`, + 'invalid-exclude-files': 'The excluded files should be an array of strings.', + 'invalid-get-manifest-entries-input': ol`The input to + 'getFileManifestEntries()' must be an object.`, + 'invalid-manifest-path': ol`The supplied manifest path is not a string with + at least one character.`, + 'invalid-manifest-entries': ol`The manifest entries must be an array of + strings or JavaScript objects containing a url parameter.`, + 'invalid-manifest-format': ol`The value of the 'format' option passed to + generateFileManifest() must be either 'iife' (the default) or 'es'.`, + 'invalid-static-file-globs': ol`The 'globPatterns' value must be an array + of strings.`, + 'invalid-templated-urls': ol`The 'templatedURLs' value should be an object + that maps URLs to either a string, or to an array of glob patterns.`, + 'templated-url-matches-glob': ol`One of the 'templatedURLs' URLs is already + being tracked via 'globPatterns': `, + 'invalid-glob-ignores': ol`The 'globIgnores' parameter must be an array of + glob pattern strings.`, + 'manifest-entry-bad-url': ol`The generated manifest contains an entry without + a URL string. This is likely an error with workbox-build.`, + 'modify-url-prefix-bad-prefixes': ol`The 'modifyURLPrefix' parameter must be + an object with string key value pairs.`, + 'invalid-inject-manifest-arg': ol`The input to 'injectManifest()' must be an + object.`, + 'injection-point-not-found': ol`Unable to find a place to inject the manifest. + Please ensure that your service worker file contains the following: `, + 'multiple-injection-points': ol`Please ensure that your 'swSrc' file contains + only one match for the following: `, + 'populating-sw-tmpl-failed': ol`Unable to generate service worker from + template.`, + 'useless-glob-pattern': ol`One of the glob patterns doesn't match any files. + Please remove or fix the following: `, + 'bad-template-urls-asset': ol`There was an issue using one of the provided + 'templatedURLs'.`, + 'invalid-runtime-caching': ol`The 'runtimeCaching' parameter must an an + array of objects with at least a 'urlPattern' and 'handler'.`, + 'static-file-globs-deprecated': ol`'staticFileGlobs' is deprecated. + Please use 'globPatterns' instead.`, + 'dynamic-url-deprecated': ol`'dynamicURLToDependencies' is deprecated. + Please use 'templatedURLs' instead.`, + 'urlPattern-is-required': ol`The 'urlPattern' option is required when using + 'runtimeCaching'.`, + 'handler-is-required': ol`The 'handler' option is required when using + runtimeCaching.`, + 'invalid-generate-file-manifest-arg': ol`The input to generateFileManifest() + must be an Object.`, + 'invalid-sw-src': `The 'swSrc' file can't be read.`, + 'same-src-and-dest': ol`Unable to find a place to inject the manifest. This is + likely because swSrc and swDest are configured to the same file. + Please ensure that your swSrc file contains the following:`, + 'only-regexp-routes-supported': ol`Please use a regular expression object as + the urlPattern parameter. (Express-style routes are not currently + supported.)`, + 'bad-runtime-caching-config': ol`An unknown configuration option was used + with runtimeCaching: `, + 'invalid-network-timeout-seconds': ol`When using networkTimeoutSeconds, you + must set the handler to 'NetworkFirst'.`, + 'no-module-name': ol`You must provide a moduleName parameter when calling + getModuleURL().`, + 'bad-manifest-transforms-return-value': ol`The return value from a + manifestTransform should be an object with 'manifest' and optionally + 'warnings' properties.`, + 'string-entry-warning': ol`Some items were passed to additionalManifestEntries + without revisioning info. This is generally NOT safe. Learn more at + https://bit.ly/wb-precache.`, + 'no-manifest-entries-or-runtime-caching': ol`Couldn't find configuration for + either precaching or runtime caching. Please ensure that the various glob + options are set to match one or more files, and/or configure the + runtimeCaching option.`, + 'cant-find-sourcemap': ol`The swSrc file refers to a sourcemap that can't be + opened:`, + 'nav-preload-runtime-caching': ol`When using navigationPreload, you must also + configure a runtimeCaching route that will use the preloaded response.`, + 'cache-name-required': ol`When using cache expiration, you must also + configure a custom cacheName.`, + 'manifest-transforms': ol`When using manifestTransforms, you must provide + an array of functions.`, + 'invalid-handler-string': ol`The handler name provided is not valid: `, +} diff --git a/packages/workbox-build/src/validation/generate-sw.ts b/packages/workbox-build/src/validation/generate-sw.ts index 9789c2f..6f4f4bd 100644 --- a/packages/workbox-build/src/validation/generate-sw.ts +++ b/packages/workbox-build/src/validation/generate-sw.ts @@ -1,4 +1,5 @@ import * as v from 'valibot' +import { errors } from './errors' // Reused schema const CacheQueryOptionsSchema = v.object({ @@ -7,8 +8,6 @@ const CacheQueryOptionsSchema = v.object({ ignoreVary: v.optional(v.boolean()), }) -export type GenerateSWOptionsSchemaType = v.InferInput - export const GenerateSWOptionsSchema = v.pipe( v.strictObject({ /** @@ -252,15 +251,31 @@ export const GenerateSWOptionsSchema = v.pipe( /** * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. */ - swDest: v.pipe(v.string(), v.endsWith('.js', 'swDest must end with .js')), + swDest: v.pipe( + v.string(errors['missing-sw-dest']), + v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + ), /** * The local directory you wish to match `globPatterns` against. The path is * relative to the current directory. */ globDirectory: v.optional(v.string()), }), - v.check( - input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), - 'globDirectory is required when runtimeCaching is not provided', + // This is the cross-field validation rule. + v.forward( + v.check( + input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), + errors['no-manifest-entries-or-runtime-caching'], + ), + // If the check fails, assign the error to the 'globDirectory' field. + ['globDirectory'], + ), + // This is the cross-field validation rule for navigationPreload. + v.forward( + v.check( + input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), + errors['nav-preload-runtime-caching'], + ), + ['navigationPreload'], ), ) diff --git a/packages/workbox-build/src/validation/get-manifest.ts b/packages/workbox-build/src/validation/get-manifest.ts index edade23..24a2646 100644 --- a/packages/workbox-build/src/validation/get-manifest.ts +++ b/packages/workbox-build/src/validation/get-manifest.ts @@ -7,81 +7,83 @@ const ManifestEntrySchema = v.strictObject({ url: v.string(), }) -export const GetManifestOptionsSchema = v.strictObject({ +export const GetManifestOptionsSchema = v.pipe( + v.strictObject({ /** * A list of entries to be precached, in addition to any entries that are * generated as part of the build configuration. */ - additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their - * URL, and exempted from the normal HTTP cache-busting that's done when - * populating the precache. While not required, it's recommended that if your - * existing build process already inserts a `[hash]` value into each filename, - * you provide a RegExp that will detect that, as it will reduce the bandwidth - * consumed when precaching. - */ - dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the - * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are - * also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optional(v.array(v.function())), - /** - * This value can be used to determine the maximum size of files that will be - * precached. This prevents you from inadvertently precaching very large files - * that might have accidentally matched one of your patterns. - * @default 2097152 (2MiB) - */ - maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be - * used to, e.g., remove or add a path prefix from a manifest entry if your - * web hosting setup doesn't match your local filesystem setup. As an - * alternative with more flexibility, you can use the `manifestTransforms` - * option and provide a function that modifies the entries in the manifest - * using whatever logic you provide. - */ - modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - /** - * Determines whether or not symlinks are followed when generating the - * precache manifest. For more information, see the definition of `follow` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globFollow: v.optional(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the - * precache manifest. For more information, see the definition of `ignore` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache - * manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest - * will cause the build to fail. If false, the problematic directory will be - * skipped. For more information, see the definition of `strict` in the `glob` - * [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optional(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may - * depend on multiple files or on some other unique string value. The keys in - * this object are server-rendered URLs. If the values are an array of - * strings, they will be interpreted as `glob` patterns, and the contents of - * any files matching the patterns will be used to uniquely version the URL. - * If used with a single string, it will be interpreted as unique versioning - * information that you've generated for a given URL. - */ - templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - /** - * The local directory you wish to match `globPatterns` against. The path is - * relative to the current directory. - */ - globDirectory: v.string(), -}) + additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optional(v.array(v.function())), + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2097152 (2MiB) + */ + maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optional(v.record(v.string(), v.string())), + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globFollow: v.optional(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optional(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.string(), + }), +) diff --git a/packages/workbox-build/src/validation/inject-manifest.ts b/packages/workbox-build/src/validation/inject-manifest.ts index a9190aa..71612f1 100644 --- a/packages/workbox-build/src/validation/inject-manifest.ts +++ b/packages/workbox-build/src/validation/inject-manifest.ts @@ -1,4 +1,5 @@ import * as v from 'valibot' +import { errors } from './errors' // ManifestEntry is used in additionalManifestEntries const ManifestEntrySchema = v.strictObject({ @@ -7,103 +8,108 @@ const ManifestEntrySchema = v.strictObject({ url: v.string(), }) -export const InjectManifestOptionsSchema = v.strictObject({ +export const InjectManifestOptionsSchema = v.pipe( + v.strictObject({ /** * A list of entries to be precached, in addition to any entries that are * generated as part of the build configuration. */ - additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their - * URL, and exempted from the normal HTTP cache-busting that's done when - * populating the precache. While not required, it's recommended that if your - * existing build process already inserts a `[hash]` value into each filename, - * you provide a RegExp that will detect that, as it will reduce the bandwidth - * consumed when precaching. - */ - dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the - * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are - * also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optional(v.array(v.function())), - /** - * This value can be used to determine the maximum size of files that will be - * precached. This prevents you from inadvertently precaching very large files - * that might have accidentally matched one of your patterns. - * @default 2097152 - */ - maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be - * used to, e.g., remove or add a path prefix from a manifest entry if your - * web hosting setup doesn't match your local filesystem setup. As an - * alternative with more flexibility, you can use the `manifestTransforms` - * option and provide a function that modifies the entries in the manifest - * using whatever logic you provide. - */ - modifyURLPrefix: v.optional(v.record(v.string(), v.string())), + additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optional(v.array(v.function())), + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2MiB (2097152 bytes) + */ + maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - /** - * Determines whether or not symlinks are followed when generating the - * precache manifest. For more information, see the definition of `follow` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - * @default true - */ - globFollow: v.optional(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the - * precache manifest. For more information, see the definition of `ignore` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache - * manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest - * will cause the build to fail. If false, the problematic directory will be - * skipped. For more information, see the definition of `strict` in the `glob` - * [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optional(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may - * depend on multiple files or on some other unique string value. The keys in - * this object are server-rendered URLs. If the values are an array of - * strings, they will be interpreted as `glob` patterns, and the contents of - * any files matching the patterns will be used to uniquely version the URL. - * If used with a single string, it will be interpreted as unique versioning - * information that you've generated for a given URL. - */ - templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + * @default true + */ + globFollow: v.optional(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optional(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - /** - * The string to find inside of the `swSrc` file. Once found, it will be - * replaced by the generated precache manifest. - * @default "self.__WB_MANIFEST" - */ - injectionPoint: v.optional(v.string(), 'self.__WB_MANIFEST'), - /** - * The path and filename of the service worker file that will be read during - * the build process, relative to the current working directory. - */ - swSrc: v.string(), + /** + * The string to find inside of the `swSrc` file. Once found, it will be + * replaced by the generated precache manifest. + * @default "self.__WB_MANIFEST" + */ + injectionPoint: v.optional(v.string(), 'self.__WB_MANIFEST'), + /** + * The path and filename of the service worker file that will be read during + * the build process, relative to the current working directory. + */ + swSrc: v.string(), - /** - * The path and filename of the service worker file that will be created by - * the build process, relative to the current working directory. It must end - * in '.js'. - */ - swDest: v.pipe(v.string(), v.endsWith('.js', 'swDest must end with .js')), + /** + * The path and filename of the service worker file that will be created by + * the build process, relative to the current working directory. It must end + * in '.js'. + */ + swDest: v.pipe( + v.string(errors['missing-sw-dest']), + v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + ), - /** - * The local directory you wish to match `globPatterns` against. The path is - * relative to the current directory. - */ - globDirectory: v.string(), -}) + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.string(), + }), +) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index ff59673..5ca94ec 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -25,8 +25,9 @@ function extractIssueMessage(issue: BaseIssue) { return issue.message // Priority 2: Missing required key. - if (issue.kind === 'schema' && issue.received === 'undefined') - return `The required option "${path}" is missing.` + if (issue.kind === 'schema' && issue.received === 'undefined') { + return `The option "${path}" is required.` + } // Priority 3: Unknown key in strict object. if (issue.type === 'strict_object') @@ -78,6 +79,44 @@ function extractIssueMessage(issue: BaseIssue) { return issue.message } +// This function intelligently sanitizes the options object from magicast. +// It creates a plain object but preserves special types like RegExp +// by checking the `$type` property provided by magicast. +function sanitizeMagicastOptions(options: any): any { + if (options === null || typeof options !== 'object') { + return options + } + + // If magicast has tagged the type, reconstruct it. + if (options.$type === 'RegExp' || options.$ast?.type === 'RegExpLiteral') { + return new RegExp(options.source, options.flags) + } + + // A proxified array is an object with `$type: 'array'` + if (options.$type === 'array') { + // We can't just use Array.from(options) because it's not a real iterable. + // We need to manually reconstruct the array from its numeric keys. + const sanitizedArray: any[] = [] + let i = 0 + while (i in options) { + sanitizedArray.push(sanitizeMagicastOptions(options[i])) + i++ + } + return sanitizedArray + } + + // For regular objects (or proxies of objects) + const sanitized: { [key: string]: any } = {} + for (const key in options) { + // Copy own properties, but ignore magicast's internal metadata. + if (Object.prototype.hasOwnProperty.call(options, key) && !key.startsWith('$')) { + sanitized[key] = sanitizeMagicastOptions(options[key]) + } + } + + return sanitized +} + /** * A wrapper around Valibot's `safeParse` that throws a user-friendly error * if validation fails. @@ -90,7 +129,7 @@ export function validate>( options: unknown, methodName: string, ): void { - const result = safeParse(schema, options) + const result = safeParse(schema, '$ast' in (options as any) ? sanitizeMagicastOptions(options) : options) if (!result.success) { const errorMessages = result.issues.map(extractIssueMessage) throw new Error( diff --git a/packages/workbox-build/src/validation/validation-options.ts b/packages/workbox-build/src/validation/validation-options.ts new file mode 100644 index 0000000..94e895c --- /dev/null +++ b/packages/workbox-build/src/validation/validation-options.ts @@ -0,0 +1,49 @@ +import type { GenerateSWOptions } from '../types' +import { errors } from './errors' + +const DEFAULT_EXCLUDE_VALUE = [/\.map$/, /^manifest.*\.js$/] + +export class WorkboxConfigError extends Error { + constructor(message?: string) { + super(message) + Object.setPrototypeOf(this, new.target.prototype) + } +} +export function ensureValidNavigationPreloadConfig( + options: GenerateSWOptions, +): void { + if ( + options.navigationPreload + && (!Array.isArray(options.runtimeCaching) + || options.runtimeCaching.length === 0) + ) { + throw new WorkboxConfigError(errors['nav-preload-runtime-caching']) + } +} + +export function ensureValidCacheExpiration( + options: GenerateSWOptions, +): void { + for (const runtimeCaching of options.runtimeCaching || []) { + if ( + runtimeCaching.options?.expiration + && !runtimeCaching.options?.cacheName + ) { + throw new WorkboxConfigError(errors['cache-name-required']) + } + } +} + +export function ensureValidRuntimeCachingOrGlobDirectory( + options: GenerateSWOptions, +): void { + if ( + !options.globDirectory + && (!Array.isArray(options.runtimeCaching) + || options.runtimeCaching.length === 0) + ) { + throw new WorkboxConfigError( + errors['no-manifest-entries-or-runtime-caching'], + ) + } +} diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index 4dd7a56..2da3feb 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -1,7 +1,7 @@ import type * as v from 'valibot' import type { GenerateSWOptions, GetManifestOptions, InjectManifestOptions } from '../src/types' import { describe, expect, expectTypeOf, it } from 'vitest' - +import { errors } from '../src/validation/errors' import { GenerateSWOptionsSchema } from '../src/validation/generate-sw' import { GetManifestOptionsSchema } from '../src/validation/get-manifest' import { InjectManifestOptionsSchema } from '../src/validation/inject-manifest' @@ -41,7 +41,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Partial expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- The required option "swDest" is missing.', + `generateSW() options validation failed: \n- The option "swDest" is required.`, ) }) @@ -90,7 +90,7 @@ describe('generateSWOptions Schema Validation', () => { runtimeCaching: [ { urlPattern: /.*/, - handler: 'NetworkFirst' as const, + handler: 'NetworkFirst', }, ], } satisfies GenerateSWOptions @@ -120,7 +120,7 @@ describe('generateSWOptions Schema Validation', () => { // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- swDest must end with .js', + `generateSW() options validation failed: \n- ${errors['invalid-sw-dest-js-ext']}`, ) }) @@ -161,7 +161,7 @@ describe('generateSWOptions Schema Validation', () => { } satisfies Omit & { runtimeCaching: { urlPattern: RegExp }[] } expectTypeOf().not.toMatchObjectType() expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( - 'generateSW() options validation failed: \n- The required option "runtimeCaching.0.handler" is missing.', + 'generateSW() options validation failed: \n- The option "runtimeCaching.0.handler" is required.', ) }) @@ -211,7 +211,7 @@ describe('getManifestOptions Schema Validation', () => { const options = {} satisfies Partial expectTypeOf().not.toMatchObjectType() expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( - 'getManifest() options validation failed: \n- The required option "globDirectory" is missing.', + 'getManifest() options validation failed: \n- The option "globDirectory" is required.', ) }) @@ -268,7 +268,7 @@ describe('injectManifestOptions Schema Validation', () => { } satisfies Partial expectTypeOf().not.toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( - 'injectManifest() options validation failed: \n- The required option "swSrc" is missing.', + 'injectManifest() options validation failed: \n- The option "swSrc" is required.', ) }) @@ -308,7 +308,7 @@ describe('injectManifestOptions Schema Validation', () => { // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).toThrow( - 'injectManifest() options validation failed: \n- swDest must end with .js', + `injectManifest() options validation failed: \n- ${errors['invalid-sw-dest-js-ext']}`, ) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ebe9221..606602d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ catalogs: pkg-types: specifier: ^2.3.0 version: 2.3.0 + proper-tags: + specifier: ^2.0.2 + version: 2.0.2 taze: specifier: ^19.7.0 version: 19.7.0 @@ -109,12 +112,15 @@ importers: tsdown: specifier: catalog:build version: 0.15.7(typescript@5.9.3) + tsx: + specifier: ^4.20.6 + version: 4.20.6 typescript: specifier: catalog:typescript version: 5.9.3 vitest: specifier: catalog:testing - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) packages/workbox-background-sync: dependencies: @@ -160,6 +166,9 @@ importers: pkg-types: specifier: catalog:utils version: 2.3.0 + proper-tags: + specifier: catalog:utils + version: 2.0.2 valibot: specifier: catalog:utils version: 1.1.0(typescript@5.9.3) @@ -655,8 +664,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-project/types@0.94.0': - resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==} + '@oxc-project/types@0.95.0': + resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} @@ -668,91 +677,91 @@ packages: '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} - '@rolldown/binding-android-arm64@1.0.0-beta.43': - resolution: {integrity: sha512-TP8bcPOb1s6UmY5syhXrDn9k0XkYcw+XaoylTN4cJxf0JOVS2j682I3aTcpfT51hOFGr2bRwNKN9RZ19XxeQbA==} + '@rolldown/binding-android-arm64@1.0.0-beta.44': + resolution: {integrity: sha512-g9ejDOehJFhxC1DIXQuZQ9bKv4lRDioOTL42cJjFjqKPl1L7DVb9QQQE1FxokGEIMr6FezLipxwnzOXWe7DNPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.43': - resolution: {integrity: sha512-kuVWnZsE4vEjMF/10SbSUyzucIW2zmdsqFghYMqy+fsjXnRHg0luTU6qWF8IqJf4Cbpm9NEZRnjIEPpAbdiSNQ==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.44': + resolution: {integrity: sha512-PxAW1PXLPmCzfhfKIS53kwpjLGTUdIfX4Ht+l9mj05C3lYCGaGowcNsYi2rdxWH24vSTmeK+ajDNRmmmrK0M7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.43': - resolution: {integrity: sha512-u9Ps4sh6lcmJ3vgLtyEg/x4jlhI64U0mM93Ew+tlfFdLDe7yKyA+Fe80cpr2n1mNCeZXrvTSbZluKpXQ0GxLjw==} + '@rolldown/binding-darwin-x64@1.0.0-beta.44': + resolution: {integrity: sha512-/CtQqs1oO9uSb5Ju60rZvsdjE7Pzn8EK2ISAdl2jedjMzeD/4neNyCbwyJOAPzU+GIQTZVyrFZJX+t7HXR1R/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.43': - resolution: {integrity: sha512-h9lUtVtXgfbk/tnicMpbFfZ3DJvk5Zn2IvmlC1/e0+nUfwoc/TFqpfrRRqcNBXk/e+xiWMSKv6b0MF8N+Rtvlg==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.44': + resolution: {integrity: sha512-V5Q5W9c4+2GJ4QabmjmVV6alY97zhC/MZBaLkDtHwGy3qwzbM4DYgXUbun/0a8AH5hGhuU27tUIlYz6ZBlvgOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.43': - resolution: {integrity: sha512-IX2C6bA6wM2rX/RvD75ko+ix9yxPKjKGGq7pOhB8wGI4Z4fqX5B1nDHga/qMDmAdCAR1m9ymzxkmqhm/AFYf7A==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': + resolution: {integrity: sha512-X6adjkHeFqKsTU0FXdNN9HY4LDozPqIfHcnXovE5RkYLWIjMWuc489mIZ6iyhrMbCqMUla9IOsh5dvXSGT9o9A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.43': - resolution: {integrity: sha512-mcjd57vEj+CEQbZAzUiaxNzNgwwgOpFtZBWcINm8DNscvkXl5b/s622Z1dqGNWSdrZmdjdC6LWMvu8iHM6v9sQ==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': + resolution: {integrity: sha512-kRRKGZI4DXWa6ANFr3dLA85aSVkwPdgXaRjfanwY84tfc3LncDiIjyWCb042e3ckPzYhHSZ3LmisO+cdOIYL6Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.43': - resolution: {integrity: sha512-Pa8QMwlkrztTo/1mVjZmPIQ44tCSci10TBqxzVBvXVA5CFh5EpiEi99fPSll2dHG2uT4dCOMeC6fIhyDdb0zXA==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': + resolution: {integrity: sha512-hMtiN9xX1NhxXBa2U3Up4XkVcsVp2h73yYtMDY59z9CDLEZLrik9RVLhBL5QtoX4zZKJ8HZKJtWuGYvtmkCbIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.43': - resolution: {integrity: sha512-BgynXKMjeaX4AfWLARhOKDetBOOghnSiVRjAHVvhiAaDXgdQN8e65mSmXRiVoVtD3cHXx/cfU8Gw0p0K+qYKVQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': + resolution: {integrity: sha512-rd1LzbpXQuR8MTG43JB9VyXDjG7ogSJbIkBpZEHJ8oMKzL6j47kQT5BpIXrg3b5UVygW9QCI2fpFdMocT5Kudg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.43': - resolution: {integrity: sha512-VIsoPlOB/tDSAw9CySckBYysoIBqLeps1/umNSYUD8pMtalJyzMTneAVI1HrUdf4ceFmQ5vARoLIXSsPwVFxNg==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': + resolution: {integrity: sha512-qI2IiPqmPRW25exXkuQr3TlweCDc05YvvbSDRPCuPsWkwb70dTiSoXn8iFxT4PWqTi71wWHg1Wyta9PlVhX5VA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.43': - resolution: {integrity: sha512-YDXTxVJG67PqTQMKyjVJSddoPbSWJ4yRz/E3xzTLHqNrTDGY0UuhG8EMr8zsYnfH/0cPFJ3wjQd/hJWHuR6nkA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': + resolution: {integrity: sha512-+vHvEc1pL5iJRFlldLC8mjm6P4Qciyfh2bh5ZI6yxDQKbYhCHRKNURaKz1mFcwxhVL5YMYsLyaqM3qizVif9MQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.43': - resolution: {integrity: sha512-3M+2DmorXvDuAIGYQ9Z93Oy1G9ETkejLwdXXb1uRTgKN9pMcu7N+KG2zDrJwqyxeeLIFE22AZGtSJm3PJbNu9Q==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': + resolution: {integrity: sha512-XSgLxRrtFj6RpTeMYmmQDAwHjKseYGKUn5LPiIdW4Cq+f5SBSStL2ToBDxkbdxKPEbCZptnLPQ/nfKcAxrC8Xg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.43': - resolution: {integrity: sha512-/B1j1pJs33y9ywtslOMxryUPHq8zIGu/OGEc2gyed0slimJ8fX2uR/SaJVhB4+NEgCFIeYDR4CX6jynAkeRuCA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': + resolution: {integrity: sha512-cF1LJdDIX02cJrFrX3wwQ6IzFM7I74BYeKFkzdcIA4QZ0+2WA7/NsKIgjvrunupepWb1Y6PFWdRlHSaz5AW1Wg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.43': - resolution: {integrity: sha512-29oG1swCz7hNP+CQYrsM4EtylsKwuYzM8ljqbqC5TsQwmKat7P8ouDpImsqg/GZxFSXcPP9ezQm0Q0wQwGM3JA==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': + resolution: {integrity: sha512-5uaJonDafhHiMn+iEh7qUp3QQ4Gihv3lEOxKfN8Vwadpy0e+5o28DWI42DpJ9YBYMrVy4JOWJ/3etB/sptpUwA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.43': - resolution: {integrity: sha512-eWBV1Ef3gfGNehxVGCyXs7wLayRIgCmyItuCZwYYXW5bsk4EvR4n2GP5m3ohjnx7wdiY3nLmwQfH2Knb5gbNZw==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': + resolution: {integrity: sha512-vsqhWAFJkkmgfBN/lkLCWTXF1PuPhMjfnAyru48KvF7mVh2+K7WkKYHezF3Fjz4X/mPScOcIv+g6cf6wnI6eWg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.43': - resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==} + '@rolldown/pluginutils@1.0.0-beta.44': + resolution: {integrity: sha512-g6eW7Zwnr2c5RADIoqziHoVs6b3W5QTQ4+qbpfjbkMJ9x+8Og211VW/oot2dj9dVwaK/UyC6Yo+02gV+wWQVNg==} '@rollup/rollup-android-arm-eabi@4.52.5': resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} @@ -2103,6 +2112,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + proper-tags@2.0.2: + resolution: {integrity: sha512-rpgQb3JC9uWAogm9J3G6heeg7n/Gq82GZKaFt080JPVbydKvGW6ECj9Ui1a6taTFv9Oa+dq9GMhHeOJyxxaURA==} + engines: {node: '>=12.0.0'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2174,8 +2187,8 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-beta.43: - resolution: {integrity: sha512-6RcqyRx0tY1MlRLnjXPp/849Rl/CPFhzpGGwNPEPjKwqBMqPq/Rbbkxasa8s0x+IkUk46ty4jazb5skZ/Vgdhw==} + rolldown@1.0.0-beta.44: + resolution: {integrity: sha512-gcqgyCi3g93Fhr49PKvymE8PoaGS0sf6ajQrsYaQ8o5de6aUEbD6rJZiJbhOfpcqOnycgsAsUNPYri1h25NgsQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2364,6 +2377,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2927,7 +2945,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@oxc-project/types@0.94.0': {} + '@oxc-project/types@0.95.0': {} '@pkgr/core@0.2.9': {} @@ -2937,51 +2955,51 @@ snapshots: dependencies: quansync: 0.2.11 - '@rolldown/binding-android-arm64@1.0.0-beta.43': + '@rolldown/binding-android-arm64@1.0.0-beta.44': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.43': + '@rolldown/binding-darwin-arm64@1.0.0-beta.44': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.43': + '@rolldown/binding-darwin-x64@1.0.0-beta.44': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.43': + '@rolldown/binding-freebsd-x64@1.0.0-beta.44': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.43': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.43': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.43': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.43': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.43': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.43': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.43': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.43': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.43': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.43': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': optional: true - '@rolldown/pluginutils@1.0.0-beta.43': {} + '@rolldown/pluginutils@1.0.0-beta.44': {} '@rollup/rollup-android-arm-eabi@4.52.5': optional: true @@ -3194,7 +3212,7 @@ snapshots: eslint: 9.37.0(jiti@2.6.1) optionalDependencies: typescript: 5.9.3 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -3206,13 +3224,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -3243,7 +3261,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) '@vitest/utils@3.2.4': dependencies: @@ -4556,6 +4574,8 @@ snapshots: prelude-ls@1.2.1: {} + proper-tags@2.0.2: {} + punycode@2.3.1: {} quansync@0.2.11: {} @@ -4597,7 +4617,7 @@ snapshots: reusify@1.1.0: {} - rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.43)(typescript@5.9.3): + rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.44)(typescript@5.9.3): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -4608,33 +4628,32 @@ snapshots: dts-resolver: 2.1.2 get-tsconfig: 4.12.0 magic-string: 0.30.19 - rolldown: 1.0.0-beta.43 + rolldown: 1.0.0-beta.44 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - oxc-resolver - supports-color - rolldown@1.0.0-beta.43: + rolldown@1.0.0-beta.44: dependencies: - '@oxc-project/types': 0.94.0 - '@rolldown/pluginutils': 1.0.0-beta.43 - ansis: 4.2.0 + '@oxc-project/types': 0.95.0 + '@rolldown/pluginutils': 1.0.0-beta.44 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.43 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.43 - '@rolldown/binding-darwin-x64': 1.0.0-beta.43 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.43 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.43 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.43 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.43 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.43 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.43 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.43 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.43 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.43 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.43 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.43 + '@rolldown/binding-android-arm64': 1.0.0-beta.44 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.44 + '@rolldown/binding-darwin-x64': 1.0.0-beta.44 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.44 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.44 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.44 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.44 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.44 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.44 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.44 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.44 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.44 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.44 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.44 rollup@4.52.5: dependencies: @@ -4815,8 +4834,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.43 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.43)(typescript@5.9.3) + rolldown: 1.0.0-beta.44 + rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.44)(typescript@5.9.3) semver: 7.7.3 tinyexec: 1.0.1 tinyglobby: 0.2.15 @@ -4834,6 +4853,13 @@ snapshots: tslib@2.8.1: optional: true + tsx@4.20.6: + dependencies: + esbuild: 0.25.11 + get-tsconfig: 4.12.0 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -4894,13 +4920,13 @@ snapshots: validate-npm-package-name@5.0.1: {} - vite-node@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -4915,7 +4941,7 @@ snapshots: - tsx - yaml - vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1): + vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -4927,13 +4953,14 @@ snapshots: '@types/node': 22.18.10 fsevents: 2.3.3 jiti: 2.6.1 + tsx: 4.20.6 yaml: 2.8.1 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.10(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -4951,8 +4978,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(yaml@2.8.1) + vite: 7.1.10(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0e73bad..1ea9dd6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,6 +27,7 @@ catalogs: magicast: ^0.3.5 pathe: ^2.0.3 pkg-types: ^2.3.0 + proper-tags: ^2.0.2 taze: ^19.7.0 valibot: ^1.1.0 From 1191feaa37151d7d31f8db0ef334885f298e5ac6 Mon Sep 17 00:00:00 2001 From: userquin Date: Fri, 24 Oct 2025 17:54:06 +0200 Subject: [PATCH 13/20] chore: add async valibot schemas chore: include magicast from https://github.com/unjs/magicast/pull/136 --- packages/workbox-build/magicast-0.3.5.tgz | Bin 0 -> 125714 bytes packages/workbox-build/package.json | 6 +- packages/workbox-build/src/generate-sw.ts | 464 +++++++++++++++++- .../src/validation/async-generate-sw.ts | 170 +++++++ .../src/validation/async-get-manifest.ts | 204 ++++++++ .../src/validation/async-inject-manifest.ts | 109 ++++ .../src/validation/generate-sw.ts | 340 +++++++++---- .../workbox-build/src/validation/utils.ts | 200 ++++++++ .../src/validation/validation-helper.ts | 90 +++- .../workbox-build/test/validation.test.ts | 2 +- pnpm-lock.yaml | 65 ++- pnpm-workspace.yaml | 4 +- 12 files changed, 1514 insertions(+), 140 deletions(-) create mode 100644 packages/workbox-build/magicast-0.3.5.tgz create mode 100644 packages/workbox-build/src/validation/async-generate-sw.ts create mode 100644 packages/workbox-build/src/validation/async-get-manifest.ts create mode 100644 packages/workbox-build/src/validation/async-inject-manifest.ts create mode 100644 packages/workbox-build/src/validation/utils.ts diff --git a/packages/workbox-build/magicast-0.3.5.tgz b/packages/workbox-build/magicast-0.3.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..245c69aff8f5214f3aa59a9005171fc5ff1047ef GIT binary patch literal 125714 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ǡu;c{RRgt(9=DK0qU@eZ$;2 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

$BL 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 Cz?(Y& literal 0 HcmV?d00001 diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index b78dfbc..c63f075 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -51,7 +51,8 @@ "scripts": { "build": "tsdown", "prepublishOnly": "pnpm run build", - "node:test": "node ./dist/generate-sw.js" + "node:test": "node ./dist/generate-sw.js", + "tsx:test": "tsx ./src/generate-sw.ts" }, "dependencies": { "@vite-pwa/workbox-background-sync": "workspace:*", @@ -60,10 +61,11 @@ "@vite-pwa/workbox-core": "workspace:*", "@vite-pwa/workbox-expiration": "workspace:*", "@vite-pwa/workbox-routing": "workspace:*", - "magicast": "catalog:utils", + "magicast": "file:magicast-0.3.5.tgz", "pathe": "catalog:utils", "pkg-types": "catalog:utils", "proper-tags": "catalog:utils", + "recast": "catalog:utils", "valibot": "catalog:utils" } } diff --git a/packages/workbox-build/src/generate-sw.ts b/packages/workbox-build/src/generate-sw.ts index fb29138..bf2ca50 100644 --- a/packages/workbox-build/src/generate-sw.ts +++ b/packages/workbox-build/src/generate-sw.ts @@ -1,34 +1,472 @@ +import type { ArrayExpression, Identifier, ObjectExpression, Program } from '@babel/types' import type { ProxifiedModule, ProxifiedObject } from 'magicast' import type { GenerateSWOptions } from './types' -import { generateCode, parseModule } from 'magicast' -import { getDefaultExportOptions } from 'magicast/helpers' -import { GenerateSWOptionsSchema } from './validation/generate-sw' -import { validate } from './validation/validation-helper' +import { AsyncGenerateSWOptionsSchema } from '@vite-pwa/workbox-build/validation/async-generate-sw' +import { builders, generateCode, parseModule } from 'magicast' +import { deepMergeObject, getDefaultExportOptions } from 'magicast/helpers' +import { validateAsync } from './validation/validation-helper' + +// Helper para capitalizar: NetworkFirst -> NetworkFirst +function capitalize(s: string, sanitize = false) { + const value = s.charAt(0).toUpperCase() + s.slice(1) + return sanitize ? value.replace(/['"]/g, '') : value +} export async function generateSW( options: ProxifiedModule, ): Promise { - const config = getDefaultExportOptions(options) as ProxifiedObject - console.log(config.swDest) - console.log(generateCode(config).code) - validate( + const proxifiedOptions = getDefaultExportOptions(options) as ProxifiedObject + /* const optionsWithDefaults = validate( GenerateSWOptionsSchema, - config, + proxifiedOptions, + 'generateSW', + ) */ + const optionsWithDefaults = await validateAsync( + AsyncGenerateSWOptionsSchema, + proxifiedOptions, 'generateSW', ) - // ensureValidNavigationPreloadConfig(config) - // ensureValidCacheExpiration(config) - // ensureValidRuntimeCachingOrGlobDirectory(config) + // inject defaults added by valibot + deepMergeObject(proxifiedOptions, optionsWithDefaults) + console.log(generateCode(proxifiedOptions).code) + // if (true) { + // return + // } + + const swModule = parseModule('') + + const strategyImports = new Set() + if (proxifiedOptions.runtimeCaching) { + for (const entry of proxifiedOptions.runtimeCaching) { + if (typeof entry.handler === 'string') { + strategyImports.add(capitalize(entry.handler)) + } + } + } + + if (proxifiedOptions.skipWaiting) { + swModule.imports.$append({ from: '@vite-pwa/workbox-core', imported: 'skipWaiting' }) + } + if (proxifiedOptions.clientsClaim) { + swModule.imports.$append({ from: '@vite-pwa/workbox-core', imported: 'clientsClaim' }) + } + if (proxifiedOptions.runtimeCaching?.length) { + swModule.imports.$append({ from: '@vite-pwa/workbox-routing', imported: 'registerRoute' }) + if (strategyImports.size > 0) { + for (const strategyImport of strategyImports) { + swModule.imports.$append({ + from: '@vite-pwa/workbox-strategies', + imported: strategyImport, + }) + } + } + } + + // --- CONSTRUCCIÓN DEL CÓDIGO --- + const swCode: string[] = [] + + if (proxifiedOptions.skipWaiting) { + swCode.push('skipWaiting();') + } + if (proxifiedOptions.clientsClaim) { + swCode.push('clientsClaim();') + } + + if (proxifiedOptions.runtimeCaching) { + swCode.push(...getRuntimeCachingEntries(proxifiedOptions, strategyImports)) + } + + const importsCode = generateCode(swModule.imports).code + const finalCode = `${importsCode}\n\n${swCode.join('\n')}` + + console.log('--- GENERATED SERVICE WORKER ---') + console.log(finalCode) +} + +function getRuntimeCachingEntries( + proxifiedOptions: ProxifiedObject, + strategyImports: Set, +): string[] { + const entries: string[] = [] + if (!proxifiedOptions.runtimeCaching) { + return entries + } + + for (const entry of proxifiedOptions.runtimeCaching) { + let handlerNode: any + + if (typeof entry.handler === 'string') { + const strategyName = capitalize(entry.handler, true) + strategyImports.add(capitalize(strategyName, true)) + // const callee = builders.identifier(strategyName) + const args = entry.options?.$ast ? [entry.options] : undefined + handlerNode = args && args.length > 0 + ? builders.newExpression(strategyName, ...args) + : builders.newExpression(strategyName) + } + else { + handlerNode = entry.handler + } + + const routeCallNode = builders.functionCall( + 'registerRoute', + entry.urlPattern, + handlerNode, + ) + + entries.push(generateCode(routeCallNode).code) + } + + return entries +} + +function __getRuntimeCachingEntries( + proxifiedOptions: ProxifiedObject, + strategyImports: Set, +): string[] { + const entries: string[] = [] + if (!proxifiedOptions.runtimeCaching) { + return entries + } + + // ¡Ahora podemos iterar directamente sobre el proxy! + for (const entry of proxifiedOptions.runtimeCaching) { + // 1. Generar el código para urlPattern de forma segura + let patternCode: string + if (typeof entry.urlPattern === 'string') { + // Es un string, lo convertimos a un literal de string + patternCode = JSON.stringify(entry.urlPattern) + } + else if (entry.urlPattern instanceof RegExp) { + // Es un RegExp nativo, usamos .toString() + patternCode = entry.urlPattern.toString() + } + else { + // Es un Proxy de función, usamos generateCode + patternCode = generateCode(entry.urlPattern).code + } + + // 2. Generar el código para handler + let handlerCode: string + if (typeof entry.handler === 'string') { + strategyImports.add(capitalize(entry.handler)) + const optionsCode = entry.options ? generateCode(entry.options).code : '' + // Usamos generateCode en el string para obtener el literal correcto (e.g. 'CacheFirst') + // y luego lo sanitizamos para quitar las comillas. + handlerCode = `new ${capitalize(entry.handler, true)}(${optionsCode})` + } + else { + handlerCode = generateCode(entry.handler).code + } + + // 3. Sanitizar el código generado para las funciones (eliminar paréntesis/punto y coma extra) + const sanitize = (code: string) => { + if (code.startsWith('(') && code.endsWith(');')) { + return code.slice(1, -2) // (function() {}); => function() {} + } + if (code.endsWith(';')) { + return code.slice(0, -1) // () => {}; => () => {} + } + return code + } + + const registerRoute = builders.functionCall('registerRoute', entry.urlPattern, entry.handler) + console.log(generateCode(registerRoute).code) + + // 4. Construir la llamada final + entries.push(`registerRoute(${sanitize(patternCode)}, ${sanitize(handlerCode)});`) + } + + return entries +} + +function _getRuntimeCachingEntries( + proxifiedOptions: ProxifiedObject, + strategyImports: Set, +): string[] { + if (!proxifiedOptions.runtimeCaching) + return [] + + // Tu genialidad: aislar el array en un módulo temporal para obtener un AST "limpio". + const runtimeCachingModule = parseModule(`const runtimeCaching = ${generateCode(proxifiedOptions.runtimeCaching).code};\nexport default runtimeCaching;`) + const runtimeCachingDeclarationId = runtimeCachingModule.exports.default!.$name + + const entries: string[] = [] + + for (const node of (runtimeCachingModule.$ast as Program).body) { + if (node.type === 'VariableDeclaration') { + for (const declaration of node.declarations) { + if ( + declaration.id.type === 'Identifier' + && declaration.id.name === runtimeCachingDeclarationId + && declaration.init + ) { + const handlers = declaration.init as ArrayExpression + for (const rCaching of handlers.elements) { + if (!rCaching) + continue + + const data = rCaching as ObjectExpression + let patternCode: string | undefined + let handlerCode: string | undefined + + for (const prop of data.properties) { + if (prop.type === 'ObjectMethod') { + const id = prop.key as Identifier + // El replace para convertir `method() {}` en `function () {}` + const code = generateCode(prop).code.replace(new RegExp(`^${id.name}`), 'function ') + if (id.name === 'handler') { + handlerCode = code + } + else if (id.name === 'urlPattern') { + patternCode = code + } + } + else if (prop.type === 'ObjectProperty') { + const id = prop.key as Identifier + const value = prop.value + if (id.name === 'handler') { + if (value.type === 'StringLiteral') { + strategyImports.add(value.value) + // La sanitización correcta para `new Strategy()` + handlerCode = `new ${capitalize(generateCode(value).code, true)}()` + } + else { + // Para funciones y objetos, simplemente generamos el código + handlerCode = generateCode(value).code + } + } + else if (id.name === 'urlPattern') { + patternCode = generateCode(value).code + } + } + } + if (patternCode && handlerCode) { + if (patternCode.startsWith('(') && patternCode.endsWith(');')) { + patternCode = patternCode.slice(1, -2) // Elimina ( y ); + } + else if (patternCode.endsWith(';')) { + patternCode = patternCode.slice(0, -1) // Elimina ; + } + + if (handlerCode.startsWith('(') && handlerCode.endsWith(');')) { + handlerCode = handlerCode.slice(1, -2) // Elimina ( y ); + } + else if (handlerCode.endsWith(';')) { + handlerCode = handlerCode.slice(0, -1) // Elimina ; + } + + entries.push(`registerRoute(${patternCode}, ${handlerCode});`) + } + } + } + } + } + } + + return entries +} + +function getRuntimeCachingEntries2( + proxifiedOptions: ProxifiedObject, + strategyImports: Set, +): string[] { + if (!proxifiedOptions.runtimeCaching) + return [] + + const runtimeCachingModule = parseModule(`const runtimeCaching = ${generateCode(proxifiedOptions.runtimeCaching).code};\nexport default runtimeCaching;`) + const runtimeCachingDeclarationId = runtimeCachingModule.exports.default!.$name + + const entries: string[] = [] + + for (const node of (runtimeCachingModule.$ast as Program).body) { + if (node.type === 'VariableDeclaration') { + for (const declaration of node.declarations) { + if ( + declaration.id.type === 'Identifier' + && declaration.id.name === runtimeCachingDeclarationId + && declaration.init + ) { + const handlers = declaration.init as ArrayExpression + for (const rCaching of handlers.elements) { + if (!rCaching) + continue + + const data = rCaching as ObjectExpression + let patternCode: string | undefined + let handlerCode: string | undefined + + for (const prop of data.properties) { + if (prop.type === 'ObjectMethod') { + const id = prop.key as Identifier + if (id.name === 'handler') { + handlerCode = generateCode(prop).code.replace(new RegExp(`^${id.name}`), 'function ') + } + else if (id.name === 'urlPattern') { + patternCode = generateCode(prop).code.replace(new RegExp(`^${id.name}`), 'function ') + } + } + else if (prop.type === 'ObjectProperty') { + const id = prop.key as Identifier + const value = prop.value + if (id.name === 'handler') { + if (value.type === 'StringLiteral') { + strategyImports.add(value.value) + handlerCode = `new ${capitalize(value.value, true)}()` + } + else { + handlerCode = generateCode(value).code + } + } + else if (id.name === 'urlPattern') { + patternCode = generateCode(value).code + } + } + } + if (patternCode && handlerCode) { + entries.push(`registerRoute(${patternCode}, ${handlerCode});`) + } + } + } + } + } + } + + return entries +} + +function getRuntimeCachingEntriesOld( + proxifiedOptions: ProxifiedObject, + strategyImports: Set, +): string[] { + const runtimeCaching = parseModule(`const runtimeCaching = ${generateCode(proxifiedOptions.runtimeCaching!).code}; +export default runtimeCaching; +`) + + const runtimeCachingDeclarationId = runtimeCaching.exports.default.$name + + const entries: string[] = [] + + for (const node of (runtimeCaching.$ast as Program).body) { + if (node.type === 'VariableDeclaration') { + for (const declaration of node.declarations) { + if ( + declaration.id.type === 'Identifier' + && declaration.id.name === runtimeCachingDeclarationId + && declaration.init + ) { + /* const init + = declaration.init.type === 'TSSatisfiesExpression' + ? declaration.init.expression + : declaration.init */ + + const handlers = declaration.init as ArrayExpression + + for (const rCaching of handlers.elements) { + if (!rCaching) { + continue + } + const data = rCaching as ObjectExpression + console.log(rCaching?.type) + console.log(data.properties) + let patternCode: string | undefined + let handlerCode: string | undefined + for (const prop of data.properties) { + if (prop.type === 'ObjectMethod') { + const id = prop.key as Identifier + if (id.name === 'handler') { + handlerCode = generateCode(prop).code + } + else if (id.name === 'urlPattern') { + patternCode = generateCode(prop).code + } + } + else if (prop.type === 'ObjectProperty') { + const id = prop.key as Identifier + const value = prop.value + if (id.name === 'handler') { + // can be an arrow function or a handler we only check for strategy name here + // todo: handle options arggg + if (value.type !== 'StringLiteral') { + handlerCode = `new ${capitalize(generateCode(value).code, true)}()` + } + else { + handlerCode = generateCode(value).code + } + } + else if (id.name === 'urlPattern') { + patternCode = generateCode(value).code + } + } + } + if (patternCode && handlerCode) { + entries.push(`registerRoute(${patternCode}, ${handlerCode});`) + } + } + } + } + } + } + + return entries } async function main() { const module = parseModule(`export default { swDest: 'sw.js', + skipWaiting: true, + clientsClaim: true, runtimeCaching: [ { - urlPattern: /.*!/, + urlPattern: /^\\/api\\//, handler: 'NetworkFirst', }, + { + urlPattern: ({ request }) => request.destination === 'image', + handler: 'CacheFirst', + options: { + cacheName: 'google-fonts', + expiration: { + maxEntries: 4, + maxAgeSeconds: 365 * 24 * 60 * 60, // 365 days + }, + }, + }, + { + urlPattern({ request }) { return request.destination === 'image' }, + handler: 'NetworkFirst', + }, + { + urlPattern: '/images/', + handler: 'CacheFirst', + }, + { + urlPattern() { return false }, + handler: 'CacheFirst', + }, + { + urlPattern: function () { return false }, + handler: 'CacheFirst', + }, + { + urlPattern: function () { return false }, + handler() { return Response.error() }, + }, + { + urlPattern: function () { return false }, + handler: { + handle: () => Response.error() + } + }, + { + // method: 'PEPE', + urlPattern: function () { return false }, + handler: { + handle() { + return Response.error() + } + } + } ], } `) diff --git a/packages/workbox-build/src/validation/async-generate-sw.ts b/packages/workbox-build/src/validation/async-generate-sw.ts new file mode 100644 index 0000000..fddf1e0 --- /dev/null +++ b/packages/workbox-build/src/validation/async-generate-sw.ts @@ -0,0 +1,170 @@ +import * as v from 'valibot' +import { errors } from './errors' +import { AsyncNumericExpressionSchema, AsyncRuntimeCachingEntrySchema } from './utils' + +export const AsyncGenerateSWOptionsSchema = v.pipeAsync( + v.strictObjectAsync({ + /** + * A list of entries to be precached, in addition to any entries that are generated as part of the build configuration. + */ + additionalManifestEntries: v.optionalAsync(v.arrayAsync(v.unionAsync([ + v.strictObjectAsync({ + integrity: v.optionalAsync(v.string()), + revision: v.nullable(v.string()), + url: v.string(), + }), + v.string(), + ]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their URL, and exempted from the normal HTTP cache-busting that's done when populating the precache. While not required, it's recommended that if your existing build process already inserts a `[hash]` value into each filename, you provide a RegExp that will detect that, as it will reduce the bandwidth consumed when precaching. + */ + dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), + /** + * This value can be used to determine the maximum size of files that will be precached. This prevents you from inadvertently precaching very large files that might have accidentally matched one of your patterns. + */ + maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be used to, e.g., remove or add a path prefix from a manifest entry if your web hosting setup doesn't match your local filesystem setup. As an alternative with more flexibility, you can use the `manifestTransforms` option and provide a function that modifies the entries in the manifest using whatever logic you provide. + */ + modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), + /** + * Determines whether or not symlinks are followed when generating the precache manifest. For more information, see the definition of `follow` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globFollow: v.optionalAsync(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the precache manifest. For more information, see the definition of `ignore` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest will cause the build to fail. If false, the problematic directory will be skipped. For more information, see the definition of `strict` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optionalAsync(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may depend on multiple files or on some other unique string value. The keys in this object are server-rendered URLs. If the values are an array of strings, they will be interpreted as `glob` patterns, and the contents of any files matching the patterns will be used to uniquely version the URL. If used with a single string, it will be interpreted as unique versioning information that you've generated for a given URL. + */ + templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), + /** + * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. + */ + babelPresetEnvTargets: v.optionalAsync(v.arrayAsync(v.string()), ['chrome >= 56']), + /** + * An optional ID to be prepended to cache names. This is primarily useful for local development where multiple sites may be served from the same `http://localhost:port` origin. + */ + cacheId: v.optionalAsync(v.nullable(v.string())), + /** + * Whether or not Workbox should attempt to identify and delete any precaches created by older, incompatible versions. + */ + cleanupOutdatedCaches: v.optionalAsync(v.boolean(), false), + /** + * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) any existing clients as soon as it activates. + */ + clientsClaim: v.optionalAsync(v.boolean(), false), + /** + * If a navigation request for a URL ending in `/` fails to match a precached URL, this value will be appended to the URL and that will be checked for a precache match. This should be set to what your web server is using for its directory index. + */ + directoryIndex: v.optionalAsync(v.nullable(v.string())), + /** + * Whether to disable logging of warnings and errors. + */ + disableDevLogs: v.optionalAsync(v.boolean(), false), + /** + * Any search parameter names that match against one of the RegExp in this array will be removed before looking for a precache match. This is useful if your users might request URLs that contain, for example, URL parameters used to track the source of the traffic. If not provided, the default value is `[/^utm_/, /^fbclid$/]`. + */ + ignoreURLParametersMatching: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), + /** + * A list of JavaScript files that should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) inside the generated service worker file. This is useful when you want to let Workbox create your top-level service worker file, but want to include some additional code, such as a push event listener. + */ + importScripts: v.optionalAsync(v.arrayAsync(v.string())), + /** + * Whether the runtime code for the Workbox library should be included in the top-level service worker, or split into a separate file that needs to be deployed alongside the service worker. Keeping the runtime separate means that users will not have to re-download the Workbox code each time your top-level service worker changes. + */ + inlineWorkboxRuntime: v.optionalAsync(v.boolean(), false), + /** + * If set to 'production', then an optimized service worker bundle that excludes debugging info will be produced. If not explicitly configured here, the `process.env.NODE_ENV` value will be used, and failing that, it will fall back to `'production'`. + */ + mode: v.optionalAsync(v.nullable(v.string()), 'production'), + /** + * If specified, all [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). + */ + navigateFallback: v.optionalAsync(v.nullable(v.string()), null), + /** + * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. + */ + navigateFallbackAllowlist: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), + /** + * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. + */ + navigateFallbackDenylist: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), + /** + * Whether or not to enable [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) in the generated service worker. When set to true, you must also use `runtimeCaching` to set up an appropriate response strategy that will match navigation requests, and make use of the preloaded response. + */ + navigationPreload: v.optionalAsync(v.boolean(), false), + /** + * When using Workbox's build tools to generate your service worker, you can specify one or more runtime caching configurations. These are then translated to {@link workbox-routing.registerRoute} calls using the match and handler configuration you define. + */ + runtimeCaching: v.optionalAsync(v.arrayAsync(AsyncRuntimeCachingEntrySchema)), + /** + * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) to the generated service worker. If `false`, then a `message` listener will be added instead, allowing client pages to trigger `skipWaiting()` by calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. + */ + skipWaiting: v.optionalAsync(v.boolean(), false), + /** + * Whether to create a sourcemap for the generated service worker files. + */ + sourcemap: v.optionalAsync(v.boolean(), true), + /** + * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. + */ + swDest: v.pipeAsync( + v.string(errors['invalid-sw-dest']), + v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + ), + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.optionalAsync(v.string()), + }), + v.forwardAsync( + v.checkAsync( + async input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), + errors['no-manifest-entries-or-runtime-caching'], + ), + ['globDirectory'], + ), + v.forwardAsync( + v.checkAsync( + async input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), + errors['nav-preload-runtime-caching'], + ), + ['navigationPreload'], + ), + // This is the cross-field validation for cacheName when expiration is present. + v.forwardAsync( + v.checkAsync( + async (options) => { + const runtimeCaching = options.runtimeCaching + if (!runtimeCaching) { + return true + } + for (const runtime of runtimeCaching) { + if (runtime.options?.expiration && !runtime.options?.cacheName) { + return false + } + } + + return true + }, + errors['cache-name-required'], + ), + ['runtimeCaching'], + ), +) diff --git a/packages/workbox-build/src/validation/async-get-manifest.ts b/packages/workbox-build/src/validation/async-get-manifest.ts new file mode 100644 index 0000000..1408bc0 --- /dev/null +++ b/packages/workbox-build/src/validation/async-get-manifest.ts @@ -0,0 +1,204 @@ +import { errors } from '@vite-pwa/workbox-build/validation/errors' +import * as v from 'valibot' +import { AsyncManifestEntrySchema, AsyncRuntimeCachingEntrySchema } from './utils' + +export const AsyncGetManifestOptionsSchema = v.pipeAsync( + v.strictObjectAsync({ + /** + * A list of entries to be precached, in addition to any entries that are + * generated as part of the build configuration. + */ + additionalManifestEntries: v.optionalAsync(v.arrayAsync(v.unionAsync([AsyncManifestEntrySchema, v.string()]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2097152 (2MiB) + */ + maximumFileSizeToCacheInBytes: v.optionalAsync(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globFollow: v.optionalAsync(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optionalAsync(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), + /** + * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. + */ + babelPresetEnvTargets: v.optionalAsync(v.arrayAsync(v.string()), ['chrome >= 56']), + /** + * An optional ID to be prepended to cache names. This is primarily useful for local development where multiple sites may be served from the same `http://localhost:port` origin. + */ + cacheId: v.optionalAsync(v.nullable(v.string())), + /** + * Whether or not Workbox should attempt to identify and delete any precaches created by older, incompatible versions. + */ + cleanupOutdatedCaches: v.optionalAsync(v.boolean(), false), + /** + * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) any existing clients as soon as it activates. + */ + clientsClaim: v.optionalAsync(v.boolean(), false), + /** + * If a navigation request for a URL ending in `/` fails to match a precached URL, this value will be appended to the URL and that will be checked for a precache match. This should be set to what your web server is using for its directory index. + */ + directoryIndex: v.optionalAsync(v.nullable(v.string())), + /** + * Whether to disable logging of warnings and errors. + */ + disableDevLogs: v.optionalAsync(v.boolean(), false), + /** + * Any search parameter names that match against one of the RegExp in this array will be removed before looking for a precache match. This is useful if your users might request URLs that contain, for example, URL parameters used to track the source of the traffic. If not provided, the default value is `[/^utm_/, /^fbclid$/]`. + */ + ignoreURLParametersMatching: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), + /** + * A list of JavaScript files that should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) inside the generated service worker file. This is useful when you want to let Workbox create your top-level service worker file, but want to include some additional code, such as a push event listener. + */ + importScripts: v.optionalAsync(v.arrayAsync(v.string())), + /** + * Whether the runtime code for the Workbox library should be included in the top-level service worker, or split into a separate file that needs to be deployed alongside the service worker. Keeping the runtime separate means that users will not have to re-download the Workbox code each time your top-level service worker changes. + */ + inlineWorkboxRuntime: v.optionalAsync(v.boolean(), false), + /** + * If set to 'production', then an optimized service worker bundle that excludes debugging info will be produced. If not explicitly configured here, the `process.env.NODE_ENV` value will be used, and failing that, it will fall back to `'production'`. + */ + mode: v.optionalAsync(v.nullable(v.string()), 'production'), + /** + * If specified, all [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). + */ + navigateFallback: v.optionalAsync(v.nullable(v.string()), null), + /** + * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. + */ + navigateFallbackAllowlist: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), + /** + * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. + */ + navigateFallbackDenylist: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), + /** + * Whether or not to enable [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) in the generated service worker. When set to true, you must also use `runtimeCaching` to set up an appropriate response strategy that will match navigation requests, and make use of the preloaded response. + */ + navigationPreload: v.optionalAsync(v.boolean(), false), + /** + * Controls whether or not to include support for [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics). When `true`, the call to `workbox-google-analytics`'s `initialize()` will be added to your generated service worker. When set to an `Object`, that object will be passed in to the `initialize()` call, allowing you to customize the behavior. + */ + /* offlineGoogleAnalytics: v.optionalAsync(v.unionAsync([ + v.objectAsync({ // Inlined GoogleAnalyticsInitializeOptionsSchema + cacheName: v.optionalAsync(v.string()), + parameterOverrides: v.optionalAsync(v.recordAsync(v.string(), v.string())), + hitFilter: v.optionalAsync(v.function()), + }), + v.boolean(), + ]), false), */ + /** + * When using Workbox's build tools to generate your service worker, you can specify one or more runtime caching configurations. These are then translated to {@link workbox-routing.registerRoute} calls using the match and handler configuration you define. + */ + runtimeCaching: v.optionalAsync(v.arrayAsync(AsyncRuntimeCachingEntrySchema)), + /** + * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) to the generated service worker. If `false`, then a `message` listener will be added instead, allowing client pages to trigger `skipWaiting()` by calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. + */ + skipWaiting: v.optionalAsync(v.boolean(), false), + /** + * Whether to create a sourcemap for the generated service worker files. + */ + sourcemap: v.optionalAsync(v.boolean(), true), + /** + * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. + */ + swDest: v.pipeAsync( + v.string(errors['missing-sw-dest']), + v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + ), + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.optionalAsync(v.string()), + }), + v.forwardAsync( + v.checkAsync( + async input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), + errors['no-manifest-entries-or-runtime-caching'], + ), + ['globDirectory'], + ), + v.forwardAsync( + v.checkAsync( + async input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), + errors['nav-preload-runtime-caching'], + ), + ['navigationPreload'], + ), + // This is the cross-field validation for cacheName when expiration is present. + v.forwardAsync( + v.checkAsync( + async (options) => { + const runtimeCaching = options.runtimeCaching + if (!runtimeCaching) { + return true + } + for (const runtime of runtimeCaching) { + if (runtime.options?.expiration && !runtime.options?.cacheName) { + return false + } + } + + return true + }, + errors['cache-name-required'], + ), + ['runtimeCaching'], + ), +) diff --git a/packages/workbox-build/src/validation/async-inject-manifest.ts b/packages/workbox-build/src/validation/async-inject-manifest.ts new file mode 100644 index 0000000..9df3b0c --- /dev/null +++ b/packages/workbox-build/src/validation/async-inject-manifest.ts @@ -0,0 +1,109 @@ +import * as v from 'valibot' +import { errors } from './errors' +import { AsyncManifestEntrySchema } from './utils' + +export const AsyncInjectManifestOptionsSchema = v.pipeAsync( + v.strictObjectAsync({ + /** + * A list of entries to be precached, in addition to any entries that are + * generated as part of the build configuration. + */ + additionalManifestEntries: v.optionalAsync(v.arrayAsync(v.unionAsync([AsyncManifestEntrySchema, v.string()]))), + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2MiB (2097152 bytes) + */ + maximumFileSizeToCacheInBytes: v.optionalAsync(v.number(), 2097152), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), + + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + * @default true + */ + globFollow: v.optionalAsync(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optionalAsync(v.boolean(), true), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), + + /** + * The string to find inside of the `swSrc` file. Once found, it will be + * replaced by the generated precache manifest. + * @default "self.__WB_MANIFEST" + */ + injectionPoint: v.optionalAsync(v.string(), 'self.__WB_MANIFEST'), + /** + * The path and filename of the service worker file that will be read during + * the build process, relative to the current working directory. + */ + swSrc: v.string(), + + /** + * The path and filename of the service worker file that will be created by + * the build process, relative to the current working directory. It must end + * in '.js'. + */ + swDest: v.pipeAsync( + v.string(errors['missing-sw-dest']), + v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + ), + + /** + * The local directory you wish to match `globPatterns` against. The path is + * relative to the current directory. + */ + globDirectory: v.string(), + }), +) diff --git a/packages/workbox-build/src/validation/generate-sw.ts b/packages/workbox-build/src/validation/generate-sw.ts index 6f4f4bd..6f8a632 100644 --- a/packages/workbox-build/src/validation/generate-sw.ts +++ b/packages/workbox-build/src/validation/generate-sw.ts @@ -1,18 +1,20 @@ import * as v from 'valibot' import { errors } from './errors' +import { NumericExpressionSchema } from './utils' // Reused schema -const CacheQueryOptionsSchema = v.object({ +const CacheQueryOptionsSchema = v.strictObject({ ignoreMethod: v.optional(v.boolean()), ignoreSearch: v.optional(v.boolean()), ignoreVary: v.optional(v.boolean()), }) +/* export const GenerateSWOptionsSchema = v.pipe( v.strictObject({ - /** + /!** * A list of entries to be precached, in addition to any entries that are generated as part of the build configuration. - */ + *!/ additionalManifestEntries: v.optional(v.array(v.union([ v.object({ // Inlined ManifestEntrySchema integrity: v.optional(v.string()), @@ -21,116 +23,116 @@ export const GenerateSWOptionsSchema = v.pipe( }), v.string(), ]))), - /** + /!** * Assets that match this will be assumed to be uniquely versioned via their URL, and exempted from the normal HTTP cache-busting that's done when populating the precache. While not required, it's recommended that if your existing build process already inserts a `[hash]` value into each filename, you provide a RegExp that will detect that, as it will reduce the bandwidth consumed when precaching. - */ + *!/ dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - /** + /!** * One or more functions which will be applied sequentially against the generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their corresponding transformations will be applied first. - */ + *!/ manifestTransforms: v.optional(v.array(v.function())), - /** + /!** * This value can be used to determine the maximum size of files that will be precached. This prevents you from inadvertently precaching very large files that might have accidentally matched one of your patterns. - */ + *!/ maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), - /** + /!** * An object mapping string prefixes to replacement string values. This can be used to, e.g., remove or add a path prefix from a manifest entry if your web hosting setup doesn't match your local filesystem setup. As an alternative with more flexibility, you can use the `manifestTransforms` option and provide a function that modifies the entries in the manifest using whatever logic you provide. - */ + *!/ modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - /** + /!** * Determines whether or not symlinks are followed when generating the precache manifest. For more information, see the definition of `follow` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ + *!/ globFollow: v.optional(v.boolean(), true), - /** + /!** * A set of patterns matching files to always exclude when generating the precache manifest. For more information, see the definition of `ignore` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), - /** + *!/ + globIgnores: v.optional(v.array(v.string()), ['**!/node_modules/!**!/!*']), + /!** * Files matching any of these patterns will be included in the precache manifest. For more information, see the * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), - /** + *!/ + globPatterns: v.optional(v.array(v.string()), ['**!/!*.{js,css,html}']), + /!** * If true, an error reading a directory when generating a precache manifest will cause the build to fail. If false, the problematic directory will be skipped. For more information, see the definition of `strict` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ + *!/ globStrict: v.optional(v.boolean(), true), - /** + /!** * If a URL is rendered based on some server-side logic, its contents may depend on multiple files or on some other unique string value. The keys in this object are server-rendered URLs. If the values are an array of strings, they will be interpreted as `glob` patterns, and the contents of any files matching the patterns will be used to uniquely version the URL. If used with a single string, it will be interpreted as unique versioning information that you've generated for a given URL. - */ + *!/ templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - /** + /!** * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. - */ + *!/ babelPresetEnvTargets: v.optional(v.array(v.string()), ['chrome >= 56']), - /** + /!** * An optional ID to be prepended to cache names. This is primarily useful for local development where multiple sites may be served from the same `http://localhost:port` origin. - */ + *!/ cacheId: v.optional(v.nullable(v.string())), - /** + /!** * Whether or not Workbox should attempt to identify and delete any precaches created by older, incompatible versions. - */ + *!/ cleanupOutdatedCaches: v.optional(v.boolean(), false), - /** + /!** * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) any existing clients as soon as it activates. - */ + *!/ clientsClaim: v.optional(v.boolean(), false), - /** + /!** * If a navigation request for a URL ending in `/` fails to match a precached URL, this value will be appended to the URL and that will be checked for a precache match. This should be set to what your web server is using for its directory index. - */ + *!/ directoryIndex: v.optional(v.nullable(v.string())), disableDevLogs: v.optional(v.boolean(), false), - /** + /!** * Any search parameter names that match against one of the RegExp in this array will be removed before looking for a precache match. This is useful if your users might request URLs that contain, for example, URL parameters used to track the source of the traffic. If not provided, the default value is `[/^utm_/, /^fbclid$/]`. - */ + *!/ ignoreURLParametersMatching: v.optional(v.array(v.instance(RegExp))), - /** + /!** * A list of JavaScript files that should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) inside the generated service worker file. This is useful when you want to let Workbox create your top-level service worker file, but want to include some additional code, such as a push event listener. - */ + *!/ importScripts: v.optional(v.array(v.string())), - /** + /!** * Whether the runtime code for the Workbox library should be included in the top-level service worker, or split into a separate file that needs to be deployed alongside the service worker. Keeping the runtime separate means that users will not have to re-download the Workbox code each time your top-level service worker changes. - */ + *!/ inlineWorkboxRuntime: v.optional(v.boolean(), false), - /** + /!** * If set to 'production', then an optimized service worker bundle that excludes debugging info will be produced. If not explicitly configured here, the `process.env.NODE_ENV` value will be used, and failing that, it will fall back to `'production'`. - */ + *!/ mode: v.optional(v.nullable(v.string()), 'production'), - /** + /!** * If specified, all [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). - */ + *!/ navigateFallback: v.optional(v.nullable(v.string()), null), - /** + /!** * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. - */ + *!/ navigateFallbackAllowlist: v.optional(v.array(v.instance(RegExp))), - /** + /!** * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. - */ + *!/ navigateFallbackDenylist: v.optional(v.array(v.instance(RegExp))), - /** + /!** * Whether or not to enable [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) in the generated service worker. When set to true, you must also use `runtimeCaching` to set up an appropriate response strategy that will match navigation requests, and make use of the preloaded response. - */ + *!/ navigationPreload: v.optional(v.boolean(), false), - /** + /!** * Controls whether or not to include support for [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics). When `true`, the call to `workbox-google-analytics`'s `initialize()` will be added to your generated service worker. When set to an `Object`, that object will be passed in to the `initialize()` call, allowing you to customize the behavior. - */ - /* offlineGoogleAnalytics: v.optional(v.union([ + *!/ + /!* offlineGoogleAnalytics: v.optional(v.union([ v.object({ // Inlined GoogleAnalyticsInitializeOptionsSchema cacheName: v.optional(v.string()), parameterOverrides: v.optional(v.record(v.string(), v.string())), hitFilter: v.optional(v.function()), }), v.boolean(), - ]), false), */ - /** + ]), false), *!/ + /!** * When using Workbox's build tools to generate your service worker, you can specify one or more runtime caching configurations. These are then translated to {@link workbox-routing.registerRoute} calls using the match and handler configuration you define. - */ + *!/ runtimeCaching: v.optional(v.array(v.object({ - /** + /!** * This determines how the runtime route will generate a response. * To use one of the built-in workbox-strategies, provide its name, like 'NetworkFirst'. * Alternatively, this can be a workbox-core.RouteHandler callback function with custom response logic. - */ + *!/ handler: v.union([ v.function(), // RouteHandlerCallback v.object({ handle: v.function() }), // Inlined RouteHandlerObjectSchema @@ -142,9 +144,9 @@ export const GenerateSWOptionsSchema = v.pipe( 'StaleWhileRevalidate', ]), ]), - /** + /!** * The HTTP method to match against. The default value of 'GET' is normally sufficient, unless you explicitly need to match 'POST', 'PUT', or another type of request. - */ + *!/ method: v.optional(v.picklist([ 'DELETE', 'GET', @@ -154,9 +156,9 @@ export const GenerateSWOptionsSchema = v.pipe( 'PUT', ]), 'GET'), options: v.optional(v.object({ - /** + /!** * Configuring this will add a workbox-background-sync.BackgroundSyncPlugin instance to the workbox-strategies configured in `handler`. - */ + *!/ backgroundSync: v.optional(v.object({ name: v.string(), options: v.optional(v.object({ // Inlined QueueOptionsSchema @@ -165,9 +167,9 @@ export const GenerateSWOptionsSchema = v.pipe( onSync: v.optional(v.function()), })), })), - /** + /!** * Configuring this will add a workbox-broadcast-update.BroadcastUpdatePlugin instance to the workbox-strategies configured in `handler`. - */ + *!/ broadcastUpdate: v.optional(v.object({ channelName: v.string(), options: v.object({ // Inlined BroadcastCacheUpdateOptionsSchema @@ -176,33 +178,33 @@ export const GenerateSWOptionsSchema = v.pipe( notifyAllClients: v.optional(v.boolean()), }), })), - /** + /!** * Configuring this will add a workbox-cacheable-response.CacheableResponsePlugin instance to the workbox-strategies configured in `handler`. - */ + *!/ cacheableResponse: v.optional(v.object({ // Inlined CacheableResponseOptionsSchema statuses: v.optional(v.array(v.number())), headers: v.optional(v.record(v.string(), v.string())), })), - /** + /!** * If provided, this will set the `cacheName` property of the workbox-strategies configured in `handler`. - */ + *!/ cacheName: v.optional(v.nullable(v.string())), - /** + /!** * Configuring this will add a workbox-expiration.ExpirationPlugin instance to the workbox-strategies configured in `handler`. - */ + *!/ expiration: v.optional(v.object({ // Inlined ExpirationPluginOptionsSchema maxEntries: v.optional(v.number()), maxAgeSeconds: v.optional(v.number()), matchOptions: v.optional(CacheQueryOptionsSchema), // Reused purgeOnQuotaError: v.optional(v.boolean()), })), - /** + /!** * If provided, this will set the `networkTimeoutSeconds` property of the workbox-strategies configured in `handler`. Note that only 'NetworkFirst' and 'NetworkOnly' support `networkTimeoutSeconds`. - */ + *!/ networkTimeoutSeconds: v.optional(v.number()), - /** + /!** * Configuring this allows the use of one or more Workbox plugins that don't have "shortcut" options (like `expiration` for workbox-expiration.ExpirationPlugin). The plugins provided here will be added to the workbox-strategies configured in `handler`. - */ + *!/ plugins: v.optional(v.array(v.object({ // Inlined WorkboxPluginSchema cacheDidUpdate: v.optional(v.function()), cachedResponseWillBeUsed: v.optional(v.function()), @@ -217,48 +219,48 @@ export const GenerateSWOptionsSchema = v.pipe( handlerWillStart: v.optional(v.function()), requestWillFetch: v.optional(v.function()), }))), - /** + /!** * Configuring this will add a workbox-precaching.PrecacheFallbackPlugin instance to the workbox-strategies configured in `handler`. - */ + *!/ precacheFallback: v.optional(v.object({ fallbackURL: v.string() })), - /** + /!** * Enabling this will add a workbox-range-requests.RangeRequestsPlugin instance to the workbox-strategies configured in `handler`. - */ + *!/ rangeRequests: v.optional(v.boolean()), - /** + /!** * Configuring this will pass along the `fetchOptions` value to the workbox-strategies configured in `handler`. - */ + *!/ fetchOptions: v.optional(v.any()), // RequestInit is too complex to define for now - /** + /!** * Configuring this will pass along the `matchOptions` value to the workbox-strategies configured in `handler`. - */ + *!/ matchOptions: v.optional(CacheQueryOptionsSchema), // Reused })), - /** + /!** * This match criteria determines whether the configured handler will generate a response for any requests that don't match one of the precached URLs. If multiple `RuntimeCaching` routes are defined, then the first one whose `urlPattern` matches will be the one that responds. * This value directly maps to the first parameter passed to workbox-routing.registerRoute. It's recommended to use a workbox-core.RouteMatchCallback function for greatest flexibility. - */ + *!/ urlPattern: v.union([v.instance(RegExp), v.function(), v.string()]), }))), - /** + /!** * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) to the generated service worker. If `false`, then a `message` listener will be added instead, allowing client pages to trigger `skipWaiting()` by calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. - */ + *!/ skipWaiting: v.optional(v.boolean(), false), - /** + /!** * Whether to create a sourcemap for the generated service worker files. - */ + *!/ sourcemap: v.optional(v.boolean(), true), - /** + /!** * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. - */ + *!/ swDest: v.pipe( v.string(errors['missing-sw-dest']), v.endsWith('.js', errors['invalid-sw-dest-js-ext']), ), - /** + /!** * The local directory you wish to match `globPatterns` against. The path is * relative to the current directory. - */ + *!/ globDirectory: v.optional(v.string()), }), // This is the cross-field validation rule. @@ -279,3 +281,159 @@ export const GenerateSWOptionsSchema = v.pipe( ['navigationPreload'], ), ) +*/ + +// Schema for the `options` property within a runtimeCaching entry. +// This allows us to attach cross-field validation rules to it. +export const RuntimeCachingOptionsSchema = v.strictObject({ + backgroundSync: v.optional(v.strictObject({ + name: v.string(), + options: v.optional(v.strictObject({ + forceSyncFallback: v.optional(v.boolean()), + maxRetentionTime: v.optional(NumericExpressionSchema), + onSync: v.optional(v.function()), + })), + })), + broadcastUpdate: v.optional(v.strictObject({ + channelName: v.string(), + options: v.strictObject({ + headersToCheck: v.optional(v.array(v.string())), + generatePayload: v.optional(v.function()), + notifyAllClients: v.optional(v.boolean()), + }), + })), + cacheableResponse: v.optional(v.strictObject({ + statuses: v.optional(v.array(NumericExpressionSchema)), + headers: v.optional(v.record(v.string(), v.string())), + })), + cacheName: v.optional(v.nullable(v.string())), + expiration: v.optional(v.strictObject({ + maxEntries: v.optional(NumericExpressionSchema), + maxAgeSeconds: v.optional(NumericExpressionSchema), + matchOptions: v.optional(CacheQueryOptionsSchema), + purgeOnQuotaError: v.optional(v.boolean()), + })), + networkTimeoutSeconds: v.optional(NumericExpressionSchema), + plugins: v.optional(v.array(v.strictObject({ + cacheDidUpdate: v.optional(v.function()), + cachedResponseWillBeUsed: v.optional(v.function()), + cacheKeyWillBeUsed: v.optional(v.function()), + cacheWillUpdate: v.optional(v.function()), + fetchDidFail: v.optional(v.function()), + fetchDidSucceed: v.optional(v.function()), + handlerDidComplete: v.optional(v.function()), + handlerDidError: v.optional(v.function()), + handlerDidRespond: v.optional(v.function()), + handlerWillRespond: v.optional(v.function()), + handlerWillStart: v.optional(v.function()), + requestWillFetch: v.optional(v.function()), + }))), + precacheFallback: v.optional(v.strictObject({ fallbackURL: v.string() })), + rangeRequests: v.optional(v.boolean()), + fetchOptions: v.optional(v.any()), + matchOptions: v.optional(CacheQueryOptionsSchema), +}) + +// Schema for a single entry in the runtimeCaching array. +// It's a strict object to prevent unknown properties. +export const RuntimeCachingEntrySchema = v.strictObject({ + handler: v.union([ + v.function(), + v.strictObject({ handle: v.function() }), + v.picklist([ + 'CacheFirst', + 'CacheOnly', + 'NetworkFirst', + 'NetworkOnly', + 'StaleWhileRevalidate', + ]), + ]), + method: v.optional(v.picklist([ + 'DELETE', + 'GET', + 'HEAD', + 'PATCH', + 'POST', + 'PUT', + ]), 'GET'), + options: v.optional(RuntimeCachingOptionsSchema), + urlPattern: v.union([v.instance(RegExp), v.function(), v.string()]), +}) + +export const GenerateSWOptionsSchema = v.pipe( + v.strictObject({ + additionalManifestEntries: v.optional(v.array(v.union([ + v.strictObject({ + integrity: v.optional(v.string()), + revision: v.nullable(v.string()), + url: v.string(), + }), + v.string(), + ]))), + dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), + manifestTransforms: v.optional(v.array(v.function())), + maximumFileSizeToCacheInBytes: v.optional(NumericExpressionSchema, 2097152), + modifyURLPrefix: v.optional(v.record(v.string(), v.string())), + globFollow: v.optional(v.boolean(), true), + globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), + globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), + globStrict: v.optional(v.boolean(), true), + templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), + babelPresetEnvTargets: v.optional(v.array(v.string()), ['chrome >= 56']), + cacheId: v.optional(v.nullable(v.string())), + cleanupOutdatedCaches: v.optional(v.boolean(), false), + clientsClaim: v.optional(v.boolean(), false), + directoryIndex: v.optional(v.nullable(v.string())), + disableDevLogs: v.optional(v.boolean(), false), + ignoreURLParametersMatching: v.optional(v.array(v.instance(RegExp))), + importScripts: v.optional(v.array(v.string())), + inlineWorkboxRuntime: v.optional(v.boolean(), false), + mode: v.optional(v.nullable(v.string()), 'production'), + navigateFallback: v.optional(v.nullable(v.string()), null), + navigateFallbackAllowlist: v.optional(v.array(v.instance(RegExp))), + navigateFallbackDenylist: v.optional(v.array(v.instance(RegExp))), + navigationPreload: v.optional(v.boolean(), false), + runtimeCaching: v.optional(v.array(RuntimeCachingEntrySchema)), + skipWaiting: v.optional(v.boolean(), false), + sourcemap: v.optional(v.boolean(), true), + swDest: v.pipe( + v.string(errors['invalid-sw-dest']), + v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + ), + globDirectory: v.optional(v.string()), + }), + v.forward( + v.check( + input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), + errors['no-manifest-entries-or-runtime-caching'], + ), + ['globDirectory'], + ), + v.forward( + v.check( + input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), + errors['nav-preload-runtime-caching'], + ), + ['navigationPreload'], + ), + // This is the cross-field validation for cacheName when expiration is present. + v.forward( + v.check( + (options) => { + const runtimeCaching = options.runtimeCaching + if (!runtimeCaching) { + return true + } + for (const runtime of runtimeCaching) { + if (runtime.options?.expiration && !runtime.options?.cacheName) { + return false + } + } + + return true + }, + errors['cache-name-required'], + ), + ['runtimeCaching'], + ), +) diff --git a/packages/workbox-build/src/validation/utils.ts b/packages/workbox-build/src/validation/utils.ts new file mode 100644 index 0000000..c8d9e81 --- /dev/null +++ b/packages/workbox-build/src/validation/utils.ts @@ -0,0 +1,200 @@ +import type { Node } from '@babel/types' +import * as v from 'valibot' + +/** + * Recursively infers the type of an AST expression node. + * It does NOT evaluate the expression. + */ +function inferExpressionType(node: Node): 'number' | 'unknown' { + switch (node.type) { + case 'NumericLiteral': + return 'number' + case 'BinaryExpression': { + // A binary expression is numeric only if both sides are numeric + // and the operator is mathematical. + const leftType = inferExpressionType(node.left) + const rightType = inferExpressionType(node.right) + if (leftType === 'number' && rightType === 'number') { + if (['+', '-', '*', '/', '%'].includes(node.operator)) + return 'number' + } + return 'unknown' + } + default: + return 'unknown' + } +} + +/** + * A custom Valibot schema that accepts either a primitive number + * or a magicast Proxy representing a numeric expression. + */ +export const NumericExpressionSchema = v.custom( + (input) => { + // Accept primitive numbers + if (typeof input === 'number') + return true + // Accept magicast proxies + if (typeof input === 'object' && input !== null && '$ast' in input) { + // And statically validate that the expression will result in a number + return inferExpressionType((input as any).$ast) === 'number' + } + return false + }, + 'invalid-number-entry-or-expression', +) + +// ManifestEntry is used in additionalManifestEntries +export const AsyncManifestEntrySchema = v.strictObjectAsync({ + integrity: v.optionalAsync(v.string()), + revision: v.nullable(v.string()), + url: v.string(), +}) + +/** + * A custom Valibot schema that accepts either a primitive number + * or a magicast Proxy representing a numeric expression. + */ +export const AsyncNumericExpressionSchema = v.customAsync( + (input) => { + // Accept primitive numbers + if (typeof input === 'number') + return true + // Accept magicast proxies + if (typeof input === 'object' && input !== null && '$ast' in input) { + // And statically validate that the expression will result in a number + return inferExpressionType((input as any).$ast) === 'number' + } + return false + }, + 'invalid-number-entry-or-expression', +) + +// Reused schema +export const AsyncCacheQueryOptionsSchema = v.strictObjectAsync({ + ignoreMethod: v.optionalAsync(v.boolean()), + ignoreSearch: v.optionalAsync(v.boolean()), + ignoreVary: v.optionalAsync(v.boolean()), +}) + +// Schema for the `options` property within a runtimeCaching entry. +// This allows us to attach cross-field validation rules to it. +export const AsyncRuntimeCachingOptionsSchema = v.strictObjectAsync({ + /** + * Configuring this will add a workbox-background-sync.BackgroundSyncPlugin instance to the workbox-strategies configured in `handler`. + */ + backgroundSync: v.optionalAsync(v.strictObjectAsync({ + name: v.string(), + options: v.optionalAsync(v.strictObjectAsync({ // Inlined QueueOptionsSchema + forceSyncFallback: v.optionalAsync(v.boolean()), + maxRetentionTime: v.optionalAsync(AsyncNumericExpressionSchema), + onSync: v.optionalAsync(v.function()), + })), + })), + /** + * Configuring this will add a workbox-broadcast-update.BroadcastUpdatePlugin instance to the workbox-strategies configured in `handler`. + */ + broadcastUpdate: v.optionalAsync(v.strictObjectAsync({ + channelName: v.string(), + options: v.strictObjectAsync({ // Inlined BroadcastCacheUpdateOptionsSchema + headersToCheck: v.optionalAsync(v.arrayAsync(v.string())), + generatePayload: v.optionalAsync(v.function()), + notifyAllClients: v.optionalAsync(v.boolean()), + }), + })), + /** + * Configuring this will add a workbox-cacheable-response.CacheableResponsePlugin instance to the workbox-strategies configured in `handler`. + */ + cacheableResponse: v.optionalAsync(v.strictObjectAsync({ // Inlined CacheableResponseOptionsSchema + statuses: v.optionalAsync(v.arrayAsync(AsyncNumericExpressionSchema)), + headers: v.optionalAsync(v.recordAsync(v.string(), v.string())), + })), + /** + * If provided, this will set the `cacheName` property of the workbox-strategies configured in `handler`. + */ + cacheName: v.optionalAsync(v.nullable(v.string())), + /** + * Configuring this will add a workbox-expiration.ExpirationPlugin instance to the workbox-strategies configured in `handler`. + */ + expiration: v.optionalAsync(v.strictObjectAsync({ // Inlined ExpirationPluginOptionsSchema + maxEntries: v.optionalAsync(AsyncNumericExpressionSchema), + maxAgeSeconds: v.optionalAsync(AsyncNumericExpressionSchema), + matchOptions: v.optionalAsync(AsyncCacheQueryOptionsSchema), // Reused + purgeOnQuotaError: v.optionalAsync(v.boolean()), + })), + /** + * If provided, this will set the `networkTimeoutSeconds` property of the workbox-strategies configured in `handler`. Note that only 'NetworkFirst' and 'NetworkOnly' support `networkTimeoutSeconds`. + */ + networkTimeoutSeconds: v.optionalAsync(AsyncNumericExpressionSchema), + /** + * Configuring this allows the use of one or more Workbox plugins that don't have "shortcut" options (like `expiration` for workbox-expiration.ExpirationPlugin). The plugins provided here will be added to the workbox-strategies configured in `handler`. + */ + plugins: v.optionalAsync(v.arrayAsync(v.strictObjectAsync({ // Inlined WorkboxPluginSchema + cacheDidUpdate: v.optionalAsync(v.function()), + cachedResponseWillBeUsed: v.optionalAsync(v.function()), + cacheKeyWillBeUsed: v.optionalAsync(v.function()), + cacheWillUpdate: v.optionalAsync(v.function()), + fetchDidFail: v.optionalAsync(v.function()), + fetchDidSucceed: v.optionalAsync(v.function()), + handlerDidComplete: v.optionalAsync(v.function()), + handlerDidError: v.optionalAsync(v.function()), + handlerDidRespond: v.optionalAsync(v.function()), + handlerWillRespond: v.optionalAsync(v.function()), + handlerWillStart: v.optionalAsync(v.function()), + requestWillFetch: v.optionalAsync(v.function()), + }))), + /** + * Configuring this will add a workbox-precaching.PrecacheFallbackPlugin instance to the workbox-strategies configured in `handler`. + */ + precacheFallback: v.optionalAsync(v.strictObjectAsync({ fallbackURL: v.string() })), + /** + * Enabling this will add a workbox-range-requests.RangeRequestsPlugin instance to the workbox-strategies configured in `handler`. + */ + rangeRequests: v.optionalAsync(v.boolean()), + /** + * Configuring this will pass along the `fetchOptions` value to the workbox-strategies configured in `handler`. + */ + fetchOptions: v.optionalAsync(v.any()), // RequestInit is too complex to define for now + /** + * Configuring this will pass along the `matchOptions` value to the workbox-strategies configured in `handler`. + */ + matchOptions: v.optionalAsync(AsyncCacheQueryOptionsSchema), // Reused +}) + +// Schema for a single entry in the runtimeCaching array. +// It's a strict object to prevent unknown properties. +export const AsyncRuntimeCachingEntrySchema = v.strictObjectAsync({ + /** + * This determines how the runtime route will generate a response. + * To use one of the built-in workbox-strategies, provide its name, like 'NetworkFirst'. + * Alternatively, this can be a workbox-core.RouteHandler callback function with custom response logic. + */ + handler: v.unionAsync([ + v.function(), // RouteHandlerCallback + v.strictObjectAsync({ handle: v.function() }), // Inlined RouteHandlerObjectSchema + v.picklist([ + 'CacheFirst', + 'CacheOnly', + 'NetworkFirst', + 'NetworkOnly', + 'StaleWhileRevalidate', + ]), + ]), + /** + * The HTTP method to match against. The default value of 'GET' is normally sufficient, unless you explicitly need to match 'POST', 'PUT', or another type of request. + */ + method: v.optional(v.picklist([ + 'DELETE', + 'GET', + 'HEAD', + 'PATCH', + 'POST', + 'PUT', + ]), 'GET'), + options: v.optionalAsync(AsyncRuntimeCachingOptionsSchema), + /** + * This match criteria determines whether the configured handler will generate a response for any requests that don't match one of the precached URLs. If multiple `RuntimeCaching` routes are defined, then the first one whose `urlPattern` matches will be the one that responds. + * This value directly maps to the first parameter passed to workbox-routing.registerRoute. It's recommended to use a workbox-core.RouteMatchCallback function for greatest flexibility. + */ + urlPattern: v.unionAsync([v.instance(RegExp), v.function(), v.string()]), +}) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 5ca94ec..f9860ec 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -1,5 +1,6 @@ -import type { BaseIssue, BaseSchema, IssuePathItem } from 'valibot' -import { getDotPath, safeParse } from 'valibot' +// import type { ArrayExpression, Program } from '@babel/types' +import type { BaseIssue, BaseSchema, BaseSchemaAsync, InferOutput, IssuePathItem } from 'valibot' +import { getDotPath, safeParse, safeParseAsync } from 'valibot' // This helper function traverses the issue's path to find the top-level object key // that contains the error. This is crucial for errors nested inside arrays. @@ -15,14 +16,21 @@ function getTopLevelKey(path: IssuePathItem[] | undefined): string | undefined { // see [Path Key not Available in safeParse](https://github.com/fabian-hiller/valibot/discussions/696). // custom Valibot's message mapping function extractIssueMessage(issue: BaseIssue) { + console.log(issue) const path = getDotPath(issue) const topLevelKey = getTopLevelKey(issue.path) const lastKey = issue.path?.[issue.path.length - 1]?.key // Priority 1: Custom messages from pipes (e.g., endsWith). // These are the most specific and should always be shown. - if (issue.type === 'custom') + if (issue.type === 'custom') { + // handle NumericExpressionSchema from utils.ts + if (issue.message === 'invalid-number-entry-or-expression') { + return `The option "${path}" must be a number or a valid numeric expression (e.g., 60 * 60).` + } + return issue.message + } // Priority 2: Missing required key. if (issue.kind === 'schema' && issue.received === 'undefined') { @@ -80,38 +88,37 @@ function extractIssueMessage(issue: BaseIssue) { } // This function intelligently sanitizes the options object from magicast. -// It creates a plain object but preserves special types like RegExp -// by checking the `$type` property provided by magicast. function sanitizeMagicastOptions(options: any): any { - if (options === null || typeof options !== 'object') { + if (options === null || typeof options !== 'object') return options - } - // If magicast has tagged the type, reconstruct it. - if (options.$type === 'RegExp' || options.$ast?.type === 'RegExpLiteral') { - return new RegExp(options.source, options.flags) + // Gracias a tu parche, podemos detectar los tipos directamente. + if (options instanceof RegExp || typeof options === 'function') { + return options } - // A proxified array is an object with `$type: 'array'` - if (options.$type === 'array') { - // We can't just use Array.from(options) because it's not a real iterable. - // We need to manually reconstruct the array from its numeric keys. + if (Array.isArray(options)) { + console.log('PASO') const sanitizedArray: any[] = [] - let i = 0 - while (i in options) { - sanitizedArray.push(sanitizeMagicastOptions(options[i])) - i++ + for (const option of options) { + sanitizedArray.push(sanitizeMagicastOptions(option)) } return sanitizedArray + // return options.map(sanitizeMagicastOptions) } - // For regular objects (or proxies of objects) + /* if (options.$type === 'array') { + const sanitizedArray: any[] = [] + for (const option of options) { + sanitizedArray.push(sanitizeMagicastOptions(option)) + } + return sanitizedArray + } */ + const sanitized: { [key: string]: any } = {} for (const key in options) { - // Copy own properties, but ignore magicast's internal metadata. - if (Object.prototype.hasOwnProperty.call(options, key) && !key.startsWith('$')) { + if (Object.prototype.hasOwnProperty.call(options, key) && !key.startsWith('$')) sanitized[key] = sanitizeMagicastOptions(options[key]) - } } return sanitized @@ -128,12 +135,47 @@ export function validate>( schema: TSchema, options: unknown, methodName: string, -): void { - const result = safeParse(schema, '$ast' in (options as any) ? sanitizeMagicastOptions(options) : options) +): InferOutput { + /* const result = safeParse( + schema, + '$ast' in (options as any) + ? sanitizeMagicastOptions(options) + : options, + ) */ + const result = safeParse( + schema, + options, + ) + // const result = safeParse(schema, options) + if (!result.success) { + const errorMessages = result.issues.map(extractIssueMessage) + throw new Error( + `${methodName}() options validation failed: \n- ${errorMessages.join('\n- ')}`, + ) + } + return result.output +} +/** + * A wrapper around Valibot's `safeParseAsync` that throws a user-friendly error + * if validation fails. + * @param schema The Valibot schema to use. + * @param options The options object to validate. + * @param methodName The name of the Workbox method being validated, for context. + */ +export async function validateAsync>( + schema: TSchema, + options: unknown, + methodName: string, +): Promise> { + const result = await safeParseAsync( + schema, + options, + ) if (!result.success) { const errorMessages = result.issues.map(extractIssueMessage) throw new Error( `${methodName}() options validation failed: \n- ${errorMessages.join('\n- ')}`, ) } + return result.output } diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index 2da3feb..3be914e 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -84,7 +84,7 @@ describe('generateSWOptions Schema Validation', () => { ) }) - it('should pass if globDirectory is missing but runtimeCaching is present', () => { + it.only('should pass if globDirectory is missing but runtimeCaching is present', () => { const options = { swDest: 'sw.js', runtimeCaching: [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 606602d..9008de7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,9 +50,6 @@ catalogs: idb: specifier: ^8.0.3 version: 8.0.3 - magicast: - specifier: ^0.3.5 - version: 0.3.5 pathe: specifier: ^2.0.3 version: 2.0.3 @@ -62,6 +59,9 @@ catalogs: proper-tags: specifier: ^2.0.2 version: 2.0.2 + recast: + specifier: ^0.23.11 + version: 0.23.11 taze: specifier: ^19.7.0 version: 19.7.0 @@ -158,8 +158,8 @@ importers: specifier: workspace:* version: link:../workbox-routing magicast: - specifier: catalog:utils - version: 0.3.5 + specifier: file:magicast-0.3.5.tgz + version: file:packages/workbox-build/magicast-0.3.5.tgz pathe: specifier: catalog:utils version: 2.0.3 @@ -169,6 +169,9 @@ importers: proper-tags: specifier: catalog:utils version: 2.0.2 + recast: + specifier: catalog:utils + version: 0.23.11 valibot: specifier: catalog:utils version: 1.1.0(typescript@5.9.3) @@ -1090,6 +1093,10 @@ packages: resolution: {integrity: sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g==} engines: {node: '>=20.19.0'} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1525,6 +1532,11 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -1804,6 +1816,10 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magicast@file:packages/workbox-build/magicast-0.3.5.tgz: + resolution: {integrity: sha512-z7mBYVtGx5aFIFqOa6lBYuyKJqYUsvS85uB8zMDcBIZTtN3j7xlYh67g2WmHTZJbFX8i74T2t9g4KcZF9U5XGA==, tarball: file:packages/workbox-build/magicast-0.3.5.tgz} + version: 0.3.5 + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -2133,6 +2149,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + refa@0.12.1: resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -2239,6 +2259,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + spdx-exceptions@2.5.0: resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} @@ -2300,6 +2324,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -3343,6 +3370,10 @@ snapshots: '@babel/parser': 7.28.4 pathe: 2.0.3 + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.17: {} @@ -3856,6 +3887,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 + esprima@4.0.1: {} + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -4080,6 +4113,13 @@ snapshots: '@babel/parser': 7.28.4 '@babel/types': 7.28.4 source-map-js: 1.2.1 + optional: true + + magicast@file:packages/workbox-build/magicast-0.3.5.tgz: + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + source-map-js: 1.2.1 markdown-table@3.0.4: {} @@ -4589,6 +4629,14 @@ snapshots: readdirp@4.1.2: {} + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + refa@0.12.1: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -4719,6 +4767,8 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.6.1: {} + spdx-exceptions@2.5.0: {} spdx-expression-parse@4.0.0: @@ -4787,6 +4837,8 @@ snapshots: dependencies: any-promise: 1.3.0 + tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -4850,8 +4902,7 @@ snapshots: - supports-color - vue-tsc - tslib@2.8.1: - optional: true + tslib@2.8.1: {} tsx@4.20.6: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1ea9dd6..12c7add 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -24,10 +24,10 @@ catalogs: '@antfu/ni': ^25.0.0 '@arethetypeswrong/cli': ^0.17.4 idb: ^8.0.3 - magicast: ^0.3.5 pathe: ^2.0.3 pkg-types: ^2.3.0 proper-tags: ^2.0.2 + recast: ^0.23.11 taze: ^19.7.0 valibot: ^1.1.0 @@ -40,6 +40,6 @@ onlyBuiltDependencies: shamefullyHoist: true -shell-emulator: true +shellEmulator: true strictPeerDependencies: false From fb4b07d0e9978402cf0876346282a8b93cd7fa41 Mon Sep 17 00:00:00 2001 From: userquin Date: Fri, 24 Oct 2025 18:06:55 +0200 Subject: [PATCH 14/20] chore: prepare migration --- package.json | 2 +- packages/sw-tooltkit/package.json | 48 +++++++++++++++++++ packages/sw-tooltkit/src/index.ts | 0 packages/sw-tooltkit/tsdown.config.ts | 16 +++++++ packages/toolkit/magicast-0.3.5.tgz | Bin 0 -> 125714 bytes packages/toolkit/package.json | 65 ++++++++++++++++++++++++++ packages/toolkit/src/index.ts | 0 packages/toolkit/tsdown.config.ts | 13 ++++++ pnpm-lock.yaml | 33 +++++++++++++ 9 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 packages/sw-tooltkit/package.json create mode 100644 packages/sw-tooltkit/src/index.ts create mode 100644 packages/sw-tooltkit/tsdown.config.ts create mode 100644 packages/toolkit/magicast-0.3.5.tgz create mode 100644 packages/toolkit/package.json create mode 100644 packages/toolkit/src/index.ts create mode 100644 packages/toolkit/tsdown.config.ts diff --git a/package.json b/package.json index a4819b0..ff22441 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "type": "module", "version": "0.0.0", "private": true, - "packageManager": "pnpm@10.18.3", + "packageManager": "pnpm@10.19.0", "description": "Top-level scripts and dependencies for the workbox monorepo.", "author": "Google's Web DevRel Team, Vite PWA's Team", "contributors": [ diff --git a/packages/sw-tooltkit/package.json b/packages/sw-tooltkit/package.json new file mode 100644 index 0000000..9eb6b91 --- /dev/null +++ b/packages/sw-tooltkit/package.json @@ -0,0 +1,48 @@ +{ + "name": "@vite-pwa/sw-toolkit", + "type": "module", + "version": "0.0.0", + "description": "This library takes a Response object and determines whether it's cacheable based on a specific configuration.", + "author": "Google's Web DevRel Team, Vite PWA's Team", + "contributors": [ + "userquin " + ], + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/vite-pwa/toolkit#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vite-pwa/toolkit.git", + "directory": "packages/sw-toolkit" + }, + "bugs": "https://github.com/vite-pwa/toolkit/issues", + "keywords": [ + "workbox", + "workboxjs", + "service worker", + "sw", + "workbox-plugin" + ], + "sideEffects": false, + "exports": { + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + } + }, + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsdown", + "prepublishOnly": "pnpm run build" + } +} diff --git a/packages/sw-tooltkit/src/index.ts b/packages/sw-tooltkit/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/sw-tooltkit/tsdown.config.ts b/packages/sw-tooltkit/tsdown.config.ts new file mode 100644 index 0000000..cfa94bb --- /dev/null +++ b/packages/sw-tooltkit/tsdown.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: './src/{index,types}.ts', + platform: 'browser', + banner: `/* + Copyright 2019 Google LLC, Vite PWA's Team + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/`, + define: { + 'process.env.NODE_ENV': 'process.env.NODE_ENV', + }, +}) diff --git a/packages/toolkit/magicast-0.3.5.tgz b/packages/toolkit/magicast-0.3.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..245c69aff8f5214f3aa59a9005171fc5ff1047ef GIT binary patch literal 125714 zcmV)3K+C@$iwFP!000003hce_cN{maAiAG@&ioH_zpT?5cUrb*CU;kglC@=dq8VHE zT1w{L)zkGYbyHHst*TB{wPj7L_qV?TfIk3|RbAbZJ+t@Re2CRW5(GgI1OX77XOs8Y zx!!!azq@yExcA>a$A6C=J^J>Ke^l`AqeqXze;+-0{OzCAqd)xV+eeQdfBWsDKPve5 z50BMaZEl|D&Bfw$JSi_X zv!Xd$d=F6H=;|`B>%1&fUaJdT>C*EHConTxDuf&aZ!WuRK%DMP94AJZrA9N+T{=U6+$QYxGo2%gN$W7fsechiCar z*J{{YXf-%AEe0Fd%T#Bx-`&d#1%!!*l0A!@{VlrDy0Sj9^%P(`& z1vE#Z)xW#flxk7y5u!R$m*q4+gMT%SY`!?1<@Lo#O>=;Kx@h!B)$kD~U<4!CEGt#( z+3a`sCgnWWwZh?g{IC*WKL?^VhDh8XHYZRhQW3FM86z z7pQ+$&SvEmjBZjE(;Nm>Z^6VJU1*h^mLD{ZhZv(Mn|wk<$7z`R$u&jwMK+tMQ*EfG zrz$U0#)nh^26dAaO`grvysWUxaHwNM;m197`1-}s-=4nRQ~QVN&D+;M??2yrt_DvJ z)&AjNr2e*l^yBNFj?~|tzJ2@j;OO7f>lfP`!TpyL%%?u^x(NVczv*c z@Z#g(X&)b7it`>#go`O{ZV|GbB-ULXCq_x5-9pdKNp z{`TV@e1jgJ9;m0gNBgf2U?jV*502hG-8~wqqt|bbT-(3xAMTCR)3^JFK$915U%wjt z?jF$e^$V!8e*g^+_6Q8nTZw6T{T6@!bhzgrs^@!8U+y3L`4Af8C~fWW@9zDhh2DSt z=l+|U{U0RK=7pYtI2liVsqeCn?*0GqlRy6H+hG6y!@vCD+yC1Czr=rEFKVr5qx*XM z-n~g#)Q!rEss5;TRHc7iJAmz!+a|Y~OR=i*l;B&g8e?XI(C;iT>s?n|~8D z{$Horsh(}lv#Qn=l-xU86u952sn+vXuxGx;eQJ1_o#&IRZbqua4;$*YdrIYJYS>)Q zb$Mn!tDT)4HK4x->cIp1`Tow1Di*UDHB;)WtkjT(`d(kd?!>{ue^h9@{W+U0wAyjM zkKgO-lWkFz0N9H3OH>1719&?-yqWJDVK#ZO(6;dn17b!T!{P&%rFQ&KA_$gl%*#U3 z!GCSDgDpm4Ol|;5&uYzJ2vRCR2svo@|INJ{#vJFmd9Kg0#jM%;2&%*DIcg6M4MMfj z&7vxRl4tq(i>kalD5u)h8Eb4>kEaA_WBcBX=wa8j`#G!f>~yA|>&YyuP~!`_;!_&} z9)AS{M=}obsVpRh8Ay5aK@$%0|_TIRMtvEw!5! zMcEkTl9aIbc`*g`$@HoS3pM=ew{FC4HvR)U!HEJ=6E_ppp6f|whrB;!{2#+AvP+yd zwz5DrD8{Jcui$^D<@DMP9_R+O#z-RM!UVAgp7j{?sjmQ?AQ<+o=KKo%u(EMJWj*fu zNdevu>cP0MUXmhV{UUF8zm}TtUF}3w84^c_hgp-?XSuHTKF%wR>hwVU5Vz5;SZzh6 z+fmmOn1mg}T<5ybRo3WTSP6*i7|K(vV6Yw&2LWZZ|2~A~8@#<)b({u@qd5`<)X!G9 zNW<TCBm^~R@9?JSp+ z+xP4wj4!dPT7CMYhQX&Db$o(VVbJ$w;nLH-EVFw*lnlI`Ls$fSBp!_govtwUy z!tfEFchqk;hV#PhD6Z|Pv>ojx*>w7I-sm^8#d%&ZO3(4b`ecOx8Y(arNo)amQR}Lq z{`ZTfOo%|*jAG<)G<;_bVVQq$vGQPj`ia@iX0y)8c}Jhr zj#Y5%Z=gEpx5YWg)8E9PMhsbpnYvN7e0%*RMQto)_}=M=oj3Zj9>X|kqaWrQYUg{E zBdHPbcZ~M1+2o#1e#r4M=JkB{!LAW4nOjH_M)luod8mVZV7^V@ymV?wa;CHM|W`WQT-`PQ+scUL{Xa)SlT}B~w~2;i4ZKz$wd^qU=tG zKY#ck+Qw$OIBzbXQEzM3_hPkHN?2mFkDiX-KF_ z0e}s)wIYNKyVc&ahjDRcP&@JrvCXUb-IS{N-@aO~cj5U}eDPJ|4LZ4N`v%Lljvl*< z0G86o+I2PGG_9PdoffTS`#+)MLfHSEk@s%<=(7Jme*E}PkDoja?f-xHmp}cN{r^k+ zm$d(bJ>uP2%{>6^mVRnNX8u>%oHvSW|5O+4{%_Fi&ue%11K#CDqpNammh*0TInNq3 z1Y3;NhBY&8(`NfM*_6MAg0Ib~=xZ?cYxRTD)^@43l-^c1iOvqPOI^>iiKgy`n(Lpd zbhqJf+_qK@AhOy~BuU4UN@tB8qMd!)cfmCT?y@jIrID>Teh)_lef<6;G?CH&3pe5& zi-&V!xq54+c*P4jr(x3UqF@G z*2(LG#pNk-t*JgP@B^B@z4zePVRMlc!7&sd;Yr?VWy25T_JK3M0X&{u=*fFq@Zf>M zzts0T3IJNo%%Sm64WMkG9%6aQl5Zvv=t9_RZmJgs(^_5WN<+NLRGsQc2BKh?U6#eU zDw_*ksY+)xDksfF)_h%KaK>d>Hx`hd>GP~;)KzwkQ9BStf}9E!_2s;|ep*%8wJHm( zve{L3jbSS1`3J2sFwt5#8P>`RhzObJF@7GvY8Y%evJO4NTee-yX6AfClo|LgI~yLK z$Yy9D=xpIk2)+@VzB}8{@tikV6jObc7kZj%=PEgQ+zFl6+xAMg<`Ruhk@!X|Ex(lc{>0ce$06OvO?ikU9&T8Y2p7q@{&i$b zMYK+h4b?-SS!HLb%k278A8Mo5lV)2F$PM~6J&^Ts@=(eS00)j2P&0S zex4WE%m7t8teN%s`Px&iTZ0Q%wmx>2NOE_mN!Cm*giPc{ISCEV)%*iEP($>XH#6+-}ImvWPE8|U7&UMvXhX@RO z%K`L`9K3}L<~lU{cr+z=He=A7lLKZ6_i^a;t0!LJkhpC7iU(zNnay1FVR`z?2x25g z%CAx%GNFFp;92G0lr9#Rx`IO+4ZRpwtkcA~(RUu5;`tKzF~Z8`n+lQNlQb*-LdwML}{#+21ykRaJO!UH+i zN$*1_09PSQ70-%5!WuEc7|rsEFv`!ahaW_|hWDb9-xL~wwlOVrtuXii7c)vwQa|!q zaA_bQ;Un(yug~0GMpV!5q*rrUA*A$;3l2 z7}Sfo7l24ShK>D`N(06lLhX?nK-|S(qy`R&fz!XdQC3lb#=V+)_+TvxW2BcnKMK_sc11 zzWTm4mXeTouF^FfY`l`;fq3D5-_-N_41j9Y7@*J$h%Y9w9tfhOImwTH6h8yH)$Jsw zax}leAH9g~`|rWLzq#dZ`WpXI=EV@;Y@md=>CMMhKBDp2X@L|J#|nPCLGI>V8Xm`A zLUFVz8x)|`olC=G`V2?W6N8YMp!k$MPdxTs*6IH)i@Zb&g)h9Jr z&itqd`=)hysfRcS5)=WsM(X~35)oZT8xQ!7RtWRf+WB}>>1jU68r^SN$fq^8joK%O zg1eNBT{B-@L-0D-zk5xez_aHp+Rj&zS`tms;EM~d=lU$UE=bV=tBwzKG@NIaM6gm0 zXfo`dodiG4YOGWbM%IhdTDQC^@C(%4{Jr{L*7T%)znL<>8DN{6YTpDlsq(6DXpPmI za=w_+J);H8DmXf_S^jtOt942|y02&wygm&u?~XGkiR0A)&$Qa*;KOB5rvJPalJU4{glayl$o1$2LQjO+QIED&uD(;rjCmx;S{Djx; zL?(>9777iY%>c8rvf9fg7sH{YWMuOt%Agne^%<-7oqEKvqiPsa?CJyoC>z3#U=j0b z3o=BD=1A&`k;^Y2G!Zuop~s|E$Fz3iAbC68i=tC1Q-=ettD22*N8P_qe=gH5|8n^H zz?k>JyyLLOQ`(jiBE-US>$g1pg|7p9kO!Nb19ZaH0biYHRr9Wn=J_xDMZW=PVsxj9HD4Dl3q@;s7nI zesmJ16E=>_pHsd22tQc`(l}tSz--2HyYu8`ii=w7nD*seJc!_mUs9PSnbf$dw5m+TN_F(X$mm=|@?kmg`B1T=VjC`q4~ zk} zxfeY(2*JvPD<;UkQw?`Eui>POks(d#Tz49;1O)pGY^%cFFcUBRn7%c4fXv*d$;^UQc{b?dJIF) zB-49slnNp&QmHjODozASg~MZjalfx6Ih;EGApl zH%jBk-j1{l-fFl{n`l4XAe*Ki@AJA7*5K z3kD{6B*0R=!N@ciK7R6tKW;#fL%jZc-65_xZ^JWDl`JgLY171>TaJTA!Va{>Sd>bT ztF|oL(Kq6pEyrB+mDpp;aS(llncgxyV&a}~KDXSIo3JpNv@Jigw)SkcWoC}PIKpr7 z8KN?x$d;o&m#kL)Vp!=?TXQH^>285HSTPG{|AU#ZlErE_Ki#@`GkYQR!Gq5IWFu7i z4A1&~?L=yK)08!e3B75X4M&JZ8(4{=(Rz7c%XGds>-CBKK61|_pPTD5W_R0Uq@)Ga z1}-FmS^WJZ+t_>%Qohxf*3;%1YpaZ!gL$PdGf?qn*VGei8;imN6oE1008xi(>SSjl z9&Lc4w5vqC*Bz#a#v7QdofNxs7QO!=I7bR+@qtZ)F*w9H@0aC-vL%o~KdSYntn=v9 zA|0O{_VbQ|Kys86KB)2HGL6;*bR@{sw{H%c;;N)a1j*HR8W4zN!ek(8aDah6x^Q)2 z5$QVI{X>wQbBVcqxSJubd5Jk#nqS-D>T#Z?7Al8>@&O#f0T6L8G66hxt4nz_I9AY? z+7Se!Ju#aSjt~uq`{M`>P1)s0W!3ozx4HADAF_N#*ow0}3`dG!4q;;>;$F|3#=q0z z*>UqOp=Yz1gT2Jyt{$3-sr3*zZ;r9%iA1i|bHl8<64{Vc96@Vz&8l;TkiQvB-8gG( zx^Ym6QG6o;D%)4;QrC5M4p0U~X&{y^44K`(!1FYmi-|x$$iQ|Xl0 zY!OH!tF$kQ1prebJZUOrW8D~RcW{6iZ*uxcECmreOE^m4*=G4p1bKQY(qD#ZOSU++ z$#KMSt_`=!&_I^k$E&(iJUxKO!lVmw?cxHg&L_l_&h-@#sl`XPltP27Y2ZQ$Z0${H zEUpctm|bP3Zy|#_`p1{R$|(e9Qz$WO8?mBwr#W^sGB1~7#AU6%LqdhjY6WL3HNhds zj#_R2@ycw4g11A&CKM9khJEMuS1C?6T1F}dvl0@#V>F6Fo3TsOCO4wh-J;Tx8gaMb zYm??hk+Ut@l>XB&?rmo_-k~v$KedOpvH>_QN#=Y}rvCE0mA-3Ya|(>W!~2YWU_gk( zX2j5m?r$&-z?&%srbQqM_6TADjPaRn6q+zHjHIgPdIAYCrfPw{0HY#O1XNKsI>W?< zna$B@q0RC}SJ_O}*F}?kRG1I^QeT1fy|GX&Co@wL(AW>;zjds<@#R$Lv64D|SSKv5^G3>IFfQ!o{J;@lOUodI} zj@}eumN1<~e<&%~AzJ&yY_WPi(2UjfAH&_-MiBW>m?8{=QT}orxAjE2q`5NJ;Rsgb zSs8mGY>P6o<@kUpq%RZBZMAuDooV4eEf=hTCch}&7Z70UVui@}*2$P>ls~>7OUhv^ z*Rk7Vc}ydr1Zs{%@9mqB+m2l83FhB@@ig4**rw=R@);J@YjAvQh>>L05KxDXz{qa{-HuqJ`zWuknxj1aH216`b zc|DyQl_xkp6v26s?KWY$NccLtVpN6YDQUbukWpmQ6@y??UcG5R?i!OhJ$6B#EV1k5 ze8FeDEZ;BYM=*24NmfjANB|^)K^tR#`pqa?*204H^V%@#xh^muuDV+;oC`@rY~E8X z0iL4ewtD!Go$xqh4)iz|F}z)*??e2y803)gqBzxGXc)h!@1m)(fvOK5{u1uMSnygrzrs)pqb)%hXV(%sEqKs&n5!6 zO>o@j90Hdp!2Flj6S&clo#wN=xgK+EIJ|EjwWaal&21XjnDHU*D=>oKj3ef|oJ}Ly z5+1Sw(aHi4@Jdmo=d*00hnxR?{PdgubMkPL#R@Fub6xFbwSH6Sv;3o6_%jR$4ypuJ zu=*+o8{Tjxcl1eCYxQW0vAB&dWhP0i^f*x|?6N6emRIzJYneu0?*X9pIrr>;k7OP~ z80$CcF*i@hZq>sGDm*Clv>BSF$q?~qSF}58*90VGd6HAb9yljZ!)FQ1oA%r%(CfT?fW;#x((|nUP z7nWnEp}ajKI!x{|hIa0)j-Wp=be&Bu=vZxs4ZGxNHhGUZIU4=ZO<bx(z+TMO1*|CQnrCM#^#K&4$}WCUZYvB5(^0dvpbxQX9Ko{Eiscof<|*o9y12m zYWN`F_ptSVC-~6ck>X}BPcShmKcLn@8VxH?s#kyVXTq@&?75JM9xX_yVDNw`$&&|o zGcdK|{xT8N48Ij|^B6?9iF2*#a3oPqU}@**60^Cp#Iy768FC4#?CenPL7{=UzWJdH zn$d|SbrSbY(ns!8ekwD9vwYTwqo|@UpJg2Q;#)WV!t}#N?P>&*(QlxB5kdazSLFv? z+e>D_MnYw@TFbMuS~nvF?;kWHr3+`7vbV#H5r6K)Ff%n!YmBsi#HW(-+&er%AD}y5 zc%Wjr0%%j`JAUDODPSK5N)a~^RI0GEqn@zqDKK>IYS#svTX|C!;Orp$A%^e@hI5&H z92#mq+E@y5RS;C37eif4M+TtO@oW-#Gj9mTPFYX}v+V`sB~Ww~i$O1?L&#c0hGd#9 zrb}g-XOB>(g(`{FVVJx%OVOzi?pk^f8rw7A4(;&z%3ik5)kCv(n5RVcwd3mhDoEpF z`yF-)p*GNe#y&whOrAWZ^D2VLtt~lD8Sf^-_WlHH_lQs{WuIW0aE|hmlNS0u@dSS8 zDz8Opqr15dIgy_D2A(W*bxIgyWPXZ1yXCUhwLFyxy644$}~geJ-IZyvK1AQLi@ zFf)Lt62KTP+oKjmgpK(W+ArJDV&U&g@H3!|!HTOj?p4E{c*D-ck;a`Yz&O}Rf=s=R znWYH9n_DT?X2W1cx8xWclBG~#=H-0Y7q@m9*Z5@DV!=i#D7YO@;)=`021xQj&@*ID z1&i2kLrnI;IizzZ>|~({;5(vs$k=pI?hBqLZ1CMqVwVoXFmoe0-42Hz{0=8J zipR7`0Q6v&U`HZ?_)z6YVLns|^D z)6CsddR#P0sg*jUBC58V>_N0Y@- zq0;j#uiokzJvD!k&%vkn28S#RMAo_%+tpT!oI|?et{2i3dzjCoB{+9A7!+*lY(I0p zN&52Lv|7hg3o}5+bF1{6pom~+g}@3(mUdp9Bgr1p6{)FMSHUv|xw8UdsY`kkomT7> zs4%`Vi5BR0617g#W=sP{f$bH>zl^r`TWGt4c?hE58`PB@^w0D`%7A`Do zw2S!{#B+Sg4vjgINdsB33daUb`KE46VCfyQb8)kse-&P}uf)9cr(e&wGG41~gC28t z0*wGSYau8%I23xwG!m#ICr|sE{1gM`M5^0wBe$&YefoOQ1jD5QD{Otii1i^>u7?DV zVc!>THLT$;j!7@x7JBIvH}#Ncv0@)w<1hy$;9C_raNdQp1SE6yuv*(;osRMGzC*)lnvJd5&E|#*#0!W+bvrFcbj4{J#_@4+ry z_A|kUv`N9q$A+pW$gs5kyMG{*$eAP)@D$ zlnTZN=3Q?n-Q(|(vpW9!_R7BYQ&9)PAGV!m{SZjOl?nXt#F(y$jqp>qSDjjQDcjW1 zt^DfuVQhF=SU0*tfb6X$rnPxAe5E!UJG(Zc=Um%Qj9t3%X+&K_=(Ueww@x}Bj4^$C zs2(R59`9a?9SiFbA6x1=K<>;D+}pV2-^0VR)Q$Db;-kc}&`32`C9nTHD^If-t9G9u zI*KkB_@;<8Ye%DS#RNrk@5k?Iox-~k#d*!@{dVJvk^h4-qLDv$0uwlf2_N#@+*EI> z@*J|?05yR7o2r~HCR&~2z)7l@6rgP7npQ3is!L*+(>t&JLS}j93jK1A+OfCUN07F+ zykeD=ZYB{1GAA7+JH;yp*X2S@^J(#Qqu_Gr2=SR@i}QahCC>7x;&sVFl?!Z$yc}Y)3%O2qXYdHFeW}w5)p%`(v(P#!-T`D%<5Wsk2r10U z_u-}FvuvWffE*q6t9CutPiJ{nCjhuI27r^~#jLz~T~R$pnk7^bbzAy$<+d{tI(fZU z!>y{kzR=TWJdgtK4h3-&q4=dLpFXurB(EhG9GF}P0TC8-I3G{*>EQ&ERJvqe@D+(g zyL2ktknVy`vLY}?T1|n)8CaQNf@mCGtQSM>+xBFxakq7Iq%Y@Xl~sc1b8!<1x1`{1 z67EKu|9+f(^Y^dboowdgM%Rt4b*p$X1OeB^o393ZFEIrV-IDWTQ4))YU~k(?;vIG#}btjD_5fo-2IXzbjt11K3V;|N>d z&t~Q1y$e<~YtBN7lq1%Ks}uEL6582v>oKz+#&$FW@yUz$WNkK=g&{7PHuzZ(dQIB|1n%i8BekC>>2)yexph^Q_mWqs*kW?(r zBWDUQ`iVz91vyKy=~UqEs|qSG6G$>V4iH;1>_E0Ky)9ke}H_~~u3b1}$!%Y0(nN4V;UajQEQfe|L9qGXX6@DD&?6zV^ zMNElX>TC!3n)E=0o zE2SC7iN{b3(aPyb`kx~$rM0)Vo+8%kWH``!R$vkk89vGRKIo6lp~>%7VXo+8l>_^# zN{_nFDW`z1nH;rSe{lJro>%#0<{mRy)cA~vA|pRt-p0&mXBw~OLD{UFWHS!L5P&Ps zpg#peBoho}iyP2E2Q|Q_dDNC7_Hi&QLs(Tf;sr1siv#B=wrvA;1X7gTbEWHu;k399Er!AV6Bd6C#6G_|@i|n2f1b_zPR=rbI^<7% z{rt6hR+ZUQ(|Htjt86mS^9GIJbHdLGdQL#MYca6nID;;Qyf%{yoKthX-`91I)%)5P z+0-1()lOhw^qe3;G8x**TfLNBcLUL)AtksmUN}RD#3b>YUDE8h&iOBr=yJ`$zrk5R z-rR!-md0N-`d2vY*9Y{pE?xXTnN~9-@cyp%Dn%-iZ9Gz)&L|H2;QU7!2F1066 zW}c_G0(m#h@Rh%e%BzLpM*;o;cBWyVD{*Qu(1{JDjZ8KmBJ3xS?uKG^BuR3%Kiu$vodGIx6MpKaDMf*tXj$)No z;zZM~oqP=N$`iR~&`ZsuDO&@LJDkKfo;MQn7?iFh&md>Gc9U3qSd%)d~o`4xNB1tgR8GnlI4@TB&PY5-xV6 zox(P_WJih0oXV}?51!cOBO`Yk@l4H-d~Wo-+>wtVCKqIPb0QyWd?MNMa6X=4VxqRd z0_QQTO^g>_rJ`Tk?jwcQkau8%@lS0+$c!_cM(i~}pJCV71_&&3+YxeIkq>u?;E{c7 zcQqu>bwdGXVFaVNTq^LxuO{H&er;l22#D=Eg?a}bJQk*Z+^SnRn3DHa373kFK+|;C z=PJpURtqz=x0xNnK9`ikmqH|YB^Haj;lol73}tv^s{OWR;wRU4nFnH`UCRe2L9agNhF zdtruyXp1jBcr&-?nfOry@mx1ssq6D?nX8G80{}9R?-g{Vo+cZ9fyw02JOO4WR?6Qq zcxMvF0jY*w4BZG5iVsgHOu#nV6RBSps(QPT5}40CR&`DnQm491I#_twKjD4%(> zWKCK=SSR0=)OWD@LGu2c8z_(9@hrl>a#Hf|tXFmuf~|AgExU?8^7rbzECCGX)uL$K z5aB)DyvU1lCSfDg@PoZoD8q9m@VN9(b7)=T_2f6u+b{wvQR3^X4KsU7CcKq`O7xhy zxx&yH`-rP=5?ts_UX*tClF=74A6vJDNL$Bor}eTb_T5O3!=!6)MqME^WmS8zSVj@e z>B@1~Q*bwLzDk>$d#hv81~a%6-bwLJ&2Bw{bDhvlJzIS(%mx)nbqS-SqY1OnL8&lz zX=t@#LFz6PwMY@7Bez{Ll^yD2GLtq>+9A~r=%CPgS|ekN7`?2n7nj;Q@OI*vuk#Ng$-w{QY9Q%#Ga!ITa(}&fbIN?d=(1T_#Tuq3hl1FmphZG{C+s;l{*b z6X71m;HM(1uGu3;TcT7LgOX*I!3J9)x+eI{6Y`aY(*f(ty}wkrSIN!e>Ez|c_9l-$ zzPZknC&J-b-iiLp@;skpvqZV43`0|z2MNS(HcPixp=RgC02;j3d<^s@8pHBgST++q zW<#Ys{Jdo}Eq%t=d!BmCRp)$(DY0j~;)#lTanZJ=yAn4BYCsHDvfdn^ym`B~yZ3zW zV0Z6$UY?}t6#%bz(g~}WOHf4!inh)t>U&kpH-a&>&JT}Z;Xsha^Tg_@^7D&k5X1ME z631AD;tig}6#5*);x#yc1zSqAlbTQGlS>rFt z%D?T>2L;=3FMgX~+3Eqx5B!@3;&t&ZB1lq78vGk<+P5LVdQ0=mO0lKlxQ8^ODhK+i zvwXKKra5RcONQG{_SSj31*5;D=W*BFXI*wl7G2^s)Z6XC-eKRm6ECA_Pe%O$9Cquq zuw=XaG780V9&YOlE&}qd;CDwypI>`eW5q;G*aqm=iIfA0Ww4a)Vcrt5bCgAsy)uY1VV!eB)JgVj85EK;U$~_ zeZOeG1?EHZ3llrlv7oGVCX@jwtlYzPfw*ufgQPTw>hX+p{-P>QhOr3q%WB*9Q3CeO zPY;g@=kniMHG*qz+*(qkFE86Z!b@{bykqpLD|X*S1~9g9Ky{l-Di;2$o||q$#^l<< zjTl|xl+iLoKQ?y4Xc>;L@#RMpAKLoPna8?bVd_|`&oP4_Q}Uv1ut_o7K66ItXc>u7 zK)&ns_`G6tjTec@S1Da1<$g355qFi9Uu!~^o7dg(#g@-H^9Hp71)^F)+eZ_lSt&%&Feo`6xqaiA$tPBPc5#zcW( z8w`cywFVn9wii7S0?emR0&L5DGM1_@>=PZ#KJ{jNOQ2)3TRLjQw`H7sKaBH|-U8qK zvz75Q?7&(sb%#ee5Q9o>ESK&h_g}e+3Ia_e5oGnuT{0Ahq0cmf=X#2)(Wxe`Fm4B? zb5sPHR6}1XTjDIUrXWRJCvpkM0dZycH2JKotj{uWc+_lYLks9Q_&Pwf1=B*GXN{g7 zg)?wEUPuu-9NZs_f`N_bUDoX$qBj^J+6=@e=j9;rdtb=iHY@`WRwl=LLYkc{O_Si0 zvk)_gh2iPj+KzK%5UFCrY|S$Bt80zCqJLN^uPAJLKj(Gcl-2qnoi0w1SCo;WuI?)D zZN4_fxl~nB=6qe3qcB<9j@Q;-6cpDr{R;C{`X@ULEy9;O&F3dWzE!4Q>aa~Qw!N*3@mzSOe5 zfQ-3_@@{#FpUg-=Jw9&dLXOVD zS?NhJ3-Yh}y7m$dA9a=pCfe+pto|Iv()TSz;~-l&2Ohv>Pjj*P+);+;>R~|7^aJQ# zp4p%-;s!RECt|t->WWv%eOYRX&7Kngu!lnuiD^Eyh|I|1 zYO05}+WU5AFto89#a3W_#o-TyxFngMkoS?SZ3N#7ysl^Wba(mPr2)fzrEPK??i z`bF?FgoxOB9Z~u}+h<&Up7R%RePb#$FTMXfAKB^IkOfqEBqr8q(4MKyb*NU(TG|5F zgN*Y9nv0nZ9>660nyfmGW9>>6LHg&q6@AHVSeRJztpZHwR~$wrUWQFU$NGg;NLGKm_i?1Qdq`&>7yXPr&Q z9V!*mA^bkMOogyDveU9^-okmIj%xIJk}acGT7XKFTnAc&szdj)SLTtcj-t2a^*K0bnt<%gP5*3cj4^-t}#sW=|R~X0h1VtR@Y(+2EZ;9 z7bfX%Bs$*$WIGxsHyJpYPE?)2>n^iv#+U7?qcu-4VAcU1%yq)hV^B&yK`HDkWy#)C z{CPfp${_Ql&%fJueQg`WrZpDb?R);7pl}e)lV2BkWlwaRUUJn7ce?)Aj7e!S+Y)VSyid2rqBZV>&`f8w>sUUi-J0`%3Jj9JK?f8BqO?W;BvOMt`JX%*pL}$`xg4P zoD`So&z`^K)e`(VjNkrq(kEruaQW*d?2GTjy(>7d<%13No%Bes+IT~sRSordHv)TH zWo_e!;9MF({z_iUvuzG&^yrnKn&^_9V_hOGuKJn_I<7&G@9V=5C%Sw!rJZ#lSO5(HnFFM*kC|c-$lKB z+M4A(R_vXF)dIT8>fK2DZ;5qE!HiF+EjG#-D+r}gxxgkYT` zb21I=v1X99&ZLRwGwt)caMm~c(qS)>`}J;UGco{#X=ryof|r>pJvF6o0QxIoMKTM9 zHMhaKtu6aol3PJU#|On^VW-DuJ;`=wYF;OU^Abnn71{(PGGO21F;ZZ_1#gR=G6N}^ z>Uo-2Vtrs8kpa8MYH8S%+9e{ak0Ti8pOKbe^MEX%Cd0_ZqvcN?`04xYf^cIJy2 zD{O4^n8y}_`+v>gDl>Q|v(;rr@(=5W{RrDi+Ts#c{n@5KiERPfpE*G2IRy$PYC?&x_g0_peVnIjQkktw z29ZADRy^}$-B3JkrT2jR*$g58vynPQH{$6`Uxp#M!E0Qs60@=Sug+ZH-qa0WN_17_ zTiJZMW=qhsnt5_K##t-!vy9ZKNX8 zy#EAqnxDaAGWe#RR%hjGR$hUVi5@csjfIcoGMf+U#c4gM@_F;3tSn1yjDIQf0_3sl zA)$N)PthgduvFTf-!_lmeU*Ll_Y?Z((Kr9|DwMdu;VYcH0t6^FaJAj~NIL!rgTS`E3L1IRUYFww**Hl2Q^?Nt6j4^1%XvA@ zPe$q#|DTrS%R#n`CfHSuvV2Bw8Tc`Rm0LPB!Uj0VC3{04qoG}DYl!(bc}J(GL~!hD zBuv1+DM#69&$MG1IeBqg3=&|h#KX|EKL_KAvznvy(uqaM<2SR|8HQPd(_l_Ff+`lw zX$%JG!0PPmuqJ-k$z|6TBq|U2mn6|{k6>WaE z)cQpfMh2DkHM}fQ#u6M?Ud!#Pxe%cTYb|Mx_D!0-Q%c7D8d9vrj|LX3k<0C22c0jM^ziitrhz_RYYgqxIuruTQhF*f*?Qz4WOm z?wzG{Z%@czb(7SgWsM^_9Jq&bJZBp`DNY%_gMS{U{{>V+G^uduY25(y$v#X)ETnTT z#>eD;*BoweA%-VO7k7p>m_>p}3&-Ux0~!7%w;4Z6ZzayxRLiG@w{`p%4+oONa~SZO zO35}(mKa-uf1_Gruh>f2E@Zu*O@`*!a6$Fl%apc``(W};+q@yR+KsgW4J;RCSTW(WmqNTx|y z>DTl6xjr)#77Ly-1iUtvIO{EwNJ%M7h-Cl1&;H(#1?`(Ujd-f-<&8mOT>+l~B z{h#0XKY#E4-1L9`cmL-D|K}(F=fC?uA#aoa+huRUPbM>yPg0kp4#ScC{SEv3_w4UY z_V<4mMBs$_Ui{#{KC!?5o&Eh_N8oHSXr&PT#0c@=foWxz(NdQ*@SJfvYb=YEVo{E!@C)PTkZweNIzk5?L=rH0W-(wJI32$P-dd~e1@U^AS# z*Wdvt=U|rH8HDcqPL{CkgHX_AeBqjTDSyNDs?*dy9u$Wa9N4{1WtJ5+?#iR6e#}ya(x5UwE#6^&5I}18SAMX#oe+?hrLo zeKs7tsqzAY#0DEVcT@1hSz%G&v{)*;$OUzQV0Rbt;ohx6J!r8Y)xq{jpJHaKBP z6tF#LPlJu|X;$kx*(aJ=M(Pl4=Ogl=+ZcJ#>7)JC0b&L=ry@TT@4_CuSz!k&C8;lCG#bsf>KicpBUgR@IRZC;g zl<~pT^D4W9%!&A-y@Tcp%>2_&-yK$8aU>Q<8W(!l*uuwOBQudc)AI&{XYWTw01b$I zN(f?Q{-QuTu5S479$fAEe`B9~JPGl`Vwnn$5$RkPw8#b{^{gx*KMd~_57VsItiCQL z-Bmq@`tIcDU5&lVd2^kpCO#qQeVcG3?QK?{u2kagt`0={AiKW&w^0anz<8eJ;OXoG zv!RR>&ucPLB3p?7Qb4W0l{{22?$R$m1|g(^pJ4~tb0(|TV-dZ~PW4Pr`wedfm0?73 zvh3$*VI;YmkHa$x*oRg@BID-dC zU;MOkyv?-w-tgY)9dyklI~!v>)J7Gk^3b?AEh{==G#uf9eNy%zvM#En}a$F{+u{QWse@ z)zcrB^=kQTtYI5O2eyOZN$hDAlC_U0;$61rSDDeS5banV*m9|5$ia3qc83zvsiJtH z2CpZrwp+?EEZrKHO0?(Y3P>1eHj&wqK9(ZswMqUUNf!cnkv9&3ks2)KQ`*o2x|n+6 zgp|lHX^%Eirc_nUv-jG>xn5FJ#K4csYaq(X>>-%xJ26G_Nr)1)F7_01`h>ROg}_3S_=r_91m z4&{B0=i@K~su_wY1*i0ftRgMNY3F<~i{^F%2(c+Ear#IVr)4&c8dqwE9^{TL@;X&5F=f6H?sz?04KzL7!sfz#8L8urk+FeqP0 z1a%I}raZ;twpXeLl$4qsn0!4klztky=PZPpJdS{NX%-~eRHZeQXf_^)eiL>S4j(V- zJ#e{Arp7Eg4L8vC0k?08ew?v^*o`$|J7$t4_43pmjydi;p*TocYz7*=i?q>~BDhpg zTavUGJfx>)zG32+>U~Z|7l_=B)7|I-tG}D-&>O%kP0@>6cR0yGw|B+a)5Vn-0e|1);fS?2=VkRRymRc05ohisMae z*vK`Yyq9(e7|Ufyu6f$-h}7Xb`uP6rn4zzM(fh7 z+`Ls8+<$4Q?1`~Pq{O$<$uC^l){osYT$HMjlixf4t*f6(DR-bRB$vNyX}$Srx4-c2 zhknfU{D#6S)}y{HS$4SVwxU*Y9j@q0c?&NN%mQmmw8)lQ;G^@nKe{}H?80P$pkvGC zCPqa+%TBLnWieJSX5|$;cUJ34%@<9r@8~Ir((~9NoVXb|N1f`)SBIM6GwS<8~aw=Y3ndalrA&1w>uL$ zv;aO(%D~jMN4zMj*FBxvpTP?2cWi#pEp+15{vJhWe~Y-{Y4Bup1fkSQ34YeiXt`e_dJMOiiAqwQBcb-?LZTVz6x z$cbRASJ1 zba$53^|Nw%ZDy1CfLdrdYF7urY08Qbwv`kuAVeLxd~h6QgBC}kpiTGW>bu`ZSCq=F zWavbE$}9SVx&1bZW-jRP87cE!U8F;FL%88c`n&VBbdy;I`zQBL(s17?TrJv zThtcsY8nK-60&Z?*rxqy&H|v2=&314MN6^8AuT+CG?w$k@MLuDOVh+|+$Sh%#ieLv z4p{JL;c@GDkEKXm8unRlCRU?(LjMSmc{-b|undD)Z{uknRhFHf!^6Lq^VtgF3{3|9 zo58P(tj?KB4!^c5z*OBD%T1RloWi0mR1jz_t+s4?cE!g6v^&(K+$T%j%n_9I(bOF= znk;9Vg8lp(I5y$E!@-uaPP;*sUAdnuydEJWj7YC^lf^1>icXih$%Lz$TYj?6Ye3Ao zomx6;UV3kF)3 zFYwBSjuuNSpmpC%nrKH^7O%RlAuADa+h*%y9-QP+JiybrOJC~qY;xUd@(K)Ev2^SZ zeDTfUPz1J+@iX~IL)+>t&8DN2C9rci8*2tXJbCo^e|D|4|MAbi*5pkpXFR97QEm3( zqyo8S7LgA6iKH5mvj6i7Q?^G!iD(KEDJ9r(sqdcAtzD+Y)A~wmQjK4BNMmZgJXmro zfHTFL6kBCJRgcmsA(-9hBFl?5jc>h3uO2a!BNcSKTC8JxbNamU^@FZU__8M^Qw>$x zh8urx4;vjRPvo9tXQBN9opu7IN_vHi-UhohQv-F$tjBFK3q{@9eLm^z^G>B<>>^mh z)a`(}AI3 zg`LC)oXCSG!Y#rZdER|0u2+eu69!)_*$9lXA6hiZ+wBYmI&H zm-=Jte#dvjfAX{CUk?9u>C!ij!*J~f{=~l={%d0W|K;#soo8xl1XoIO`T6&r5Nl@1 znx@(@m3_Ux9R91He8_v0<%K`R*k-?Bvm4n=@|Lao-8BE8Hoq5DUsTz7vhMfanVN<( zeG$yiFYr7N{91N+3W#RuFTX9nQN!Vd<3%YXLmZNII7WALpos6CEoHr~ zE3cvTzHO9U;Vxr(qSCKG&t2x(bivvoo}wh2H(45wMCdbrIs8|Wn@bn-8s=0tGzT1C zmJf%5}8bCkeI_skv%BJ2Qjm zywTbLnt@35kh$eaL-5OWwQf8d-V5iNa0a{e27#BLLpR-f4XiWDB2Ds+qjZANI>xpH zeGb(x<{k^pRrV`Qd}>>2tWq#jzWZ>oCEDx;B^zrksRX~oL=Z$xO~WcpNHW`cEJ`8> zI{-&oq=N=mccIS6g&%aT9{=dGz$V&bmJdMe5qD_HS^01y?}yfFCJApl$heH)(J>}S z_}$|WW0R44Y(u3hx_ios;)|)`w8=Yo1O7I!Dx~0Jp>A~5b~BKA>Z->3jEpHR+$FfT z8w|bcq|xHQy#>TLD1%nT5v3tt3W%fNC{vr3j@(N_mY3MkM)7Uu_-;cF6WU4_3~onV zV*-o9%}4Az!%&*H>|;QIiOYRno9cmm41=(b0wAW4KW`hak$rp(rO5^Rx2=@c%I@3^ zC+xrg$4tKPG`K`OznnXZR07{)&sqDvrZ@808b^nI3Lx__&aBVka?*xdt8sAk2f=3L zReuwdkknGP|C5`jAP+%na2h^~oRcib~>N8SC> z?{ydo8{RR(9jY}W744?$uq3s-W29|JaW~d&y#@K2!dh+WK_JpN56@5G8yUTWAAy#2Hlgi z1|H6=uOw0vB>;vxZ=@{pj6W@>nzk%A2{Nb6MzPu+#}F;Q)7GEy+vAdg+wuLU-1t7t zN9ho4nWW&rwgP)`pYQHDT;(4&Al%Z)CA!Mn1mr(!Udb~PQ@~wy9z_XTcCA-D60F|O5k5N5zlY`F9^JaNPB;t!BS2v{X-5q7yiW%`@sG9~+@pe}1|# z1lp7mk5|XG6cIW_8u_i8GAU|!TzlX_CV;y4rD_q?)auSy68qF`{Eftwyz}xo^>{NY zHie`Io_bPqg^OnfZv6QBdT_m|SWm&U5zh4+_q~rkWRSC&0{aT}2ml30EE`+;G04^K zs`Yy$R-k3#Haf9e%{kZ3rlw185n5)Ezdv>P_UIXLyIUQChA&3jrNN( zT|p#Mx~BLXc#{o9`#0^hl&5cU$MKqXBh}l|X*VJw&QGyVFtSuZUN-A3Il{j{&wIt< za;?K$-yPS#nZj0nvA8snmr~-6WEsUIC5y!}f&i_D1SjGeNH`;chvo3#D4H~zdg`%U4NW$oA)1}E zNpzgz1zRe)yX{HnBzFg|LNo8J9Z_of@2=xO-%HH$m+32__)0qe8p&Gll3@R+;Z^Q> ztaxi<_1hsxg7mZMu^JldR7=wJM{d{6O?Rnssz1=iFbQ_XJMAs04)}Xf9sLF2 zN6{UQ)(i_iC*11k75XOpW4)zvPQCzQZRr)x%IXP{X?4@3z5q<9 zXs1%U$5Dm<^arx|j#A8|Z6~Y$JSUkTc@}%?6DNl3yF8Wo=I^afaO?M*RP0{)G}d7I zkf$)y_PSdC@y5kdJOL$pGWnmR>Iax4$v%x45j*MRTF+ z{O`Iqez(au=Ce4pz1ENr;CGVP{_}Qa7X23!IR^4)mppc5+F4Os%%(gS3koX9Z_~k%MwlbHoFQg z_#fL_qZ(Se3#0b&ccm<>EybaZc)88lZNA;uLWkhFwgPCl!9df-hTPIedLEuJLPrc8#CKH70)jZ*_!{7yaV&>}@eJsSy~EE%yXh zN0DJ;L9ar;57|sTRD;m7lwW4EFFB0{jmwrhXze&eowqzk-Ew1a^}$6}x3~Iy@8f(q zJpbb9;B1zii-0>@5(K_cy+k}5p+Y^kXDBK3m8pZ$Vr=>uQR)%|Zf>A=OqC62v7Is9 z+MKn9`r{lY@jo`d`b~eF+kS4$PwYs2`lJSfoBxonW9xJM5gtE4IHs9<_ys>=VZ3QpE^Axz}gxZZWXob&5SR+Ju8D_Oin;qFX)2~dVY&MB2HFuXxv~Apx zX~>Q)RK|Gd0lR?YueZeS%DWVM{!)wg*7bT_^bX!476Gr$7?pmNFbV~52db6cD&=q6 z_o&|E%^MMymSM1dewxp6cZXqf6BB(LPO5y~h(NXI1+r!Ff7HocXJ(FGxn4UP@t1ul z{ELu$g|}BhhW+!tZkx`J9acyPP~Nbum@cW5)jIF$eztdGLvi_B+IepyHB*l@-)<#@ z5$uB8{YTJ-xgFZ_5tuEOr#+Xr#dF;`cj2{CF1>nEwAJ1mla^}gqxl zYHYh!d5!57)$r)`^VdojXJs|fsH<$GP4z;XT0a*&Pda60n&+F6Jg{EMDYmJztz0}H z&R-Vy;W*)~o?crqiDG-zd2u?U8$xp$h`AWp=n2ht7in%*ClD`*Sadz=-TgD@j#8&} zQ}w>Q9&B;9BQJAT`E}T-L3F{uO-T0}M!zSCTZ7MR`W|xva-V!&)%Nqg zcPNQ{x3FK76~1@V&w4%6XN|v|5e2@P;?CHNF5k2?AMkr7$#vQmDqQqndWGO=a$9@6UlRBTIQXQl$1-+gdI4bA)_^8`S(30h_cRW+!Xphm}&RzT^^V$kax&(Vxm6tjt+m! zYMVu&`(n1g8*;!>V5PEhusVE99I?K=_P@y3otU-%RmN7->RX_bxK!seH?4tG;&zTl zieJ%5AeFjf-Uo$%)6!eG++4#=Rd7SNN4Bxb(|U>dIQ1IZm$;sHC~rQ}1aMDmSq*^rM?m8x#ji>oM~mx(=UKF*I&qyGdp0vEkOVV{~^VYS)Fy^h@ zvY6(6t8Zrj)Mu?CXYxtdpP;4Mzfm=ne8`(7H?sgkZlKbG$hhxzmLI-L68nS7J29Ix_iFD(SFI{vb&mFR0d}1A&XETlE)LF_OuWgWC-~ zt+D;x=Bqm7I}!>kGgdE+Hy}ph`JAoBYbLnuz`=_tuHAmx+*F56KAWjUt*1YjkM13f zeu}b9b^0GmdoVTApKtP(M|`a07istZ_i~W5993DOVIBssig#xSy-=^V7eD_ELH9bwt|nJWcx&ZlTWk5nYaTn`7W@8x85ttk~9=ZK(T zO3Ji8RKU!?@!JHS$3f;#f;5<=62e(#ATgC=(1 zno!S5mixbAnh$NXZo{r>M zUNl{~;=5Rmi+ctuJ|Fdb<|$4I-y1}zEuuhM#UmO^_>_;dpEJFur}s1a+`FGcNQUq) zdleyMvlFQTv#6EjKIxT%cVZ`dDtxDJCXDWooAn-z5N6DU_t?jRD8#fID0&Ain#CD&L3sqfhe97?2P z_1`^doO=Lm`ML|`7E+ssZ1!7Oc}?NUD=w2!g$j5O=~akr(y-H7yOSPE z#nRgVw@C#>)?Z8mwQ5SeRg&hL2o=cNE$@H!5pk{Rt{>9h^5)`5 zALsY;u7Kz~IMOUf@{<{yx{uGlFEb37aYmcToP}XPW@*TsMdI zRUcQ&4{1vkU;P0-!4TeL%?13;|NS|y^QNreXI)-uhu4wGfVQadpAS&&-i^8UGz9bP z=B9d~^-P^rTD$2k;qp|aFUt>F!Tlz@^sZ((Wd1x;DDZ2{=Y|tktEMDKld?F=&li=( zFzIU?5H7O%^%Ws##siYIgwc+v%!-2TdptS@(o7!d>M}0?nhBDz0W$w@F@WXB z>yrc>l2@f3L0rDujee%|a{vKuo0&Rrm&{@}hRmg+@kWpd%9I_y*VisTohjnIglJ*( z^6Rs|>gyU(VP(~M&F^kjx?ap0T%d~)DHYpk7v3#Z!XMLL^Yi0M&)WpJ=#{e z`VMPutA`JBi!1?nudi!QZ}>F?V8{81N$TgS?UoBn;{tV9U#z!ZsJCC#cdkD4_zRC( zf-wML>f>KdPT*ppxN*lQMO6ijz-g9M`b}96=VkqPq~;}vNH*K>c|3!w7vEqZ{r%|^ z6->(6;<@TA{H$4J{PDYhpWQr5mUrfVaAzvxohvz%b zQMJ%7%gL}Pr+S35RC{@JAHI%fx~)*t6tF$GMis}aI@g2-7^@Qye@X3r4dRq^24*hi*9v9+YZhFUQA4fUORPT`ijHChF*;Cto5{KqI=Yw=76O->aJnXx9lRFwwjWz5+^#HmULl9!%W^VOlZ$)? z-#mA_LSO<}91+YGTavl9W?CfOf>I!!Xdfmv;M$FAio|p6;>i&;c^A5vazZo0oaG;1 zfDU^7vYb$cig?}AdY+Y_TiRv1Z74RA^l>Yjbgl2yV~<@>wxb@0gEq^*rL8LwEd|@) zSqpPIyvX5&N2{wbvJ+#T40iPJ({nJ0{A>>04MtKp=e75qjEY!is9OFY95M(+D$F;0%|bV*4s zgJL=;wIUIEk-=*LHvu`mdNK~JUpPyN?@>=2TV_H4YSQB z+;w;}3_R^I(Ii;O?lvhu8%Dvk zvW~>fOl~q%$ITf+F|%RBpqY`Yi+rZlkf1$yfK9Nn+nUVQ7w<&zG{%!0P3N|l&Oqeb zKewX_<{(d1>^a`d96$QPf2Wns-ft)76-78r7c6xv)XfCyI{31fPqRi#)p^Mt(8d$d zefT6>xQ4$jvO2dW7o^*xB`w$%ZOlYxjjmqmv*uYgd4H5o-qRu0GJ#al#mNb`&HoJ; zSUEuG96vfqDwV!I)A^JA2mF(22$roxORhwilLw@kSu!@yl4e47C^{*8K^9&Om~ z$U}8~2FoNYS342*Qq@6xSf+${e}ux33JiPGayHAV-9Tf2kcK)pY3e*ZGxj-79QDw#bVMNEigQQMV~RjQ8_U`41iF#p zv{fS&f|TM=>Z`n&Trknf#-7z$CEh{YN|vKNzGpT6BA2Wr9wlD}3ojKes}#I!zU2s= zOjIY+)JfY8WyOdIJI|U6e%PavB>3CZ4!mJ;F?NmN0@EwHy z5?>M=>q(Cl9CBnH0E5TGvb_Ra)Kl+_nSc%KVt@&;fsrMhGB-Hy%V3k=%82+iAe z0p|Dbi>k+_|Iq6bRD&Vii(V=Oz)g1gw|v@MY^gsQFXmBpT5lPVfB+Y@{@X>~P-M~; z3GI8Net93*R>~~~)vhYA&69!kU$BqOP4%>%=QKǡu;c{RRgt(9=DK0qU@eZ$;2 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

$BL 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 Cz?(Y& literal 0 HcmV?d00001 diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json new file mode 100644 index 0000000..9c09049 --- /dev/null +++ b/packages/toolkit/package.json @@ -0,0 +1,65 @@ +{ + "name": "@vite-pwa/toolkit", + "type": "module", + "version": "0.0.0", + "description": "A module that integrates into your build process, helping you generate a manifest of local files that workbox-sw should precache.", + "author": "Google's Web DevRel Team, Vite PWA's Team", + "contributors": [ + "userquin " + ], + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/vite-pwa/toolkit#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vite-pwa/toolkit.git", + "directory": "packages/toolkit" + }, + "bugs": "https://github.com/vite-pwa/toolkit/issues", + "keywords": [ + "workbox", + "workboxjs", + "service worker", + "caching", + "fetch requests", + "offline", + "file manifest" + ], + "sideEffects": false, + "exports": { + ".": "./dist/index.js", + "./types": { + "types": "./dist/types.d.ts" + }, + "./generate-sw": "./dist/generate-sw.js", + "./inject-manifest": "./dist/inject-manifest.js" + }, + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "types": ["dist/types.d.ts"], + "generate-sw": ["dist/generate-sw.d.ts"], + "get-manifest": ["dist/get-manifest.d.ts"], + "inject-manifest": ["dist/inject-manifest.d.ts"] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsdown", + "prepublishOnly": "pnpm run build", + "node:test": "node ./dist/generate-sw.js", + "tsx:test": "tsx ./src/generate-sw.ts" + }, + "dependencies": { + "magicast": "file:magicast-0.3.5.tgz", + "pathe": "catalog:utils", + "pkg-types": "catalog:utils", + "proper-tags": "catalog:utils", + "recast": "catalog:utils", + "valibot": "catalog:utils" + } +} diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/toolkit/tsdown.config.ts b/packages/toolkit/tsdown.config.ts new file mode 100644 index 0000000..84e6a58 --- /dev/null +++ b/packages/toolkit/tsdown.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: './src/{index,types,generate-sw,get-manifest,inject-manifest}.ts', + platform: 'node', + banner: `/* + Copyright 2019 Google LLC, Vite PWA's Team + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/`, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9008de7..8d8fdb9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,6 +122,29 @@ importers: specifier: catalog:testing version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) + packages/sw-tooltkit: {} + + packages/toolkit: + dependencies: + magicast: + specifier: file:magicast-0.3.5.tgz + version: file:packages/toolkit/magicast-0.3.5.tgz + pathe: + specifier: catalog:utils + version: 2.0.3 + pkg-types: + specifier: catalog:utils + version: 2.3.0 + proper-tags: + specifier: catalog:utils + version: 2.0.2 + recast: + specifier: catalog:utils + version: 0.23.11 + valibot: + specifier: catalog:utils + version: 1.1.0(typescript@5.9.3) + packages/workbox-background-sync: dependencies: '@vite-pwa/workbox-core': @@ -1816,6 +1839,10 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magicast@file:packages/toolkit/magicast-0.3.5.tgz: + resolution: {integrity: sha512-z7mBYVtGx5aFIFqOa6lBYuyKJqYUsvS85uB8zMDcBIZTtN3j7xlYh67g2WmHTZJbFX8i74T2t9g4KcZF9U5XGA==, tarball: file:packages/toolkit/magicast-0.3.5.tgz} + version: 0.3.5 + magicast@file:packages/workbox-build/magicast-0.3.5.tgz: resolution: {integrity: sha512-z7mBYVtGx5aFIFqOa6lBYuyKJqYUsvS85uB8zMDcBIZTtN3j7xlYh67g2WmHTZJbFX8i74T2t9g4KcZF9U5XGA==, tarball: file:packages/workbox-build/magicast-0.3.5.tgz} version: 0.3.5 @@ -4115,6 +4142,12 @@ snapshots: source-map-js: 1.2.1 optional: true + magicast@file:packages/toolkit/magicast-0.3.5.tgz: + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + source-map-js: 1.2.1 + magicast@file:packages/workbox-build/magicast-0.3.5.tgz: dependencies: '@babel/parser': 7.28.4 From 70d365c472696036ce69a5f65b16c07ea73e49af Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 25 Oct 2025 00:09:11 +0200 Subject: [PATCH 15/20] chore: refactor packages --- package.json | 2 +- packages/sw-tooltkit/package.json | 48 ----- packages/sw-tooltkit/src/index.ts | 0 packages/sw-tooltkit/tsdown.config.ts | 16 -- packages/toolkit/magicast-0.3.5.tgz | Bin 125714 -> 0 bytes packages/toolkit/package.json | 65 ------- packages/toolkit/src/index.ts | 0 packages/toolkit/tsdown.config.ts | 13 -- packages/workbox-background-sync/package.json | 54 ------ .../workbox-background-sync/tsdown.config.ts | 16 -- .../workbox-broadcast-update/package.json | 51 ----- .../workbox-broadcast-update/tsdown.config.ts | 16 -- packages/workbox-build/package.json | 8 +- packages/workbox-build/src/types.ts | 8 +- .../workbox-cacheable-response/package.json | 51 ----- .../tsdown.config.ts | 16 -- packages/workbox-core/package.json | 49 ----- packages/workbox-core/tsdown.config.ts | 16 -- packages/workbox-expiration/package.json | 53 ----- packages/workbox-expiration/tsdown.config.ts | 16 -- .../workbox-navigation-preload/package.json | 47 ----- .../tsdown.config.ts | 16 -- packages/workbox-precaching/package.json | 44 ----- packages/workbox-precaching/tsdown.config.ts | 16 -- packages/workbox-range-requests/package.json | 47 ----- .../workbox-range-requests/tsdown.config.ts | 16 -- packages/workbox-recipes/package.json | 49 ----- packages/workbox-recipes/tsdown.config.ts | 16 -- packages/workbox-routing/package.json | 53 ----- packages/workbox-routing/tsdown.config.ts | 16 -- packages/workbox-strategies/package.json | 44 ----- packages/workbox-strategies/tsdown.config.ts | 16 -- packages/workbox-streams/package.json | 45 ----- packages/workbox-streams/tsdown.config.ts | 16 -- packages/workbox-sw/package.json | 145 +++++++++++++- packages/workbox-sw/src/_types.ts | 22 --- .../background-sync}/BackgroundSyncPlugin.ts | 2 +- .../src/background-sync}/Queue.ts | 2 +- .../src/background-sync}/QueueStore.ts | 0 .../src/background-sync}/StorableRequest.ts | 0 .../src/background-sync}/index.ts | 0 .../src/background-sync}/lib/QueueDb.ts | 0 .../src/background-sync}/lib/QueueStore.ts | 2 +- .../background-sync}/lib/StorableRequest.ts | 4 +- .../src/background-sync}/types.ts | 0 .../broadcast-update}/BroadcastCacheUpdate.ts | 4 +- .../BroadcastUpdatePlugin.ts | 4 +- .../src/broadcast-update}/index.ts | 0 .../src/broadcast-update}/responsesAreSame.ts | 2 +- .../src/broadcast-update}/types.ts | 0 .../src/broadcast-update}/utils/constants.ts | 0 .../cacheable-response}/CacheableResponse.ts | 2 +- .../CacheableResponsePlugin.ts | 2 +- .../src/cacheable-response}/index.ts | 0 .../src/cacheable-response}/types.ts | 0 .../src => workbox-sw/src/core}/_private.ts | 0 .../src/core}/_private/Deferred.ts | 0 .../src/core}/_private/WorkboxError.ts | 0 .../src/core}/_private/assert.ts | 2 +- .../core}/_private/cacheMatchIgnoreParams.ts | 0 .../src/core}/_private/cacheNames.ts | 0 .../_private/canConstructReadableStream.ts | 0 .../canConstructResponseFromBodyStream.ts | 0 .../src/core}/_private/dontWaitFor.ts | 0 .../_private/executeQuotaErrorCallbacks.ts | 2 +- .../src/core}/_private/getFriendlyURL.ts | 0 .../src/core}/_private/logger.ts | 0 .../core}/_private/resultingClientExists.ts | 0 .../src/core}/_private/timeout.ts | 0 .../src/core}/_private/waitUntil.ts | 0 .../src => workbox-sw/src/core}/cacheNames.ts | 0 .../src/core}/clientsClaim.ts | 0 .../src/core}/copyResponse.ts | 0 .../src => workbox-sw/src/core}/index.ts | 0 .../src => workbox-sw/src/core}/internals.ts | 0 .../core}/models/messages/messageGenerator.ts | 0 .../src/core}/models/messages/messages.ts | 0 .../src/core}/models/pluginEvents.ts | 0 .../src/core}/models/quotaErrorCallbacks.ts | 0 .../src/core}/registerQuotaErrorCallback.ts | 0 .../src/core}/setCacheNameDetails.ts | 0 .../src/core}/skipWaiting.ts | 0 .../src => workbox-sw/src/core}/types.ts | 0 .../src/core}/utils/pluginUtils.ts | 0 .../src/core}/utils/welcome.ts | 0 .../src/expiration}/CacheExpiration.ts | 2 +- .../src/expiration}/ExpirationPlugin.ts | 6 +- .../src/expiration}/index.ts | 0 .../models/CacheTimestampsModel.ts | 0 .../src/expiration}/types.ts | 0 packages/workbox-sw/src/index.ts | 25 ++- .../src/navigation-preload}/disable.ts | 2 +- .../src/navigation-preload}/enable.ts | 2 +- .../src/navigation-preload}/index.ts | 0 .../src/navigation-preload}/isSupported.ts | 0 .../src/precaching}/PrecacheController.ts | 8 +- .../src/precaching}/PrecacheFallbackPlugin.ts | 2 +- .../src/precaching}/PrecacheRoute.ts | 8 +- .../src/precaching}/PrecacheStrategy.ts | 11 +- .../src/precaching}/addPlugins.ts | 2 +- .../src/precaching}/addRoute.ts | 4 +- .../src/precaching}/cleanupOutdatedCaches.ts | 2 +- .../precaching}/createHandlerBoundToURL.ts | 2 +- .../src/precaching}/getCacheKeyForURL.ts | 0 .../src/precaching}/index.ts | 2 +- .../src/precaching}/matchPrecache.ts | 0 .../src/precaching}/precache.ts | 2 +- .../src/precaching}/precacheAndRoute.ts | 2 +- .../src/precaching/types.ts} | 0 .../utils/PrecacheCacheKeyPlugin.ts | 2 +- .../utils/PrecacheInstallReportPlugin.ts | 2 +- .../src/precaching}/utils/createCacheKey.ts | 4 +- .../precaching}/utils/deleteOutdatedCaches.ts | 0 .../utils/generateURLVariations.ts | 4 +- .../precaching}/utils/getCacheKeyForURL.ts | 2 +- .../utils/getOrCreatePrecacheController.ts | 0 .../precaching}/utils/printCleanupDetails.ts | 2 +- .../precaching}/utils/printInstallDetails.ts | 2 +- .../utils/removeIgnoredSearchParams.ts | 0 .../range-requests}/RangeRequestsPlugin.ts | 2 +- .../range-requests}/createPartialResponse.ts | 2 +- .../src/range-requests}/index.ts | 0 .../utils/calculateEffectiveBoundaries.ts | 2 +- .../range-requests}/utils/parseRangeHeader.ts | 2 +- .../src/recipes}/googleFontsCache.ts | 9 +- .../src/recipes}/imageCache.ts | 10 +- .../src => workbox-sw/src/recipes}/index.ts | 0 .../src/recipes}/offlineFallback.ts | 6 +- .../src/recipes}/pageCache.ts | 8 +- .../src/recipes}/staticResourceCache.ts | 8 +- .../src/recipes}/warmStrategyCache.ts | 2 +- .../src/routing}/NavigationRoute.ts | 4 +- .../src/routing}/RegExpRoute.ts | 4 +- .../src => workbox-sw/src/routing}/Route.ts | 4 +- .../src => workbox-sw/src/routing}/Router.ts | 4 +- .../src => workbox-sw/src/routing}/_types.ts | 0 .../src => workbox-sw/src/routing}/index.ts | 0 .../src/routing}/registerRoute.ts | 4 +- .../src/routing}/setCatchHandler.ts | 2 +- .../src/routing}/setDefaultHandler.ts | 2 +- .../src => workbox-sw/src/routing}/types.ts | 0 .../src/routing}/utils/constants.ts | 0 .../utils/getOrCreateDefaultRouter.ts | 0 .../src/routing}/utils/normalizeHandler.ts | 4 +- .../src/strategies}/CacheFirst.ts | 4 +- .../src/strategies}/CacheOnly.ts | 2 +- .../src/strategies}/NetworkFirst.ts | 2 +- .../src/strategies}/NetworkOnly.ts | 2 +- .../src/strategies}/StaleWhileRevalidate.ts | 2 +- .../src/strategies}/Strategy.ts | 4 +- .../src/strategies}/StrategyHandler.ts | 4 +- .../src/strategies}/index.ts | 0 .../plugins/cacheOkAndOpaquePlugin.ts | 2 +- .../src/strategies}/utils/messages.ts | 2 +- .../src/streams}/concatenate.ts | 4 +- .../src/streams}/concatenateToResponse.ts | 2 +- .../src => workbox-sw/src/streams}/index.ts | 2 +- .../src/streams}/isSupported.ts | 2 +- .../src/streams}/strategy.ts | 6 +- .../src/streams/types.ts} | 0 .../src/streams}/utils/createHeaders.ts | 0 packages/workbox-sw/src/sw/index.ts | 13 ++ .../workbox-sw/src/{ => sw}/workbox-sw.ts | 0 packages/workbox-sw/src/types.ts | 13 ++ packages/workbox-sw/tsdown.config.ts | 47 ++++- pnpm-lock.yaml | 181 +++--------------- pnpm-workspace.yaml | 2 +- tsconfig.json | 64 +------ 168 files changed, 374 insertions(+), 1299 deletions(-) delete mode 100644 packages/sw-tooltkit/package.json delete mode 100644 packages/sw-tooltkit/src/index.ts delete mode 100644 packages/sw-tooltkit/tsdown.config.ts delete mode 100644 packages/toolkit/magicast-0.3.5.tgz delete mode 100644 packages/toolkit/package.json delete mode 100644 packages/toolkit/src/index.ts delete mode 100644 packages/toolkit/tsdown.config.ts delete mode 100644 packages/workbox-background-sync/package.json delete mode 100644 packages/workbox-background-sync/tsdown.config.ts delete mode 100644 packages/workbox-broadcast-update/package.json delete mode 100644 packages/workbox-broadcast-update/tsdown.config.ts delete mode 100644 packages/workbox-cacheable-response/package.json delete mode 100644 packages/workbox-cacheable-response/tsdown.config.ts delete mode 100644 packages/workbox-core/package.json delete mode 100644 packages/workbox-core/tsdown.config.ts delete mode 100644 packages/workbox-expiration/package.json delete mode 100644 packages/workbox-expiration/tsdown.config.ts delete mode 100644 packages/workbox-navigation-preload/package.json delete mode 100644 packages/workbox-navigation-preload/tsdown.config.ts delete mode 100644 packages/workbox-precaching/package.json delete mode 100644 packages/workbox-precaching/tsdown.config.ts delete mode 100644 packages/workbox-range-requests/package.json delete mode 100644 packages/workbox-range-requests/tsdown.config.ts delete mode 100644 packages/workbox-recipes/package.json delete mode 100644 packages/workbox-recipes/tsdown.config.ts delete mode 100644 packages/workbox-routing/package.json delete mode 100644 packages/workbox-routing/tsdown.config.ts delete mode 100644 packages/workbox-strategies/package.json delete mode 100644 packages/workbox-strategies/tsdown.config.ts delete mode 100644 packages/workbox-streams/package.json delete mode 100644 packages/workbox-streams/tsdown.config.ts delete mode 100644 packages/workbox-sw/src/_types.ts rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/BackgroundSyncPlugin.ts (95%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/Queue.ts (99%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/QueueStore.ts (100%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/StorableRequest.ts (100%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/index.ts (100%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/lib/QueueDb.ts (100%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/lib/QueueStore.ts (98%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/lib/StorableRequest.ts (97%) rename packages/{workbox-background-sync/src => workbox-sw/src/background-sync}/types.ts (100%) rename packages/{workbox-broadcast-update/src => workbox-sw/src/broadcast-update}/BroadcastCacheUpdate.ts (98%) rename packages/{workbox-broadcast-update/src => workbox-sw/src/broadcast-update}/BroadcastUpdatePlugin.ts (94%) rename packages/{workbox-broadcast-update/src => workbox-sw/src/broadcast-update}/index.ts (100%) rename packages/{workbox-broadcast-update/src => workbox-sw/src/broadcast-update}/responsesAreSame.ts (96%) rename packages/{workbox-broadcast-update/src => workbox-sw/src/broadcast-update}/types.ts (100%) rename packages/{workbox-broadcast-update/src => workbox-sw/src/broadcast-update}/utils/constants.ts (100%) rename packages/{workbox-cacheable-response/src => workbox-sw/src/cacheable-response}/CacheableResponse.ts (97%) rename packages/{workbox-cacheable-response/src => workbox-sw/src/cacheable-response}/CacheableResponsePlugin.ts (96%) rename packages/{workbox-cacheable-response/src => workbox-sw/src/cacheable-response}/index.ts (100%) rename packages/{workbox-cacheable-response/src => workbox-sw/src/cacheable-response}/types.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/Deferred.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/WorkboxError.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/assert.ts (97%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/cacheMatchIgnoreParams.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/cacheNames.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/canConstructReadableStream.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/canConstructResponseFromBodyStream.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/dontWaitFor.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/executeQuotaErrorCallbacks.ts (96%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/getFriendlyURL.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/logger.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/resultingClientExists.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/timeout.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/_private/waitUntil.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/cacheNames.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/clientsClaim.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/copyResponse.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/index.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/internals.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/models/messages/messageGenerator.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/models/messages/messages.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/models/pluginEvents.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/models/quotaErrorCallbacks.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/registerQuotaErrorCallback.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/setCacheNameDetails.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/skipWaiting.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/types.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/utils/pluginUtils.ts (100%) rename packages/{workbox-core/src => workbox-sw/src/core}/utils/welcome.ts (100%) rename packages/{workbox-expiration/src => workbox-sw/src/expiration}/CacheExpiration.ts (98%) rename packages/{workbox-expiration/src => workbox-sw/src/expiration}/ExpirationPlugin.ts (98%) rename packages/{workbox-expiration/src => workbox-sw/src/expiration}/index.ts (100%) rename packages/{workbox-expiration/src => workbox-sw/src/expiration}/models/CacheTimestampsModel.ts (100%) rename packages/{workbox-expiration/src => workbox-sw/src/expiration}/types.ts (100%) rename packages/{workbox-navigation-preload/src => workbox-sw/src/navigation-preload}/disable.ts (94%) rename packages/{workbox-navigation-preload/src => workbox-sw/src/navigation-preload}/enable.ts (96%) rename packages/{workbox-navigation-preload/src => workbox-sw/src/navigation-preload}/index.ts (100%) rename packages/{workbox-navigation-preload/src => workbox-sw/src/navigation-preload}/isSupported.ts (100%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/PrecacheController.ts (98%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/PrecacheFallbackPlugin.ts (97%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/PrecacheRoute.ts (92%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/PrecacheStrategy.ts (97%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/addPlugins.ts (90%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/addRoute.ts (90%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/cleanupOutdatedCaches.ts (93%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/createHandlerBoundToURL.ts (93%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/getCacheKeyForURL.ts (100%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/index.ts (98%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/matchPrecache.ts (100%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/precache.ts (95%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/precacheAndRoute.ts (92%) rename packages/{workbox-precaching/src/_types.ts => workbox-sw/src/precaching/types.ts} (100%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/PrecacheCacheKeyPlugin.ts (98%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/PrecacheInstallReportPlugin.ts (98%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/createCacheKey.ts (94%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/deleteOutdatedCaches.ts (100%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/generateURLVariations.ts (91%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/getCacheKeyForURL.ts (95%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/getOrCreatePrecacheController.ts (100%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/printCleanupDetails.ts (94%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/printInstallDetails.ts (96%) rename packages/{workbox-precaching/src => workbox-sw/src/precaching}/utils/removeIgnoredSearchParams.ts (100%) rename packages/{workbox-range-requests/src => workbox-sw/src/range-requests}/RangeRequestsPlugin.ts (96%) rename packages/{workbox-range-requests/src => workbox-sw/src/range-requests}/createPartialResponse.ts (97%) rename packages/{workbox-range-requests/src => workbox-sw/src/range-requests}/index.ts (100%) rename packages/{workbox-range-requests/src => workbox-sw/src/range-requests}/utils/calculateEffectiveBoundaries.ts (95%) rename packages/{workbox-range-requests/src => workbox-sw/src/range-requests}/utils/parseRangeHeader.ts (96%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/googleFontsCache.ts (84%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/imageCache.ts (88%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/index.ts (100%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/offlineFallback.ts (94%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/pageCache.ts (89%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/staticResourceCache.ts (89%) rename packages/{workbox-recipes/src => workbox-sw/src/recipes}/warmStrategyCache.ts (93%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/NavigationRoute.ts (96%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/RegExpRoute.ts (96%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/Route.ts (96%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/Router.ts (99%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/_types.ts (100%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/index.ts (100%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/registerRoute.ts (96%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/setCatchHandler.ts (92%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/setDefaultHandler.ts (93%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/types.ts (100%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/utils/constants.ts (100%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/utils/getOrCreateDefaultRouter.ts (100%) rename packages/{workbox-routing/src => workbox-sw/src/routing}/utils/normalizeHandler.ts (89%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/CacheFirst.ts (94%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/CacheOnly.ts (96%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/NetworkFirst.ts (99%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/NetworkOnly.ts (97%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/StaleWhileRevalidate.ts (98%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/Strategy.ts (99%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/StrategyHandler.ts (99%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/index.ts (100%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/plugins/cacheOkAndOpaquePlugin.ts (91%) rename packages/{workbox-strategies/src => workbox-sw/src/strategies}/utils/messages.ts (89%) rename packages/{workbox-streams/src => workbox-sw/src/streams}/concatenate.ts (97%) rename packages/{workbox-streams/src => workbox-sw/src/streams}/concatenateToResponse.ts (97%) rename packages/{workbox-streams/src => workbox-sw/src/streams}/index.ts (95%) rename packages/{workbox-streams/src => workbox-sw/src/streams}/isSupported.ts (90%) rename packages/{workbox-streams/src => workbox-sw/src/streams}/strategy.ts (95%) rename packages/{workbox-streams/src/_types.ts => workbox-sw/src/streams/types.ts} (100%) rename packages/{workbox-streams/src => workbox-sw/src/streams}/utils/createHeaders.ts (100%) create mode 100644 packages/workbox-sw/src/sw/index.ts rename packages/workbox-sw/src/{ => sw}/workbox-sw.ts (100%) create mode 100644 packages/workbox-sw/src/types.ts diff --git a/package.json b/package.json index ff22441..745b683 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node": ">=20.0.0" }, "scripts": { - "build": "pnpm -r --filter=./packages/* run build", + "build": "pnpm -r --filter \"./packages/*\" run build", "lint": "eslint .", "lint:fix": "nr lint --fix", "test": "vitest", diff --git a/packages/sw-tooltkit/package.json b/packages/sw-tooltkit/package.json deleted file mode 100644 index 9eb6b91..0000000 --- a/packages/sw-tooltkit/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@vite-pwa/sw-toolkit", - "type": "module", - "version": "0.0.0", - "description": "This library takes a Response object and determines whether it's cacheable based on a specific configuration.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/toolkit#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/toolkit.git", - "directory": "packages/sw-toolkit" - }, - "bugs": "https://github.com/vite-pwa/toolkit/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "workbox-plugin" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - } -} diff --git a/packages/sw-tooltkit/src/index.ts b/packages/sw-tooltkit/src/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/sw-tooltkit/tsdown.config.ts b/packages/sw-tooltkit/tsdown.config.ts deleted file mode 100644 index cfa94bb..0000000 --- a/packages/sw-tooltkit/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/toolkit/magicast-0.3.5.tgz b/packages/toolkit/magicast-0.3.5.tgz deleted file mode 100644 index 245c69aff8f5214f3aa59a9005171fc5ff1047ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125714 zcmV)3K+C@$iwFP!000003hce_cN{maAiAG@&ioH_zpT?5cUrb*CU;kglC@=dq8VHE zT1w{L)zkGYbyHHst*TB{wPj7L_qV?TfIk3|RbAbZJ+t@Re2CRW5(GgI1OX77XOs8Y zx!!!azq@yExcA>a$A6C=J^J>Ke^l`AqeqXze;+-0{OzCAqd)xV+eeQdfBWsDKPve5 z50BMaZEl|D&Bfw$JSi_X zv!Xd$d=F6H=;|`B>%1&fUaJdT>C*EHConTxDuf&aZ!WuRK%DMP94AJZrA9N+T{=U6+$QYxGo2%gN$W7fsechiCar z*J{{YXf-%AEe0Fd%T#Bx-`&d#1%!!*l0A!@{VlrDy0Sj9^%P(`& z1vE#Z)xW#flxk7y5u!R$m*q4+gMT%SY`!?1<@Lo#O>=;Kx@h!B)$kD~U<4!CEGt#( z+3a`sCgnWWwZh?g{IC*WKL?^VhDh8XHYZRhQW3FM86z z7pQ+$&SvEmjBZjE(;Nm>Z^6VJU1*h^mLD{ZhZv(Mn|wk<$7z`R$u&jwMK+tMQ*EfG zrz$U0#)nh^26dAaO`grvysWUxaHwNM;m197`1-}s-=4nRQ~QVN&D+;M??2yrt_DvJ z)&AjNr2e*l^yBNFj?~|tzJ2@j;OO7f>lfP`!TpyL%%?u^x(NVczv*c z@Z#g(X&)b7it`>#go`O{ZV|GbB-ULXCq_x5-9pdKNp z{`TV@e1jgJ9;m0gNBgf2U?jV*502hG-8~wqqt|bbT-(3xAMTCR)3^JFK$915U%wjt z?jF$e^$V!8e*g^+_6Q8nTZw6T{T6@!bhzgrs^@!8U+y3L`4Af8C~fWW@9zDhh2DSt z=l+|U{U0RK=7pYtI2liVsqeCn?*0GqlRy6H+hG6y!@vCD+yC1Czr=rEFKVr5qx*XM z-n~g#)Q!rEss5;TRHc7iJAmz!+a|Y~OR=i*l;B&g8e?XI(C;iT>s?n|~8D z{$Horsh(}lv#Qn=l-xU86u952sn+vXuxGx;eQJ1_o#&IRZbqua4;$*YdrIYJYS>)Q zb$Mn!tDT)4HK4x->cIp1`Tow1Di*UDHB;)WtkjT(`d(kd?!>{ue^h9@{W+U0wAyjM zkKgO-lWkFz0N9H3OH>1719&?-yqWJDVK#ZO(6;dn17b!T!{P&%rFQ&KA_$gl%*#U3 z!GCSDgDpm4Ol|;5&uYzJ2vRCR2svo@|INJ{#vJFmd9Kg0#jM%;2&%*DIcg6M4MMfj z&7vxRl4tq(i>kalD5u)h8Eb4>kEaA_WBcBX=wa8j`#G!f>~yA|>&YyuP~!`_;!_&} z9)AS{M=}obsVpRh8Ay5aK@$%0|_TIRMtvEw!5! zMcEkTl9aIbc`*g`$@HoS3pM=ew{FC4HvR)U!HEJ=6E_ppp6f|whrB;!{2#+AvP+yd zwz5DrD8{Jcui$^D<@DMP9_R+O#z-RM!UVAgp7j{?sjmQ?AQ<+o=KKo%u(EMJWj*fu zNdevu>cP0MUXmhV{UUF8zm}TtUF}3w84^c_hgp-?XSuHTKF%wR>hwVU5Vz5;SZzh6 z+fmmOn1mg}T<5ybRo3WTSP6*i7|K(vV6Yw&2LWZZ|2~A~8@#<)b({u@qd5`<)X!G9 zNW<TCBm^~R@9?JSp+ z+xP4wj4!dPT7CMYhQX&Db$o(VVbJ$w;nLH-EVFw*lnlI`Ls$fSBp!_govtwUy z!tfEFchqk;hV#PhD6Z|Pv>ojx*>w7I-sm^8#d%&ZO3(4b`ecOx8Y(arNo)amQR}Lq z{`ZTfOo%|*jAG<)G<;_bVVQq$vGQPj`ia@iX0y)8c}Jhr zj#Y5%Z=gEpx5YWg)8E9PMhsbpnYvN7e0%*RMQto)_}=M=oj3Zj9>X|kqaWrQYUg{E zBdHPbcZ~M1+2o#1e#r4M=JkB{!LAW4nOjH_M)luod8mVZV7^V@ymV?wa;CHM|W`WQT-`PQ+scUL{Xa)SlT}B~w~2;i4ZKz$wd^qU=tG zKY#ck+Qw$OIBzbXQEzM3_hPkHN?2mFkDiX-KF_ z0e}s)wIYNKyVc&ahjDRcP&@JrvCXUb-IS{N-@aO~cj5U}eDPJ|4LZ4N`v%Lljvl*< z0G86o+I2PGG_9PdoffTS`#+)MLfHSEk@s%<=(7Jme*E}PkDoja?f-xHmp}cN{r^k+ zm$d(bJ>uP2%{>6^mVRnNX8u>%oHvSW|5O+4{%_Fi&ue%11K#CDqpNammh*0TInNq3 z1Y3;NhBY&8(`NfM*_6MAg0Ib~=xZ?cYxRTD)^@43l-^c1iOvqPOI^>iiKgy`n(Lpd zbhqJf+_qK@AhOy~BuU4UN@tB8qMd!)cfmCT?y@jIrID>Teh)_lef<6;G?CH&3pe5& zi-&V!xq54+c*P4jr(x3UqF@G z*2(LG#pNk-t*JgP@B^B@z4zePVRMlc!7&sd;Yr?VWy25T_JK3M0X&{u=*fFq@Zf>M zzts0T3IJNo%%Sm64WMkG9%6aQl5Zvv=t9_RZmJgs(^_5WN<+NLRGsQc2BKh?U6#eU zDw_*ksY+)xDksfF)_h%KaK>d>Hx`hd>GP~;)KzwkQ9BStf}9E!_2s;|ep*%8wJHm( zve{L3jbSS1`3J2sFwt5#8P>`RhzObJF@7GvY8Y%evJO4NTee-yX6AfClo|LgI~yLK z$Yy9D=xpIk2)+@VzB}8{@tikV6jObc7kZj%=PEgQ+zFl6+xAMg<`Ruhk@!X|Ex(lc{>0ce$06OvO?ikU9&T8Y2p7q@{&i$b zMYK+h4b?-SS!HLb%k278A8Mo5lV)2F$PM~6J&^Ts@=(eS00)j2P&0S zex4WE%m7t8teN%s`Px&iTZ0Q%wmx>2NOE_mN!Cm*giPc{ISCEV)%*iEP($>XH#6+-}ImvWPE8|U7&UMvXhX@RO z%K`L`9K3}L<~lU{cr+z=He=A7lLKZ6_i^a;t0!LJkhpC7iU(zNnay1FVR`z?2x25g z%CAx%GNFFp;92G0lr9#Rx`IO+4ZRpwtkcA~(RUu5;`tKzF~Z8`n+lQNlQb*-LdwML}{#+21ykRaJO!UH+i zN$*1_09PSQ70-%5!WuEc7|rsEFv`!ahaW_|hWDb9-xL~wwlOVrtuXii7c)vwQa|!q zaA_bQ;Un(yug~0GMpV!5q*rrUA*A$;3l2 z7}Sfo7l24ShK>D`N(06lLhX?nK-|S(qy`R&fz!XdQC3lb#=V+)_+TvxW2BcnKMK_sc11 zzWTm4mXeTouF^FfY`l`;fq3D5-_-N_41j9Y7@*J$h%Y9w9tfhOImwTH6h8yH)$Jsw zax}leAH9g~`|rWLzq#dZ`WpXI=EV@;Y@md=>CMMhKBDp2X@L|J#|nPCLGI>V8Xm`A zLUFVz8x)|`olC=G`V2?W6N8YMp!k$MPdxTs*6IH)i@Zb&g)h9Jr z&itqd`=)hysfRcS5)=WsM(X~35)oZT8xQ!7RtWRf+WB}>>1jU68r^SN$fq^8joK%O zg1eNBT{B-@L-0D-zk5xez_aHp+Rj&zS`tms;EM~d=lU$UE=bV=tBwzKG@NIaM6gm0 zXfo`dodiG4YOGWbM%IhdTDQC^@C(%4{Jr{L*7T%)znL<>8DN{6YTpDlsq(6DXpPmI za=w_+J);H8DmXf_S^jtOt942|y02&wygm&u?~XGkiR0A)&$Qa*;KOB5rvJPalJU4{glayl$o1$2LQjO+QIED&uD(;rjCmx;S{Djx; zL?(>9777iY%>c8rvf9fg7sH{YWMuOt%Agne^%<-7oqEKvqiPsa?CJyoC>z3#U=j0b z3o=BD=1A&`k;^Y2G!Zuop~s|E$Fz3iAbC68i=tC1Q-=ettD22*N8P_qe=gH5|8n^H zz?k>JyyLLOQ`(jiBE-US>$g1pg|7p9kO!Nb19ZaH0biYHRr9Wn=J_xDMZW=PVsxj9HD4Dl3q@;s7nI zesmJ16E=>_pHsd22tQc`(l}tSz--2HyYu8`ii=w7nD*seJc!_mUs9PSnbf$dw5m+TN_F(X$mm=|@?kmg`B1T=VjC`q4~ zk} zxfeY(2*JvPD<;UkQw?`Eui>POks(d#Tz49;1O)pGY^%cFFcUBRn7%c4fXv*d$;^UQc{b?dJIF) zB-49slnNp&QmHjODozASg~MZjalfx6Ih;EGApl zH%jBk-j1{l-fFl{n`l4XAe*Ki@AJA7*5K z3kD{6B*0R=!N@ciK7R6tKW;#fL%jZc-65_xZ^JWDl`JgLY171>TaJTA!Va{>Sd>bT ztF|oL(Kq6pEyrB+mDpp;aS(llncgxyV&a}~KDXSIo3JpNv@Jigw)SkcWoC}PIKpr7 z8KN?x$d;o&m#kL)Vp!=?TXQH^>285HSTPG{|AU#ZlErE_Ki#@`GkYQR!Gq5IWFu7i z4A1&~?L=yK)08!e3B75X4M&JZ8(4{=(Rz7c%XGds>-CBKK61|_pPTD5W_R0Uq@)Ga z1}-FmS^WJZ+t_>%Qohxf*3;%1YpaZ!gL$PdGf?qn*VGei8;imN6oE1008xi(>SSjl z9&Lc4w5vqC*Bz#a#v7QdofNxs7QO!=I7bR+@qtZ)F*w9H@0aC-vL%o~KdSYntn=v9 zA|0O{_VbQ|Kys86KB)2HGL6;*bR@{sw{H%c;;N)a1j*HR8W4zN!ek(8aDah6x^Q)2 z5$QVI{X>wQbBVcqxSJubd5Jk#nqS-D>T#Z?7Al8>@&O#f0T6L8G66hxt4nz_I9AY? z+7Se!Ju#aSjt~uq`{M`>P1)s0W!3ozx4HADAF_N#*ow0}3`dG!4q;;>;$F|3#=q0z z*>UqOp=Yz1gT2Jyt{$3-sr3*zZ;r9%iA1i|bHl8<64{Vc96@Vz&8l;TkiQvB-8gG( zx^Ym6QG6o;D%)4;QrC5M4p0U~X&{y^44K`(!1FYmi-|x$$iQ|Xl0 zY!OH!tF$kQ1prebJZUOrW8D~RcW{6iZ*uxcECmreOE^m4*=G4p1bKQY(qD#ZOSU++ z$#KMSt_`=!&_I^k$E&(iJUxKO!lVmw?cxHg&L_l_&h-@#sl`XPltP27Y2ZQ$Z0${H zEUpctm|bP3Zy|#_`p1{R$|(e9Qz$WO8?mBwr#W^sGB1~7#AU6%LqdhjY6WL3HNhds zj#_R2@ycw4g11A&CKM9khJEMuS1C?6T1F}dvl0@#V>F6Fo3TsOCO4wh-J;Tx8gaMb zYm??hk+Ut@l>XB&?rmo_-k~v$KedOpvH>_QN#=Y}rvCE0mA-3Ya|(>W!~2YWU_gk( zX2j5m?r$&-z?&%srbQqM_6TADjPaRn6q+zHjHIgPdIAYCrfPw{0HY#O1XNKsI>W?< zna$B@q0RC}SJ_O}*F}?kRG1I^QeT1fy|GX&Co@wL(AW>;zjds<@#R$Lv64D|SSKv5^G3>IFfQ!o{J;@lOUodI} zj@}eumN1<~e<&%~AzJ&yY_WPi(2UjfAH&_-MiBW>m?8{=QT}orxAjE2q`5NJ;Rsgb zSs8mGY>P6o<@kUpq%RZBZMAuDooV4eEf=hTCch}&7Z70UVui@}*2$P>ls~>7OUhv^ z*Rk7Vc}ydr1Zs{%@9mqB+m2l83FhB@@ig4**rw=R@);J@YjAvQh>>L05KxDXz{qa{-HuqJ`zWuknxj1aH216`b zc|DyQl_xkp6v26s?KWY$NccLtVpN6YDQUbukWpmQ6@y??UcG5R?i!OhJ$6B#EV1k5 ze8FeDEZ;BYM=*24NmfjANB|^)K^tR#`pqa?*204H^V%@#xh^muuDV+;oC`@rY~E8X z0iL4ewtD!Go$xqh4)iz|F}z)*??e2y803)gqBzxGXc)h!@1m)(fvOK5{u1uMSnygrzrs)pqb)%hXV(%sEqKs&n5!6 zO>o@j90Hdp!2Flj6S&clo#wN=xgK+EIJ|EjwWaal&21XjnDHU*D=>oKj3ef|oJ}Ly z5+1Sw(aHi4@Jdmo=d*00hnxR?{PdgubMkPL#R@Fub6xFbwSH6Sv;3o6_%jR$4ypuJ zu=*+o8{Tjxcl1eCYxQW0vAB&dWhP0i^f*x|?6N6emRIzJYneu0?*X9pIrr>;k7OP~ z80$CcF*i@hZq>sGDm*Clv>BSF$q?~qSF}58*90VGd6HAb9yljZ!)FQ1oA%r%(CfT?fW;#x((|nUP z7nWnEp}ajKI!x{|hIa0)j-Wp=be&Bu=vZxs4ZGxNHhGUZIU4=ZO<bx(z+TMO1*|CQnrCM#^#K&4$}WCUZYvB5(^0dvpbxQX9Ko{Eiscof<|*o9y12m zYWN`F_ptSVC-~6ck>X}BPcShmKcLn@8VxH?s#kyVXTq@&?75JM9xX_yVDNw`$&&|o zGcdK|{xT8N48Ij|^B6?9iF2*#a3oPqU}@**60^Cp#Iy768FC4#?CenPL7{=UzWJdH zn$d|SbrSbY(ns!8ekwD9vwYTwqo|@UpJg2Q;#)WV!t}#N?P>&*(QlxB5kdazSLFv? z+e>D_MnYw@TFbMuS~nvF?;kWHr3+`7vbV#H5r6K)Ff%n!YmBsi#HW(-+&er%AD}y5 zc%Wjr0%%j`JAUDODPSK5N)a~^RI0GEqn@zqDKK>IYS#svTX|C!;Orp$A%^e@hI5&H z92#mq+E@y5RS;C37eif4M+TtO@oW-#Gj9mTPFYX}v+V`sB~Ww~i$O1?L&#c0hGd#9 zrb}g-XOB>(g(`{FVVJx%OVOzi?pk^f8rw7A4(;&z%3ik5)kCv(n5RVcwd3mhDoEpF z`yF-)p*GNe#y&whOrAWZ^D2VLtt~lD8Sf^-_WlHH_lQs{WuIW0aE|hmlNS0u@dSS8 zDz8Opqr15dIgy_D2A(W*bxIgyWPXZ1yXCUhwLFyxy644$}~geJ-IZyvK1AQLi@ zFf)Lt62KTP+oKjmgpK(W+ArJDV&U&g@H3!|!HTOj?p4E{c*D-ck;a`Yz&O}Rf=s=R znWYH9n_DT?X2W1cx8xWclBG~#=H-0Y7q@m9*Z5@DV!=i#D7YO@;)=`021xQj&@*ID z1&i2kLrnI;IizzZ>|~({;5(vs$k=pI?hBqLZ1CMqVwVoXFmoe0-42Hz{0=8J zipR7`0Q6v&U`HZ?_)z6YVLns|^D z)6CsddR#P0sg*jUBC58V>_N0Y@- zq0;j#uiokzJvD!k&%vkn28S#RMAo_%+tpT!oI|?et{2i3dzjCoB{+9A7!+*lY(I0p zN&52Lv|7hg3o}5+bF1{6pom~+g}@3(mUdp9Bgr1p6{)FMSHUv|xw8UdsY`kkomT7> zs4%`Vi5BR0617g#W=sP{f$bH>zl^r`TWGt4c?hE58`PB@^w0D`%7A`Do zw2S!{#B+Sg4vjgINdsB33daUb`KE46VCfyQb8)kse-&P}uf)9cr(e&wGG41~gC28t z0*wGSYau8%I23xwG!m#ICr|sE{1gM`M5^0wBe$&YefoOQ1jD5QD{Otii1i^>u7?DV zVc!>THLT$;j!7@x7JBIvH}#Ncv0@)w<1hy$;9C_raNdQp1SE6yuv*(;osRMGzC*)lnvJd5&E|#*#0!W+bvrFcbj4{J#_@4+ry z_A|kUv`N9q$A+pW$gs5kyMG{*$eAP)@D$ zlnTZN=3Q?n-Q(|(vpW9!_R7BYQ&9)PAGV!m{SZjOl?nXt#F(y$jqp>qSDjjQDcjW1 zt^DfuVQhF=SU0*tfb6X$rnPxAe5E!UJG(Zc=Um%Qj9t3%X+&K_=(Ueww@x}Bj4^$C zs2(R59`9a?9SiFbA6x1=K<>;D+}pV2-^0VR)Q$Db;-kc}&`32`C9nTHD^If-t9G9u zI*KkB_@;<8Ye%DS#RNrk@5k?Iox-~k#d*!@{dVJvk^h4-qLDv$0uwlf2_N#@+*EI> z@*J|?05yR7o2r~HCR&~2z)7l@6rgP7npQ3is!L*+(>t&JLS}j93jK1A+OfCUN07F+ zykeD=ZYB{1GAA7+JH;yp*X2S@^J(#Qqu_Gr2=SR@i}QahCC>7x;&sVFl?!Z$yc}Y)3%O2qXYdHFeW}w5)p%`(v(P#!-T`D%<5Wsk2r10U z_u-}FvuvWffE*q6t9CutPiJ{nCjhuI27r^~#jLz~T~R$pnk7^bbzAy$<+d{tI(fZU z!>y{kzR=TWJdgtK4h3-&q4=dLpFXurB(EhG9GF}P0TC8-I3G{*>EQ&ERJvqe@D+(g zyL2ktknVy`vLY}?T1|n)8CaQNf@mCGtQSM>+xBFxakq7Iq%Y@Xl~sc1b8!<1x1`{1 z67EKu|9+f(^Y^dboowdgM%Rt4b*p$X1OeB^o393ZFEIrV-IDWTQ4))YU~k(?;vIG#}btjD_5fo-2IXzbjt11K3V;|N>d z&t~Q1y$e<~YtBN7lq1%Ks}uEL6582v>oKz+#&$FW@yUz$WNkK=g&{7PHuzZ(dQIB|1n%i8BekC>>2)yexph^Q_mWqs*kW?(r zBWDUQ`iVz91vyKy=~UqEs|qSG6G$>V4iH;1>_E0Ky)9ke}H_~~u3b1}$!%Y0(nN4V;UajQEQfe|L9qGXX6@DD&?6zV^ zMNElX>TC!3n)E=0o zE2SC7iN{b3(aPyb`kx~$rM0)Vo+8%kWH``!R$vkk89vGRKIo6lp~>%7VXo+8l>_^# zN{_nFDW`z1nH;rSe{lJro>%#0<{mRy)cA~vA|pRt-p0&mXBw~OLD{UFWHS!L5P&Ps zpg#peBoho}iyP2E2Q|Q_dDNC7_Hi&QLs(Tf;sr1siv#B=wrvA;1X7gTbEWHu;k399Er!AV6Bd6C#6G_|@i|n2f1b_zPR=rbI^<7% z{rt6hR+ZUQ(|Htjt86mS^9GIJbHdLGdQL#MYca6nID;;Qyf%{yoKthX-`91I)%)5P z+0-1()lOhw^qe3;G8x**TfLNBcLUL)AtksmUN}RD#3b>YUDE8h&iOBr=yJ`$zrk5R z-rR!-md0N-`d2vY*9Y{pE?xXTnN~9-@cyp%Dn%-iZ9Gz)&L|H2;QU7!2F1066 zW}c_G0(m#h@Rh%e%BzLpM*;o;cBWyVD{*Qu(1{JDjZ8KmBJ3xS?uKG^BuR3%Kiu$vodGIx6MpKaDMf*tXj$)No z;zZM~oqP=N$`iR~&`ZsuDO&@LJDkKfo;MQn7?iFh&md>Gc9U3qSd%)d~o`4xNB1tgR8GnlI4@TB&PY5-xV6 zox(P_WJih0oXV}?51!cOBO`Yk@l4H-d~Wo-+>wtVCKqIPb0QyWd?MNMa6X=4VxqRd z0_QQTO^g>_rJ`Tk?jwcQkau8%@lS0+$c!_cM(i~}pJCV71_&&3+YxeIkq>u?;E{c7 zcQqu>bwdGXVFaVNTq^LxuO{H&er;l22#D=Eg?a}bJQk*Z+^SnRn3DHa373kFK+|;C z=PJpURtqz=x0xNnK9`ikmqH|YB^Haj;lol73}tv^s{OWR;wRU4nFnH`UCRe2L9agNhF zdtruyXp1jBcr&-?nfOry@mx1ssq6D?nX8G80{}9R?-g{Vo+cZ9fyw02JOO4WR?6Qq zcxMvF0jY*w4BZG5iVsgHOu#nV6RBSps(QPT5}40CR&`DnQm491I#_twKjD4%(> zWKCK=SSR0=)OWD@LGu2c8z_(9@hrl>a#Hf|tXFmuf~|AgExU?8^7rbzECCGX)uL$K z5aB)DyvU1lCSfDg@PoZoD8q9m@VN9(b7)=T_2f6u+b{wvQR3^X4KsU7CcKq`O7xhy zxx&yH`-rP=5?ts_UX*tClF=74A6vJDNL$Bor}eTb_T5O3!=!6)MqME^WmS8zSVj@e z>B@1~Q*bwLzDk>$d#hv81~a%6-bwLJ&2Bw{bDhvlJzIS(%mx)nbqS-SqY1OnL8&lz zX=t@#LFz6PwMY@7Bez{Ll^yD2GLtq>+9A~r=%CPgS|ekN7`?2n7nj;Q@OI*vuk#Ng$-w{QY9Q%#Ga!ITa(}&fbIN?d=(1T_#Tuq3hl1FmphZG{C+s;l{*b z6X71m;HM(1uGu3;TcT7LgOX*I!3J9)x+eI{6Y`aY(*f(ty}wkrSIN!e>Ez|c_9l-$ zzPZknC&J-b-iiLp@;skpvqZV43`0|z2MNS(HcPixp=RgC02;j3d<^s@8pHBgST++q zW<#Ys{Jdo}Eq%t=d!BmCRp)$(DY0j~;)#lTanZJ=yAn4BYCsHDvfdn^ym`B~yZ3zW zV0Z6$UY?}t6#%bz(g~}WOHf4!inh)t>U&kpH-a&>&JT}Z;Xsha^Tg_@^7D&k5X1ME z631AD;tig}6#5*);x#yc1zSqAlbTQGlS>rFt z%D?T>2L;=3FMgX~+3Eqx5B!@3;&t&ZB1lq78vGk<+P5LVdQ0=mO0lKlxQ8^ODhK+i zvwXKKra5RcONQG{_SSj31*5;D=W*BFXI*wl7G2^s)Z6XC-eKRm6ECA_Pe%O$9Cquq zuw=XaG780V9&YOlE&}qd;CDwypI>`eW5q;G*aqm=iIfA0Ww4a)Vcrt5bCgAsy)uY1VV!eB)JgVj85EK;U$~_ zeZOeG1?EHZ3llrlv7oGVCX@jwtlYzPfw*ufgQPTw>hX+p{-P>QhOr3q%WB*9Q3CeO zPY;g@=kniMHG*qz+*(qkFE86Z!b@{bykqpLD|X*S1~9g9Ky{l-Di;2$o||q$#^l<< zjTl|xl+iLoKQ?y4Xc>;L@#RMpAKLoPna8?bVd_|`&oP4_Q}Uv1ut_o7K66ItXc>u7 zK)&ns_`G6tjTec@S1Da1<$g355qFi9Uu!~^o7dg(#g@-H^9Hp71)^F)+eZ_lSt&%&Feo`6xqaiA$tPBPc5#zcW( z8w`cywFVn9wii7S0?emR0&L5DGM1_@>=PZ#KJ{jNOQ2)3TRLjQw`H7sKaBH|-U8qK zvz75Q?7&(sb%#ee5Q9o>ESK&h_g}e+3Ia_e5oGnuT{0Ahq0cmf=X#2)(Wxe`Fm4B? zb5sPHR6}1XTjDIUrXWRJCvpkM0dZycH2JKotj{uWc+_lYLks9Q_&Pwf1=B*GXN{g7 zg)?wEUPuu-9NZs_f`N_bUDoX$qBj^J+6=@e=j9;rdtb=iHY@`WRwl=LLYkc{O_Si0 zvk)_gh2iPj+KzK%5UFCrY|S$Bt80zCqJLN^uPAJLKj(Gcl-2qnoi0w1SCo;WuI?)D zZN4_fxl~nB=6qe3qcB<9j@Q;-6cpDr{R;C{`X@ULEy9;O&F3dWzE!4Q>aa~Qw!N*3@mzSOe5 zfQ-3_@@{#FpUg-=Jw9&dLXOVD zS?NhJ3-Yh}y7m$dA9a=pCfe+pto|Iv()TSz;~-l&2Ohv>Pjj*P+);+;>R~|7^aJQ# zp4p%-;s!RECt|t->WWv%eOYRX&7Kngu!lnuiD^Eyh|I|1 zYO05}+WU5AFto89#a3W_#o-TyxFngMkoS?SZ3N#7ysl^Wba(mPr2)fzrEPK??i z`bF?FgoxOB9Z~u}+h<&Up7R%RePb#$FTMXfAKB^IkOfqEBqr8q(4MKyb*NU(TG|5F zgN*Y9nv0nZ9>660nyfmGW9>>6LHg&q6@AHVSeRJztpZHwR~$wrUWQFU$NGg;NLGKm_i?1Qdq`&>7yXPr&Q z9V!*mA^bkMOogyDveU9^-okmIj%xIJk}acGT7XKFTnAc&szdj)SLTtcj-t2a^*K0bnt<%gP5*3cj4^-t}#sW=|R~X0h1VtR@Y(+2EZ;9 z7bfX%Bs$*$WIGxsHyJpYPE?)2>n^iv#+U7?qcu-4VAcU1%yq)hV^B&yK`HDkWy#)C z{CPfp${_Ql&%fJueQg`WrZpDb?R);7pl}e)lV2BkWlwaRUUJn7ce?)Aj7e!S+Y)VSyid2rqBZV>&`f8w>sUUi-J0`%3Jj9JK?f8BqO?W;BvOMt`JX%*pL}$`xg4P zoD`So&z`^K)e`(VjNkrq(kEruaQW*d?2GTjy(>7d<%13No%Bes+IT~sRSordHv)TH zWo_e!;9MF({z_iUvuzG&^yrnKn&^_9V_hOGuKJn_I<7&G@9V=5C%Sw!rJZ#lSO5(HnFFM*kC|c-$lKB z+M4A(R_vXF)dIT8>fK2DZ;5qE!HiF+EjG#-D+r}gxxgkYT` zb21I=v1X99&ZLRwGwt)caMm~c(qS)>`}J;UGco{#X=ryof|r>pJvF6o0QxIoMKTM9 zHMhaKtu6aol3PJU#|On^VW-DuJ;`=wYF;OU^Abnn71{(PGGO21F;ZZ_1#gR=G6N}^ z>Uo-2Vtrs8kpa8MYH8S%+9e{ak0Ti8pOKbe^MEX%Cd0_ZqvcN?`04xYf^cIJy2 zD{O4^n8y}_`+v>gDl>Q|v(;rr@(=5W{RrDi+Ts#c{n@5KiERPfpE*G2IRy$PYC?&x_g0_peVnIjQkktw z29ZADRy^}$-B3JkrT2jR*$g58vynPQH{$6`Uxp#M!E0Qs60@=Sug+ZH-qa0WN_17_ zTiJZMW=qhsnt5_K##t-!vy9ZKNX8 zy#EAqnxDaAGWe#RR%hjGR$hUVi5@csjfIcoGMf+U#c4gM@_F;3tSn1yjDIQf0_3sl zA)$N)PthgduvFTf-!_lmeU*Ll_Y?Z((Kr9|DwMdu;VYcH0t6^FaJAj~NIL!rgTS`E3L1IRUYFww**Hl2Q^?Nt6j4^1%XvA@ zPe$q#|DTrS%R#n`CfHSuvV2Bw8Tc`Rm0LPB!Uj0VC3{04qoG}DYl!(bc}J(GL~!hD zBuv1+DM#69&$MG1IeBqg3=&|h#KX|EKL_KAvznvy(uqaM<2SR|8HQPd(_l_Ff+`lw zX$%JG!0PPmuqJ-k$z|6TBq|U2mn6|{k6>WaE z)cQpfMh2DkHM}fQ#u6M?Ud!#Pxe%cTYb|Mx_D!0-Q%c7D8d9vrj|LX3k<0C22c0jM^ziitrhz_RYYgqxIuruTQhF*f*?Qz4WOm z?wzG{Z%@czb(7SgWsM^_9Jq&bJZBp`DNY%_gMS{U{{>V+G^uduY25(y$v#X)ETnTT z#>eD;*BoweA%-VO7k7p>m_>p}3&-Ux0~!7%w;4Z6ZzayxRLiG@w{`p%4+oONa~SZO zO35}(mKa-uf1_Gruh>f2E@Zu*O@`*!a6$Fl%apc``(W};+q@yR+KsgW4J;RCSTW(WmqNTx|y z>DTl6xjr)#77Ly-1iUtvIO{EwNJ%M7h-Cl1&;H(#1?`(Ujd-f-<&8mOT>+l~B z{h#0XKY#E4-1L9`cmL-D|K}(F=fC?uA#aoa+huRUPbM>yPg0kp4#ScC{SEv3_w4UY z_V<4mMBs$_Ui{#{KC!?5o&Eh_N8oHSXr&PT#0c@=foWxz(NdQ*@SJfvYb=YEVo{E!@C)PTkZweNIzk5?L=rH0W-(wJI32$P-dd~e1@U^AS# z*Wdvt=U|rH8HDcqPL{CkgHX_AeBqjTDSyNDs?*dy9u$Wa9N4{1WtJ5+?#iR6e#}ya(x5UwE#6^&5I}18SAMX#oe+?hrLo zeKs7tsqzAY#0DEVcT@1hSz%G&v{)*;$OUzQV0Rbt;ohx6J!r8Y)xq{jpJHaKBP z6tF#LPlJu|X;$kx*(aJ=M(Pl4=Ogl=+ZcJ#>7)JC0b&L=ry@TT@4_CuSz!k&C8;lCG#bsf>KicpBUgR@IRZC;g zl<~pT^D4W9%!&A-y@Tcp%>2_&-yK$8aU>Q<8W(!l*uuwOBQudc)AI&{XYWTw01b$I zN(f?Q{-QuTu5S479$fAEe`B9~JPGl`Vwnn$5$RkPw8#b{^{gx*KMd~_57VsItiCQL z-Bmq@`tIcDU5&lVd2^kpCO#qQeVcG3?QK?{u2kagt`0={AiKW&w^0anz<8eJ;OXoG zv!RR>&ucPLB3p?7Qb4W0l{{22?$R$m1|g(^pJ4~tb0(|TV-dZ~PW4Pr`wedfm0?73 zvh3$*VI;YmkHa$x*oRg@BID-dC zU;MOkyv?-w-tgY)9dyklI~!v>)J7Gk^3b?AEh{==G#uf9eNy%zvM#En}a$F{+u{QWse@ z)zcrB^=kQTtYI5O2eyOZN$hDAlC_U0;$61rSDDeS5banV*m9|5$ia3qc83zvsiJtH z2CpZrwp+?EEZrKHO0?(Y3P>1eHj&wqK9(ZswMqUUNf!cnkv9&3ks2)KQ`*o2x|n+6 zgp|lHX^%Eirc_nUv-jG>xn5FJ#K4csYaq(X>>-%xJ26G_Nr)1)F7_01`h>ROg}_3S_=r_91m z4&{B0=i@K~su_wY1*i0ftRgMNY3F<~i{^F%2(c+Ear#IVr)4&c8dqwE9^{TL@;X&5F=f6H?sz?04KzL7!sfz#8L8urk+FeqP0 z1a%I}raZ;twpXeLl$4qsn0!4klztky=PZPpJdS{NX%-~eRHZeQXf_^)eiL>S4j(V- zJ#e{Arp7Eg4L8vC0k?08ew?v^*o`$|J7$t4_43pmjydi;p*TocYz7*=i?q>~BDhpg zTavUGJfx>)zG32+>U~Z|7l_=B)7|I-tG}D-&>O%kP0@>6cR0yGw|B+a)5Vn-0e|1);fS?2=VkRRymRc05ohisMae z*vK`Yyq9(e7|Ufyu6f$-h}7Xb`uP6rn4zzM(fh7 z+`Ls8+<$4Q?1`~Pq{O$<$uC^l){osYT$HMjlixf4t*f6(DR-bRB$vNyX}$Srx4-c2 zhknfU{D#6S)}y{HS$4SVwxU*Y9j@q0c?&NN%mQmmw8)lQ;G^@nKe{}H?80P$pkvGC zCPqa+%TBLnWieJSX5|$;cUJ34%@<9r@8~Ir((~9NoVXb|N1f`)SBIM6GwS<8~aw=Y3ndalrA&1w>uL$ zv;aO(%D~jMN4zMj*FBxvpTP?2cWi#pEp+15{vJhWe~Y-{Y4Bup1fkSQ34YeiXt`e_dJMOiiAqwQBcb-?LZTVz6x z$cbRASJ1 zba$53^|Nw%ZDy1CfLdrdYF7urY08Qbwv`kuAVeLxd~h6QgBC}kpiTGW>bu`ZSCq=F zWavbE$}9SVx&1bZW-jRP87cE!U8F;FL%88c`n&VBbdy;I`zQBL(s17?TrJv zThtcsY8nK-60&Z?*rxqy&H|v2=&314MN6^8AuT+CG?w$k@MLuDOVh+|+$Sh%#ieLv z4p{JL;c@GDkEKXm8unRlCRU?(LjMSmc{-b|undD)Z{uknRhFHf!^6Lq^VtgF3{3|9 zo58P(tj?KB4!^c5z*OBD%T1RloWi0mR1jz_t+s4?cE!g6v^&(K+$T%j%n_9I(bOF= znk;9Vg8lp(I5y$E!@-uaPP;*sUAdnuydEJWj7YC^lf^1>icXih$%Lz$TYj?6Ye3Ao zomx6;UV3kF)3 zFYwBSjuuNSpmpC%nrKH^7O%RlAuADa+h*%y9-QP+JiybrOJC~qY;xUd@(K)Ev2^SZ zeDTfUPz1J+@iX~IL)+>t&8DN2C9rci8*2tXJbCo^e|D|4|MAbi*5pkpXFR97QEm3( zqyo8S7LgA6iKH5mvj6i7Q?^G!iD(KEDJ9r(sqdcAtzD+Y)A~wmQjK4BNMmZgJXmro zfHTFL6kBCJRgcmsA(-9hBFl?5jc>h3uO2a!BNcSKTC8JxbNamU^@FZU__8M^Qw>$x zh8urx4;vjRPvo9tXQBN9opu7IN_vHi-UhohQv-F$tjBFK3q{@9eLm^z^G>B<>>^mh z)a`(}AI3 zg`LC)oXCSG!Y#rZdER|0u2+eu69!)_*$9lXA6hiZ+wBYmI&H zm-=Jte#dvjfAX{CUk?9u>C!ij!*J~f{=~l={%d0W|K;#soo8xl1XoIO`T6&r5Nl@1 znx@(@m3_Ux9R91He8_v0<%K`R*k-?Bvm4n=@|Lao-8BE8Hoq5DUsTz7vhMfanVN<( zeG$yiFYr7N{91N+3W#RuFTX9nQN!Vd<3%YXLmZNII7WALpos6CEoHr~ zE3cvTzHO9U;Vxr(qSCKG&t2x(bivvoo}wh2H(45wMCdbrIs8|Wn@bn-8s=0tGzT1C zmJf%5}8bCkeI_skv%BJ2Qjm zywTbLnt@35kh$eaL-5OWwQf8d-V5iNa0a{e27#BLLpR-f4XiWDB2Ds+qjZANI>xpH zeGb(x<{k^pRrV`Qd}>>2tWq#jzWZ>oCEDx;B^zrksRX~oL=Z$xO~WcpNHW`cEJ`8> zI{-&oq=N=mccIS6g&%aT9{=dGz$V&bmJdMe5qD_HS^01y?}yfFCJApl$heH)(J>}S z_}$|WW0R44Y(u3hx_ios;)|)`w8=Yo1O7I!Dx~0Jp>A~5b~BKA>Z->3jEpHR+$FfT z8w|bcq|xHQy#>TLD1%nT5v3tt3W%fNC{vr3j@(N_mY3MkM)7Uu_-;cF6WU4_3~onV zV*-o9%}4Az!%&*H>|;QIiOYRno9cmm41=(b0wAW4KW`hak$rp(rO5^Rx2=@c%I@3^ zC+xrg$4tKPG`K`OznnXZR07{)&sqDvrZ@808b^nI3Lx__&aBVka?*xdt8sAk2f=3L zReuwdkknGP|C5`jAP+%na2h^~oRcib~>N8SC> z?{ydo8{RR(9jY}W744?$uq3s-W29|JaW~d&y#@K2!dh+WK_JpN56@5G8yUTWAAy#2Hlgi z1|H6=uOw0vB>;vxZ=@{pj6W@>nzk%A2{Nb6MzPu+#}F;Q)7GEy+vAdg+wuLU-1t7t zN9ho4nWW&rwgP)`pYQHDT;(4&Al%Z)CA!Mn1mr(!Udb~PQ@~wy9z_XTcCA-D60F|O5k5N5zlY`F9^JaNPB;t!BS2v{X-5q7yiW%`@sG9~+@pe}1|# z1lp7mk5|XG6cIW_8u_i8GAU|!TzlX_CV;y4rD_q?)auSy68qF`{Eftwyz}xo^>{NY zHie`Io_bPqg^OnfZv6QBdT_m|SWm&U5zh4+_q~rkWRSC&0{aT}2ml30EE`+;G04^K zs`Yy$R-k3#Haf9e%{kZ3rlw185n5)Ezdv>P_UIXLyIUQChA&3jrNN( zT|p#Mx~BLXc#{o9`#0^hl&5cU$MKqXBh}l|X*VJw&QGyVFtSuZUN-A3Il{j{&wIt< za;?K$-yPS#nZj0nvA8snmr~-6WEsUIC5y!}f&i_D1SjGeNH`;chvo3#D4H~zdg`%U4NW$oA)1}E zNpzgz1zRe)yX{HnBzFg|LNo8J9Z_of@2=xO-%HH$m+32__)0qe8p&Gll3@R+;Z^Q> ztaxi<_1hsxg7mZMu^JldR7=wJM{d{6O?Rnssz1=iFbQ_XJMAs04)}Xf9sLF2 zN6{UQ)(i_iC*11k75XOpW4)zvPQCzQZRr)x%IXP{X?4@3z5q<9 zXs1%U$5Dm<^arx|j#A8|Z6~Y$JSUkTc@}%?6DNl3yF8Wo=I^afaO?M*RP0{)G}d7I zkf$)y_PSdC@y5kdJOL$pGWnmR>Iax4$v%x45j*MRTF+ z{O`Iqez(au=Ce4pz1ENr;CGVP{_}Qa7X23!IR^4)mppc5+F4Os%%(gS3koX9Z_~k%MwlbHoFQg z_#fL_qZ(Se3#0b&ccm<>EybaZc)88lZNA;uLWkhFwgPCl!9df-hTPIedLEuJLPrc8#CKH70)jZ*_!{7yaV&>}@eJsSy~EE%yXh zN0DJ;L9ar;57|sTRD;m7lwW4EFFB0{jmwrhXze&eowqzk-Ew1a^}$6}x3~Iy@8f(q zJpbb9;B1zii-0>@5(K_cy+k}5p+Y^kXDBK3m8pZ$Vr=>uQR)%|Zf>A=OqC62v7Is9 z+MKn9`r{lY@jo`d`b~eF+kS4$PwYs2`lJSfoBxonW9xJM5gtE4IHs9<_ys>=VZ3QpE^Axz}gxZZWXob&5SR+Ju8D_Oin;qFX)2~dVY&MB2HFuXxv~Apx zX~>Q)RK|Gd0lR?YueZeS%DWVM{!)wg*7bT_^bX!476Gr$7?pmNFbV~52db6cD&=q6 z_o&|E%^MMymSM1dewxp6cZXqf6BB(LPO5y~h(NXI1+r!Ff7HocXJ(FGxn4UP@t1ul z{ELu$g|}BhhW+!tZkx`J9acyPP~Nbum@cW5)jIF$eztdGLvi_B+IepyHB*l@-)<#@ z5$uB8{YTJ-xgFZ_5tuEOr#+Xr#dF;`cj2{CF1>nEwAJ1mla^}gqxl zYHYh!d5!57)$r)`^VdojXJs|fsH<$GP4z;XT0a*&Pda60n&+F6Jg{EMDYmJztz0}H z&R-Vy;W*)~o?crqiDG-zd2u?U8$xp$h`AWp=n2ht7in%*ClD`*Sadz=-TgD@j#8&} zQ}w>Q9&B;9BQJAT`E}T-L3F{uO-T0}M!zSCTZ7MR`W|xva-V!&)%Nqg zcPNQ{x3FK76~1@V&w4%6XN|v|5e2@P;?CHNF5k2?AMkr7$#vQmDqQqndWGO=a$9@6UlRBTIQXQl$1-+gdI4bA)_^8`S(30h_cRW+!Xphm}&RzT^^V$kax&(Vxm6tjt+m! zYMVu&`(n1g8*;!>V5PEhusVE99I?K=_P@y3otU-%RmN7->RX_bxK!seH?4tG;&zTl zieJ%5AeFjf-Uo$%)6!eG++4#=Rd7SNN4Bxb(|U>dIQ1IZm$;sHC~rQ}1aMDmSq*^rM?m8x#ji>oM~mx(=UKF*I&qyGdp0vEkOVV{~^VYS)Fy^h@ zvY6(6t8Zrj)Mu?CXYxtdpP;4Mzfm=ne8`(7H?sgkZlKbG$hhxzmLI-L68nS7J29Ix_iFD(SFI{vb&mFR0d}1A&XETlE)LF_OuWgWC-~ zt+D;x=Bqm7I}!>kGgdE+Hy}ph`JAoBYbLnuz`=_tuHAmx+*F56KAWjUt*1YjkM13f zeu}b9b^0GmdoVTApKtP(M|`a07istZ_i~W5993DOVIBssig#xSy-=^V7eD_ELH9bwt|nJWcx&ZlTWk5nYaTn`7W@8x85ttk~9=ZK(T zO3Ji8RKU!?@!JHS$3f;#f;5<=62e(#ATgC=(1 zno!S5mixbAnh$NXZo{r>M zUNl{~;=5Rmi+ctuJ|Fdb<|$4I-y1}zEuuhM#UmO^_>_;dpEJFur}s1a+`FGcNQUq) zdleyMvlFQTv#6EjKIxT%cVZ`dDtxDJCXDWooAn-z5N6DU_t?jRD8#fID0&Ain#CD&L3sqfhe97?2P z_1`^doO=Lm`ML|`7E+ssZ1!7Oc}?NUD=w2!g$j5O=~akr(y-H7yOSPE z#nRgVw@C#>)?Z8mwQ5SeRg&hL2o=cNE$@H!5pk{Rt{>9h^5)`5 zALsY;u7Kz~IMOUf@{<{yx{uGlFEb37aYmcToP}XPW@*TsMdI zRUcQ&4{1vkU;P0-!4TeL%?13;|NS|y^QNreXI)-uhu4wGfVQadpAS&&-i^8UGz9bP z=B9d~^-P^rTD$2k;qp|aFUt>F!Tlz@^sZ((Wd1x;DDZ2{=Y|tktEMDKld?F=&li=( zFzIU?5H7O%^%Ws##siYIgwc+v%!-2TdptS@(o7!d>M}0?nhBDz0W$w@F@WXB z>yrc>l2@f3L0rDujee%|a{vKuo0&Rrm&{@}hRmg+@kWpd%9I_y*VisTohjnIglJ*( z^6Rs|>gyU(VP(~M&F^kjx?ap0T%d~)DHYpk7v3#Z!XMLL^Yi0M&)WpJ=#{e z`VMPutA`JBi!1?nudi!QZ}>F?V8{81N$TgS?UoBn;{tV9U#z!ZsJCC#cdkD4_zRC( zf-wML>f>KdPT*ppxN*lQMO6ijz-g9M`b}96=VkqPq~;}vNH*K>c|3!w7vEqZ{r%|^ z6->(6;<@TA{H$4J{PDYhpWQr5mUrfVaAzvxohvz%b zQMJ%7%gL}Pr+S35RC{@JAHI%fx~)*t6tF$GMis}aI@g2-7^@Qye@X3r4dRq^24*hi*9v9+YZhFUQA4fUORPT`ijHChF*;Cto5{KqI=Yw=76O->aJnXx9lRFwwjWz5+^#HmULl9!%W^VOlZ$)? z-#mA_LSO<}91+YGTavl9W?CfOf>I!!Xdfmv;M$FAio|p6;>i&;c^A5vazZo0oaG;1 zfDU^7vYb$cig?}AdY+Y_TiRv1Z74RA^l>Yjbgl2yV~<@>wxb@0gEq^*rL8LwEd|@) zSqpPIyvX5&N2{wbvJ+#T40iPJ({nJ0{A>>04MtKp=e75qjEY!is9OFY95M(+D$F;0%|bV*4s zgJL=;wIUIEk-=*LHvu`mdNK~JUpPyN?@>=2TV_H4YSQB z+;w;}3_R^I(Ii;O?lvhu8%Dvk zvW~>fOl~q%$ITf+F|%RBpqY`Yi+rZlkf1$yfK9Nn+nUVQ7w<&zG{%!0P3N|l&Oqeb zKewX_<{(d1>^a`d96$QPf2Wns-ft)76-78r7c6xv)XfCyI{31fPqRi#)p^Mt(8d$d zefT6>xQ4$jvO2dW7o^*xB`w$%ZOlYxjjmqmv*uYgd4H5o-qRu0GJ#al#mNb`&HoJ; zSUEuG96vfqDwV!I)A^JA2mF(22$roxORhwilLw@kSu!@yl4e47C^{*8K^9&Om~ z$U}8~2FoNYS342*Qq@6xSf+${e}ux33JiPGayHAV-9Tf2kcK)pY3e*ZGxj-79QDw#bVMNEigQQMV~RjQ8_U`41iF#p zv{fS&f|TM=>Z`n&Trknf#-7z$CEh{YN|vKNzGpT6BA2Wr9wlD}3ojKes}#I!zU2s= zOjIY+)JfY8WyOdIJI|U6e%PavB>3CZ4!mJ;F?NmN0@EwHy z5?>M=>q(Cl9CBnH0E5TGvb_Ra)Kl+_nSc%KVt@&;fsrMhGB-Hy%V3k=%82+iAe z0p|Dbi>k+_|Iq6bRD&Vii(V=Oz)g1gw|v@MY^gsQFXmBpT5lPVfB+Y@{@X>~P-M~; z3GI8Net93*R>~~~)vhYA&69!kU$BqOP4%>%=QKǡu;c{RRgt(9=DK0qU@eZ$;2 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

$BL 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 Cz?(Y& diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json deleted file mode 100644 index 9c09049..0000000 --- a/packages/toolkit/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "@vite-pwa/toolkit", - "type": "module", - "version": "0.0.0", - "description": "A module that integrates into your build process, helping you generate a manifest of local files that workbox-sw should precache.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/toolkit#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/toolkit.git", - "directory": "packages/toolkit" - }, - "bugs": "https://github.com/vite-pwa/toolkit/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "caching", - "fetch requests", - "offline", - "file manifest" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - }, - "./generate-sw": "./dist/generate-sw.js", - "./inject-manifest": "./dist/inject-manifest.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"], - "generate-sw": ["dist/generate-sw.d.ts"], - "get-manifest": ["dist/get-manifest.d.ts"], - "inject-manifest": ["dist/inject-manifest.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build", - "node:test": "node ./dist/generate-sw.js", - "tsx:test": "tsx ./src/generate-sw.ts" - }, - "dependencies": { - "magicast": "file:magicast-0.3.5.tgz", - "pathe": "catalog:utils", - "pkg-types": "catalog:utils", - "proper-tags": "catalog:utils", - "recast": "catalog:utils", - "valibot": "catalog:utils" - } -} diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/toolkit/tsdown.config.ts b/packages/toolkit/tsdown.config.ts deleted file mode 100644 index 84e6a58..0000000 --- a/packages/toolkit/tsdown.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types,generate-sw,get-manifest,inject-manifest}.ts', - platform: 'node', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, -}) diff --git a/packages/workbox-background-sync/package.json b/packages/workbox-background-sync/package.json deleted file mode 100644 index 4f1c3af..0000000 --- a/packages/workbox-background-sync/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "@vite-pwa/workbox-background-sync", - "type": "module", - "version": "0.0.0", - "description": "Queues failed requests and uses the Background Sync API to replay them when the network is available", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-background-sync" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "background", - "sync", - "workbox-plugin" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*", - "idb": "catalog:utils" - } -} diff --git a/packages/workbox-background-sync/tsdown.config.ts b/packages/workbox-background-sync/tsdown.config.ts deleted file mode 100644 index cfa94bb..0000000 --- a/packages/workbox-background-sync/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-broadcast-update/package.json b/packages/workbox-broadcast-update/package.json deleted file mode 100644 index de34023..0000000 --- a/packages/workbox-broadcast-update/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@vite-pwa/workbox-broadcast-update", - "type": "module", - "version": "0.0.0", - "description": "A service worker helper library that uses the Broadcast Channel API to announce when a cached response has updated", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-broadcast-update" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "workbox-plugin" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*" - } -} diff --git a/packages/workbox-broadcast-update/tsdown.config.ts b/packages/workbox-broadcast-update/tsdown.config.ts deleted file mode 100644 index cfa94bb..0000000 --- a/packages/workbox-broadcast-update/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index c63f075..57dd9a4 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -55,12 +55,8 @@ "tsx:test": "tsx ./src/generate-sw.ts" }, "dependencies": { - "@vite-pwa/workbox-background-sync": "workspace:*", - "@vite-pwa/workbox-broadcast-update": "workspace:*", - "@vite-pwa/workbox-cacheable-response": "workspace:*", - "@vite-pwa/workbox-core": "workspace:*", - "@vite-pwa/workbox-expiration": "workspace:*", - "@vite-pwa/workbox-routing": "workspace:*", + "@vite-pwa/workbox-sw": "workspace:*", + "@vite-pwa/workbox-window": "workspace:*", "magicast": "file:magicast-0.3.5.tgz", "pathe": "catalog:utils", "pkg-types": "catalog:utils", diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts index 776a59b..027ce17 100644 --- a/packages/workbox-build/src/types.ts +++ b/packages/workbox-build/src/types.ts @@ -1,10 +1,10 @@ import type { QueueOptions } from '@vite-pwa/workbox-background-sync/types' -import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-broadcast-update/types' -import type { CacheableResponseOptions } from '@vite-pwa/workbox-cacheable-response/types' -import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-core/types' import type { ExpirationPluginOptions } from '@vite-pwa/workbox-expiration/types' +import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-sw/src/broadcast-update/types' +import type { CacheableResponseOptions } from '@vite-pwa/workbox-sw/src/cacheable-response/types' +import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-sw/src/core/types' // import type { GoogleAnalyticsInitializeOptions } from 'workbox-google-analytics/initialize' -import type { HTTPMethod } from '@vite-pwa/workbox-routing/types' +import type { HTTPMethod } from '@vite-pwa/workbox-sw/src/routing/types' import type { PackageJson } from 'pkg-types' export interface ManifestEntry { diff --git a/packages/workbox-cacheable-response/package.json b/packages/workbox-cacheable-response/package.json deleted file mode 100644 index 084c230..0000000 --- a/packages/workbox-cacheable-response/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@vite-pwa/workbox-cacheable-response", - "type": "module", - "version": "0.0.0", - "description": "This library takes a Response object and determines whether it's cacheable based on a specific configuration.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-cacheable-response" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "workbox-plugin" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*" - } -} diff --git a/packages/workbox-cacheable-response/tsdown.config.ts b/packages/workbox-cacheable-response/tsdown.config.ts deleted file mode 100644 index cfa94bb..0000000 --- a/packages/workbox-cacheable-response/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-core/package.json b/packages/workbox-core/package.json deleted file mode 100644 index 2ee9d8d..0000000 --- a/packages/workbox-core/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@vite-pwa/workbox-core", - "type": "module", - "version": "0.0.0", - "description": "This module is used by a number of the other Workbox modules to share common code.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-core" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - }, - "./internals": "./dist/internals.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"], - "internals": ["dist/internals.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - } -} diff --git a/packages/workbox-core/tsdown.config.ts b/packages/workbox-core/tsdown.config.ts deleted file mode 100644 index 0f71965..0000000 --- a/packages/workbox-core/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types,internals}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-expiration/package.json b/packages/workbox-expiration/package.json deleted file mode 100644 index dc3776f..0000000 --- a/packages/workbox-expiration/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@vite-pwa/workbox-expiration", - "type": "module", - "version": "0.0.0", - "description": "A service worker helper library that expires cached responses based on age or maximum number of entries.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-expiration" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "workbox-plugin" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"], - "internals": ["dist/internals.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*", - "idb": "catalog:utils" - } -} diff --git a/packages/workbox-expiration/tsdown.config.ts b/packages/workbox-expiration/tsdown.config.ts deleted file mode 100644 index cfa94bb..0000000 --- a/packages/workbox-expiration/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-navigation-preload/package.json b/packages/workbox-navigation-preload/package.json deleted file mode 100644 index adde92d..0000000 --- a/packages/workbox-navigation-preload/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@vite-pwa/workbox-navigation-preload", - "type": "module", - "version": "0.0.0", - "description": "This library allows developers to opt-in to using Navigation Preload in their service worker.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-navigation-preload" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "navigation" - ], - "workbox": { - "browserNamespace": "workbox.navigationPreload", - "packageType": "sw" - }, - "sideEffects": false, - "exports": { - ".": "./dist/index.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*" - } -} diff --git a/packages/workbox-navigation-preload/tsdown.config.ts b/packages/workbox-navigation-preload/tsdown.config.ts deleted file mode 100644 index 1ba9d65..0000000 --- a/packages/workbox-navigation-preload/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/index.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-precaching/package.json b/packages/workbox-precaching/package.json deleted file mode 100644 index 9416c37..0000000 --- a/packages/workbox-precaching/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@vite-pwa/workbox-precaching", - "type": "module", - "version": "0.0.0", - "description": "This module efficiently precaches assets.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-precaching" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*", - "@vite-pwa/workbox-routing": "workspace:*", - "@vite-pwa/workbox-strategies": "workspace:*" - } -} diff --git a/packages/workbox-precaching/tsdown.config.ts b/packages/workbox-precaching/tsdown.config.ts deleted file mode 100644 index 1ba9d65..0000000 --- a/packages/workbox-precaching/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/index.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-range-requests/package.json b/packages/workbox-range-requests/package.json deleted file mode 100644 index 6699edf..0000000 --- a/packages/workbox-range-requests/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@vite-pwa/workbox-range-requests", - "type": "module", - "version": "0.0.0", - "description": "This library creates a new Response, given a source Response and a Range header value.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-range-requests" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "caching", - "cache", - "range", - "media", - "workbox-plugin" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*" - } -} diff --git a/packages/workbox-range-requests/tsdown.config.ts b/packages/workbox-range-requests/tsdown.config.ts deleted file mode 100644 index 1ba9d65..0000000 --- a/packages/workbox-range-requests/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/index.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-recipes/package.json b/packages/workbox-recipes/package.json deleted file mode 100644 index 9afcb5c..0000000 --- a/packages/workbox-recipes/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@vite-pwa/workbox-recipes", - "type": "module", - "version": "0.0.0", - "description": "A service worker helper library to manage common request and caching patterns", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-recipes" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "router", - "routing" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-cacheable-response": "workspace:*", - "@vite-pwa/workbox-core": "workspace:*", - "@vite-pwa/workbox-expiration": "workspace:*", - "@vite-pwa/workbox-precaching": "workspace:*", - "@vite-pwa/workbox-routing": "workspace:*", - "@vite-pwa/workbox-strategies": "workspace:*" - } -} diff --git a/packages/workbox-recipes/tsdown.config.ts b/packages/workbox-recipes/tsdown.config.ts deleted file mode 100644 index 1ba9d65..0000000 --- a/packages/workbox-recipes/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/index.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-routing/package.json b/packages/workbox-routing/package.json deleted file mode 100644 index 367beeb..0000000 --- a/packages/workbox-routing/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@vite-pwa/workbox-routing", - "type": "module", - "version": "0.0.0", - "description": "A service worker helper library to route request URLs to handlers.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-routing" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "router", - "routing" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js", - "./types": { - "types": "./dist/types.d.ts" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "types": ["dist/types.d.ts"], - "internals": ["dist/internals.d.ts"] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*" - } -} diff --git a/packages/workbox-routing/tsdown.config.ts b/packages/workbox-routing/tsdown.config.ts deleted file mode 100644 index cfa94bb..0000000 --- a/packages/workbox-routing/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/{index,types}.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-strategies/package.json b/packages/workbox-strategies/package.json deleted file mode 100644 index 71c42c2..0000000 --- a/packages/workbox-strategies/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@vite-pwa/workbox-strategies", - "type": "module", - "version": "0.0.0", - "description": "A service worker helper library implementing common caching strategies.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-strategies" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "router", - "routing" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*" - } -} diff --git a/packages/workbox-strategies/tsdown.config.ts b/packages/workbox-strategies/tsdown.config.ts deleted file mode 100644 index 1ba9d65..0000000 --- a/packages/workbox-strategies/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/index.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-streams/package.json b/packages/workbox-streams/package.json deleted file mode 100644 index 651c962..0000000 --- a/packages/workbox-streams/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "@vite-pwa/workbox-streams", - "type": "module", - "version": "0.0.0", - "description": "A library that makes it easier to work with Streams in the browser.", - "author": "Google's Web DevRel Team, Vite PWA's Team", - "contributors": [ - "userquin " - ], - "license": "MIT", - "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/vite-pwa/workbox.git", - "directory": "packages/workbox-streams" - }, - "bugs": "https://github.com/vite-pwa/workbox/issues", - "keywords": [ - "workbox", - "workboxjs", - "service worker", - "sw", - "streams", - "readablestream" - ], - "sideEffects": false, - "exports": { - ".": "./dist/index.js" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "prepublishOnly": "pnpm run build" - }, - "dependencies": { - "@vite-pwa/workbox-core": "workspace:*", - "@vite-pwa/workbox-routing": "workspace:*" - } -} diff --git a/packages/workbox-streams/tsdown.config.ts b/packages/workbox-streams/tsdown.config.ts deleted file mode 100644 index 1ba9d65..0000000 --- a/packages/workbox-streams/tsdown.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'tsdown' - -export default defineConfig({ - entry: './src/index.ts', - platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, -}) diff --git a/packages/workbox-sw/package.json b/packages/workbox-sw/package.json index 01c403a..fd8af45 100644 --- a/packages/workbox-sw/package.json +++ b/packages/workbox-sw/package.json @@ -2,35 +2,164 @@ "name": "@vite-pwa/workbox-sw", "type": "module", "version": "0.0.0", - "description": "This module makes it easy to get started with the Workbox service worker libraries.", + "description": "This library contains all the packages from workbox packages for the service worker.", "author": "Google's Web DevRel Team, Vite PWA's Team", "contributors": [ "userquin " ], "license": "MIT", "funding": "https://github.com/sponsors/antfu", - "homepage": "https://github.com/vite-pwa/workbox#readme", + "homepage": "https://github.com/vite-pwa/toolkit#readme", "repository": { "type": "git", - "url": "https://github.com/vite-pwa/workbox.git", + "url": "git+https://github.com/vite-pwa/toolkit.git", "directory": "packages/workbox-sw" }, - "bugs": "https://github.com/vite-pwa/workbox/issues", + "bugs": "https://github.com/vite-pwa/toolkit/issues", "keywords": [ "workbox", "workboxjs", "service worker", - "sw" + "sw", + "background", + "sync", + "navigation", + "caching", + "cache", + "range", + "media", + "workbox-plugin", + "router", + "routing", + "streams", + "readablestream" ], "sideEffects": false, - "main": "dist/workbox-sw.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": "./dist/index.js", + "./background-sync": "./dist/background-sync/index.js", + "./background-sync/types": { + "types": "./dist/background-sync/types.d.ts" + }, + "./broadcast-update": "./dist/broadcast-update/index.js", + "./broadcast-update/types": { + "types": "./dist/broadcast-update/types.d.ts" + }, + "./cacheable-response": "./dist/cacheable-response/index.js", + "./cacheable-response/types": { + "types": "./dist/cacheable-response/types.d.ts" + }, + "./core": "./dist/core/index.js", + "./core/types": { + "types": "./dist/core/types.d.ts" + }, + "./expiration": "./dist/expiration/index.js", + "./expiration/types": { + "types": "./dist/expiration/types.d.ts" + }, + "./navigation-preload": "./dist/navigation-preload/index.js", + "./precaching": "./dist/precaching/index.js", + "./precaching/types": { + "types": "./dist/precaching/types.d.ts" + }, + "./range-requests": "./dist/range-requests/index.js", + "./recipes": "./dist/recipes/index.js", + "./routing": "./dist/routing/index.js", + "./routing/types": { + "types": "./dist/routing/types.d.ts" + }, + "./strategies": "./dist/strategies/index.js", + "./streams": "./dist/streams/index.js", + "./streams/types": { + "types": "./dist/streams/types.d.ts" + }, + "./sw": "./dist/sw/index.js", + "./types": { + "types": "./dist/types.d.ts" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "background-sync": [ + "./dist/background-sync/index.d.ts" + ], + "background-sync/types": [ + "./dist/background-sync/types.d.ts" + ], + "broadcast-update": [ + "./dist/broadcast-update/index.d.ts" + ], + "broadcast-update/types": [ + "./dist/broadcast-update/types.d.ts" + ], + "cacheable-response": [ + "./dist/cacheable-response/index.d.ts" + ], + "cacheable-response/types": [ + "./dist/cacheable-response/types.d.ts" + ], + "core": [ + "./dist/core/index.d.ts" + ], + "core/types": [ + "./dist/core/types.d.ts" + ], + "expiration": [ + "./dist/expiration/index.d.ts" + ], + "expiration/types": [ + "./dist/expiration/types.d.ts" + ], + "navigation-preload": [ + "./dist/navigation-preload/index.d.ts" + ], + "precaching": [ + "./dist/precaching/index.d.ts" + ], + "precaching/types": [ + "./dist/precaching/types.d.ts" + ], + "range-requests": [ + "./dist/range-requests/index.d.ts" + ], + "recipes": [ + "./dist/recipes/index.d.ts" + ], + "routing": [ + "./dist/routing/index.d.ts" + ], + "routing/types": [ + "./dist/routing/types.d.ts" + ], + "strategies": [ + "./dist/strategies/index.d.ts" + ], + "streams": [ + "./dist/streams/index.d.ts" + ], + "streams/types": [ + "./dist/streams/types.d.ts" + ], + "sw": [ + "./dist/sw/index.d.ts" + ], + "types": [ + "./dist/types.d.ts" + ] + } + }, "files": [ "dist" ], "scripts": { "build": "tsdown", "prepublishOnly": "pnpm run build" + }, + "dependencies": { + "idb": "catalog:utils" } } diff --git a/packages/workbox-sw/src/_types.ts b/packages/workbox-sw/src/_types.ts deleted file mode 100644 index 515baa7..0000000 --- a/packages/workbox-sw/src/_types.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -/** - * A `ModulePathCallback` function can be used to modify the modify the where - * Workbox modules are loaded. - * - * @callback ~ModulePathCallback - * @param {string} moduleName The name of the module to load (i.e. - * 'workbox-core', 'workbox-precaching' etc.). - * @param {boolean} debug When true, `dev` builds should be loaded, otherwise - * load `prod` builds. - * @return {string} This callback should return a path of module. This will - * be passed to `importScripts()`. - * - * @memberof workbox - */ diff --git a/packages/workbox-background-sync/src/BackgroundSyncPlugin.ts b/packages/workbox-sw/src/background-sync/BackgroundSyncPlugin.ts similarity index 95% rename from packages/workbox-background-sync/src/BackgroundSyncPlugin.ts rename to packages/workbox-sw/src/background-sync/BackgroundSyncPlugin.ts index d0a64ef..6f7dfd6 100644 --- a/packages/workbox-background-sync/src/BackgroundSyncPlugin.ts +++ b/packages/workbox-sw/src/background-sync/BackgroundSyncPlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../core/types' import type { QueueOptions } from './Queue' import { Queue } from './Queue' diff --git a/packages/workbox-background-sync/src/Queue.ts b/packages/workbox-sw/src/background-sync/Queue.ts similarity index 99% rename from packages/workbox-background-sync/src/Queue.ts rename to packages/workbox-sw/src/background-sync/Queue.ts index cffc46c..1d73ef1 100644 --- a/packages/workbox-background-sync/src/Queue.ts +++ b/packages/workbox-sw/src/background-sync/Queue.ts @@ -8,7 +8,7 @@ import type { QueueStoreEntry, UnidentifiedQueueStoreEntry } from './lib/QueueDb' -import { assert, getFriendlyURL, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, getFriendlyURL, logger, WorkboxError } from '../core/internals' import { QueueStore } from './lib/QueueStore' import { StorableRequest } from './lib/StorableRequest' diff --git a/packages/workbox-background-sync/src/QueueStore.ts b/packages/workbox-sw/src/background-sync/QueueStore.ts similarity index 100% rename from packages/workbox-background-sync/src/QueueStore.ts rename to packages/workbox-sw/src/background-sync/QueueStore.ts diff --git a/packages/workbox-background-sync/src/StorableRequest.ts b/packages/workbox-sw/src/background-sync/StorableRequest.ts similarity index 100% rename from packages/workbox-background-sync/src/StorableRequest.ts rename to packages/workbox-sw/src/background-sync/StorableRequest.ts diff --git a/packages/workbox-background-sync/src/index.ts b/packages/workbox-sw/src/background-sync/index.ts similarity index 100% rename from packages/workbox-background-sync/src/index.ts rename to packages/workbox-sw/src/background-sync/index.ts diff --git a/packages/workbox-background-sync/src/lib/QueueDb.ts b/packages/workbox-sw/src/background-sync/lib/QueueDb.ts similarity index 100% rename from packages/workbox-background-sync/src/lib/QueueDb.ts rename to packages/workbox-sw/src/background-sync/lib/QueueDb.ts diff --git a/packages/workbox-background-sync/src/lib/QueueStore.ts b/packages/workbox-sw/src/background-sync/lib/QueueStore.ts similarity index 98% rename from packages/workbox-background-sync/src/lib/QueueStore.ts rename to packages/workbox-sw/src/background-sync/lib/QueueStore.ts index 3d726f9..515a8c2 100644 --- a/packages/workbox-background-sync/src/lib/QueueStore.ts +++ b/packages/workbox-sw/src/background-sync/lib/QueueStore.ts @@ -10,7 +10,7 @@ import type { QueueStoreEntry, UnidentifiedQueueStoreEntry, } from './QueueDb' -import { assert } from '@vite-pwa/workbox-core/internals' +import { assert } from '../../core/internals' import { QueueDb, } from './QueueDb' diff --git a/packages/workbox-background-sync/src/lib/StorableRequest.ts b/packages/workbox-sw/src/background-sync/lib/StorableRequest.ts similarity index 97% rename from packages/workbox-background-sync/src/lib/StorableRequest.ts rename to packages/workbox-sw/src/background-sync/lib/StorableRequest.ts index f17586a..4af338c 100644 --- a/packages/workbox-background-sync/src/lib/StorableRequest.ts +++ b/packages/workbox-sw/src/background-sync/lib/StorableRequest.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import type { MapLikeObject } from '@vite-pwa/workbox-core' -import { assert } from '@vite-pwa/workbox-core/internals' +import type { MapLikeObject } from '../../core/types' +import { assert } from '../../core/internals' type SerializableProperties = | 'method' diff --git a/packages/workbox-background-sync/src/types.ts b/packages/workbox-sw/src/background-sync/types.ts similarity index 100% rename from packages/workbox-background-sync/src/types.ts rename to packages/workbox-sw/src/background-sync/types.ts diff --git a/packages/workbox-broadcast-update/src/BroadcastCacheUpdate.ts b/packages/workbox-sw/src/broadcast-update/BroadcastCacheUpdate.ts similarity index 98% rename from packages/workbox-broadcast-update/src/BroadcastCacheUpdate.ts rename to packages/workbox-sw/src/broadcast-update/BroadcastCacheUpdate.ts index e5ec863..4c0c51e 100644 --- a/packages/workbox-broadcast-update/src/BroadcastCacheUpdate.ts +++ b/packages/workbox-sw/src/broadcast-update/BroadcastCacheUpdate.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import type { CacheDidUpdateCallbackParam } from '@vite-pwa/workbox-core' -import { assert, logger, resultingClientExists, timeout } from '@vite-pwa/workbox-core/internals' +import type { CacheDidUpdateCallbackParam } from '../core/types' +import { assert, logger, resultingClientExists, timeout } from '../core/internals' import { responsesAreSame } from './responsesAreSame' import { diff --git a/packages/workbox-broadcast-update/src/BroadcastUpdatePlugin.ts b/packages/workbox-sw/src/broadcast-update/BroadcastUpdatePlugin.ts similarity index 94% rename from packages/workbox-broadcast-update/src/BroadcastUpdatePlugin.ts rename to packages/workbox-sw/src/broadcast-update/BroadcastUpdatePlugin.ts index 82c99e0..0da3c03 100644 --- a/packages/workbox-broadcast-update/src/BroadcastUpdatePlugin.ts +++ b/packages/workbox-sw/src/broadcast-update/BroadcastUpdatePlugin.ts @@ -6,12 +6,12 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../core/types' import type { BroadcastCacheUpdateOptions, } from './BroadcastCacheUpdate' -import { dontWaitFor } from '@vite-pwa/workbox-core/internals' +import { dontWaitFor } from '../core/internals' import { BroadcastCacheUpdate, } from './BroadcastCacheUpdate' diff --git a/packages/workbox-broadcast-update/src/index.ts b/packages/workbox-sw/src/broadcast-update/index.ts similarity index 100% rename from packages/workbox-broadcast-update/src/index.ts rename to packages/workbox-sw/src/broadcast-update/index.ts diff --git a/packages/workbox-broadcast-update/src/responsesAreSame.ts b/packages/workbox-sw/src/broadcast-update/responsesAreSame.ts similarity index 96% rename from packages/workbox-broadcast-update/src/responsesAreSame.ts rename to packages/workbox-sw/src/broadcast-update/responsesAreSame.ts index 0ce6804..18f62d1 100644 --- a/packages/workbox-broadcast-update/src/responsesAreSame.ts +++ b/packages/workbox-sw/src/broadcast-update/responsesAreSame.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { logger, WorkboxError } from '../core/internals' /** * Given two `Response's`, compares several header values to see if they are diff --git a/packages/workbox-broadcast-update/src/types.ts b/packages/workbox-sw/src/broadcast-update/types.ts similarity index 100% rename from packages/workbox-broadcast-update/src/types.ts rename to packages/workbox-sw/src/broadcast-update/types.ts diff --git a/packages/workbox-broadcast-update/src/utils/constants.ts b/packages/workbox-sw/src/broadcast-update/utils/constants.ts similarity index 100% rename from packages/workbox-broadcast-update/src/utils/constants.ts rename to packages/workbox-sw/src/broadcast-update/utils/constants.ts diff --git a/packages/workbox-cacheable-response/src/CacheableResponse.ts b/packages/workbox-sw/src/cacheable-response/CacheableResponse.ts similarity index 97% rename from packages/workbox-cacheable-response/src/CacheableResponse.ts rename to packages/workbox-sw/src/cacheable-response/CacheableResponse.ts index 2902e43..db0dfb5 100644 --- a/packages/workbox-cacheable-response/src/CacheableResponse.ts +++ b/packages/workbox-sw/src/cacheable-response/CacheableResponse.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { assert, getFriendlyURL, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, getFriendlyURL, logger, WorkboxError } from '../core/internals' export interface CacheableResponseOptions { statuses?: number[] diff --git a/packages/workbox-cacheable-response/src/CacheableResponsePlugin.ts b/packages/workbox-sw/src/cacheable-response/CacheableResponsePlugin.ts similarity index 96% rename from packages/workbox-cacheable-response/src/CacheableResponsePlugin.ts rename to packages/workbox-sw/src/cacheable-response/CacheableResponsePlugin.ts index 8dbd3b7..81e044e 100644 --- a/packages/workbox-cacheable-response/src/CacheableResponsePlugin.ts +++ b/packages/workbox-sw/src/cacheable-response/CacheableResponsePlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../core/types' import type { CacheableResponseOptions } from './CacheableResponse' import { CacheableResponse } from './CacheableResponse' diff --git a/packages/workbox-cacheable-response/src/index.ts b/packages/workbox-sw/src/cacheable-response/index.ts similarity index 100% rename from packages/workbox-cacheable-response/src/index.ts rename to packages/workbox-sw/src/cacheable-response/index.ts diff --git a/packages/workbox-cacheable-response/src/types.ts b/packages/workbox-sw/src/cacheable-response/types.ts similarity index 100% rename from packages/workbox-cacheable-response/src/types.ts rename to packages/workbox-sw/src/cacheable-response/types.ts diff --git a/packages/workbox-core/src/_private.ts b/packages/workbox-sw/src/core/_private.ts similarity index 100% rename from packages/workbox-core/src/_private.ts rename to packages/workbox-sw/src/core/_private.ts diff --git a/packages/workbox-core/src/_private/Deferred.ts b/packages/workbox-sw/src/core/_private/Deferred.ts similarity index 100% rename from packages/workbox-core/src/_private/Deferred.ts rename to packages/workbox-sw/src/core/_private/Deferred.ts diff --git a/packages/workbox-core/src/_private/WorkboxError.ts b/packages/workbox-sw/src/core/_private/WorkboxError.ts similarity index 100% rename from packages/workbox-core/src/_private/WorkboxError.ts rename to packages/workbox-sw/src/core/_private/WorkboxError.ts diff --git a/packages/workbox-core/src/_private/assert.ts b/packages/workbox-sw/src/core/_private/assert.ts similarity index 97% rename from packages/workbox-core/src/_private/assert.ts rename to packages/workbox-sw/src/core/_private/assert.ts index 9925a79..b10f1fa 100644 --- a/packages/workbox-core/src/_private/assert.ts +++ b/packages/workbox-sw/src/core/_private/assert.ts @@ -7,7 +7,7 @@ */ import type { MapLikeObject } from '../types' -import { WorkboxError } from '../_private/WorkboxError' +import { WorkboxError } from './WorkboxError' /* * This method throws if the supplied value is not an array. diff --git a/packages/workbox-core/src/_private/cacheMatchIgnoreParams.ts b/packages/workbox-sw/src/core/_private/cacheMatchIgnoreParams.ts similarity index 100% rename from packages/workbox-core/src/_private/cacheMatchIgnoreParams.ts rename to packages/workbox-sw/src/core/_private/cacheMatchIgnoreParams.ts diff --git a/packages/workbox-core/src/_private/cacheNames.ts b/packages/workbox-sw/src/core/_private/cacheNames.ts similarity index 100% rename from packages/workbox-core/src/_private/cacheNames.ts rename to packages/workbox-sw/src/core/_private/cacheNames.ts diff --git a/packages/workbox-core/src/_private/canConstructReadableStream.ts b/packages/workbox-sw/src/core/_private/canConstructReadableStream.ts similarity index 100% rename from packages/workbox-core/src/_private/canConstructReadableStream.ts rename to packages/workbox-sw/src/core/_private/canConstructReadableStream.ts diff --git a/packages/workbox-core/src/_private/canConstructResponseFromBodyStream.ts b/packages/workbox-sw/src/core/_private/canConstructResponseFromBodyStream.ts similarity index 100% rename from packages/workbox-core/src/_private/canConstructResponseFromBodyStream.ts rename to packages/workbox-sw/src/core/_private/canConstructResponseFromBodyStream.ts diff --git a/packages/workbox-core/src/_private/dontWaitFor.ts b/packages/workbox-sw/src/core/_private/dontWaitFor.ts similarity index 100% rename from packages/workbox-core/src/_private/dontWaitFor.ts rename to packages/workbox-sw/src/core/_private/dontWaitFor.ts diff --git a/packages/workbox-core/src/_private/executeQuotaErrorCallbacks.ts b/packages/workbox-sw/src/core/_private/executeQuotaErrorCallbacks.ts similarity index 96% rename from packages/workbox-core/src/_private/executeQuotaErrorCallbacks.ts rename to packages/workbox-sw/src/core/_private/executeQuotaErrorCallbacks.ts index 0f00f02..a02d1a5 100644 --- a/packages/workbox-core/src/_private/executeQuotaErrorCallbacks.ts +++ b/packages/workbox-sw/src/core/_private/executeQuotaErrorCallbacks.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import { logger } from '../_private/logger' import { quotaErrorCallbacks } from '../models/quotaErrorCallbacks' +import { logger } from './logger' /** * Runs all of the callback functions, one at a time sequentially, in the order diff --git a/packages/workbox-core/src/_private/getFriendlyURL.ts b/packages/workbox-sw/src/core/_private/getFriendlyURL.ts similarity index 100% rename from packages/workbox-core/src/_private/getFriendlyURL.ts rename to packages/workbox-sw/src/core/_private/getFriendlyURL.ts diff --git a/packages/workbox-core/src/_private/logger.ts b/packages/workbox-sw/src/core/_private/logger.ts similarity index 100% rename from packages/workbox-core/src/_private/logger.ts rename to packages/workbox-sw/src/core/_private/logger.ts diff --git a/packages/workbox-core/src/_private/resultingClientExists.ts b/packages/workbox-sw/src/core/_private/resultingClientExists.ts similarity index 100% rename from packages/workbox-core/src/_private/resultingClientExists.ts rename to packages/workbox-sw/src/core/_private/resultingClientExists.ts diff --git a/packages/workbox-core/src/_private/timeout.ts b/packages/workbox-sw/src/core/_private/timeout.ts similarity index 100% rename from packages/workbox-core/src/_private/timeout.ts rename to packages/workbox-sw/src/core/_private/timeout.ts diff --git a/packages/workbox-core/src/_private/waitUntil.ts b/packages/workbox-sw/src/core/_private/waitUntil.ts similarity index 100% rename from packages/workbox-core/src/_private/waitUntil.ts rename to packages/workbox-sw/src/core/_private/waitUntil.ts diff --git a/packages/workbox-core/src/cacheNames.ts b/packages/workbox-sw/src/core/cacheNames.ts similarity index 100% rename from packages/workbox-core/src/cacheNames.ts rename to packages/workbox-sw/src/core/cacheNames.ts diff --git a/packages/workbox-core/src/clientsClaim.ts b/packages/workbox-sw/src/core/clientsClaim.ts similarity index 100% rename from packages/workbox-core/src/clientsClaim.ts rename to packages/workbox-sw/src/core/clientsClaim.ts diff --git a/packages/workbox-core/src/copyResponse.ts b/packages/workbox-sw/src/core/copyResponse.ts similarity index 100% rename from packages/workbox-core/src/copyResponse.ts rename to packages/workbox-sw/src/core/copyResponse.ts diff --git a/packages/workbox-core/src/index.ts b/packages/workbox-sw/src/core/index.ts similarity index 100% rename from packages/workbox-core/src/index.ts rename to packages/workbox-sw/src/core/index.ts diff --git a/packages/workbox-core/src/internals.ts b/packages/workbox-sw/src/core/internals.ts similarity index 100% rename from packages/workbox-core/src/internals.ts rename to packages/workbox-sw/src/core/internals.ts diff --git a/packages/workbox-core/src/models/messages/messageGenerator.ts b/packages/workbox-sw/src/core/models/messages/messageGenerator.ts similarity index 100% rename from packages/workbox-core/src/models/messages/messageGenerator.ts rename to packages/workbox-sw/src/core/models/messages/messageGenerator.ts diff --git a/packages/workbox-core/src/models/messages/messages.ts b/packages/workbox-sw/src/core/models/messages/messages.ts similarity index 100% rename from packages/workbox-core/src/models/messages/messages.ts rename to packages/workbox-sw/src/core/models/messages/messages.ts diff --git a/packages/workbox-core/src/models/pluginEvents.ts b/packages/workbox-sw/src/core/models/pluginEvents.ts similarity index 100% rename from packages/workbox-core/src/models/pluginEvents.ts rename to packages/workbox-sw/src/core/models/pluginEvents.ts diff --git a/packages/workbox-core/src/models/quotaErrorCallbacks.ts b/packages/workbox-sw/src/core/models/quotaErrorCallbacks.ts similarity index 100% rename from packages/workbox-core/src/models/quotaErrorCallbacks.ts rename to packages/workbox-sw/src/core/models/quotaErrorCallbacks.ts diff --git a/packages/workbox-core/src/registerQuotaErrorCallback.ts b/packages/workbox-sw/src/core/registerQuotaErrorCallback.ts similarity index 100% rename from packages/workbox-core/src/registerQuotaErrorCallback.ts rename to packages/workbox-sw/src/core/registerQuotaErrorCallback.ts diff --git a/packages/workbox-core/src/setCacheNameDetails.ts b/packages/workbox-sw/src/core/setCacheNameDetails.ts similarity index 100% rename from packages/workbox-core/src/setCacheNameDetails.ts rename to packages/workbox-sw/src/core/setCacheNameDetails.ts diff --git a/packages/workbox-core/src/skipWaiting.ts b/packages/workbox-sw/src/core/skipWaiting.ts similarity index 100% rename from packages/workbox-core/src/skipWaiting.ts rename to packages/workbox-sw/src/core/skipWaiting.ts diff --git a/packages/workbox-core/src/types.ts b/packages/workbox-sw/src/core/types.ts similarity index 100% rename from packages/workbox-core/src/types.ts rename to packages/workbox-sw/src/core/types.ts diff --git a/packages/workbox-core/src/utils/pluginUtils.ts b/packages/workbox-sw/src/core/utils/pluginUtils.ts similarity index 100% rename from packages/workbox-core/src/utils/pluginUtils.ts rename to packages/workbox-sw/src/core/utils/pluginUtils.ts diff --git a/packages/workbox-core/src/utils/welcome.ts b/packages/workbox-sw/src/core/utils/welcome.ts similarity index 100% rename from packages/workbox-core/src/utils/welcome.ts rename to packages/workbox-sw/src/core/utils/welcome.ts diff --git a/packages/workbox-expiration/src/CacheExpiration.ts b/packages/workbox-sw/src/expiration/CacheExpiration.ts similarity index 98% rename from packages/workbox-expiration/src/CacheExpiration.ts rename to packages/workbox-sw/src/expiration/CacheExpiration.ts index 73c4b03..9ab02c4 100644 --- a/packages/workbox-expiration/src/CacheExpiration.ts +++ b/packages/workbox-sw/src/expiration/CacheExpiration.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { assert, dontWaitFor, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, dontWaitFor, logger, WorkboxError } from '../core/internals' import { CacheTimestampsModel } from './models/CacheTimestampsModel' diff --git a/packages/workbox-expiration/src/ExpirationPlugin.ts b/packages/workbox-sw/src/expiration/ExpirationPlugin.ts similarity index 98% rename from packages/workbox-expiration/src/ExpirationPlugin.ts rename to packages/workbox-sw/src/expiration/ExpirationPlugin.ts index 0ea5968..f2408e7 100644 --- a/packages/workbox-expiration/src/ExpirationPlugin.ts +++ b/packages/workbox-sw/src/expiration/ExpirationPlugin.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' -import { registerQuotaErrorCallback } from '@vite-pwa/workbox-core' -import { assert, privateCacheNames as cacheNames, dontWaitFor, getFriendlyURL, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import type { WorkboxPlugin } from '../core/types' +import { registerQuotaErrorCallback } from '../core/index' +import { assert, privateCacheNames as cacheNames, dontWaitFor, getFriendlyURL, logger, WorkboxError } from '../core/internals' import { CacheExpiration } from './CacheExpiration' diff --git a/packages/workbox-expiration/src/index.ts b/packages/workbox-sw/src/expiration/index.ts similarity index 100% rename from packages/workbox-expiration/src/index.ts rename to packages/workbox-sw/src/expiration/index.ts diff --git a/packages/workbox-expiration/src/models/CacheTimestampsModel.ts b/packages/workbox-sw/src/expiration/models/CacheTimestampsModel.ts similarity index 100% rename from packages/workbox-expiration/src/models/CacheTimestampsModel.ts rename to packages/workbox-sw/src/expiration/models/CacheTimestampsModel.ts diff --git a/packages/workbox-expiration/src/types.ts b/packages/workbox-sw/src/expiration/types.ts similarity index 100% rename from packages/workbox-expiration/src/types.ts rename to packages/workbox-sw/src/expiration/types.ts diff --git a/packages/workbox-sw/src/index.ts b/packages/workbox-sw/src/index.ts index 52ea9ec..93ab306 100644 --- a/packages/workbox-sw/src/index.ts +++ b/packages/workbox-sw/src/index.ts @@ -1,13 +1,12 @@ -/* - Copyright 2018 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/ - -import { WorkboxSw } from './workbox-sw' - -// Don't export anything, just expose a global. -// eslint-disable-next-line no-restricted-globals -self.workbox = new WorkboxSw() +export * from './background-sync/index' +export * from './broadcast-update/index' +export * from './cacheable-response/index' +export * from './core/index' +export * from './expiration/index' +export * from './precaching/index' +export * from './range-requests/index' +export * from './recipes/index' +export * from './routing/index' +export * from './strategies/index' +export * from './streams/index' +export * from './sw/index' diff --git a/packages/workbox-navigation-preload/src/disable.ts b/packages/workbox-sw/src/navigation-preload/disable.ts similarity index 94% rename from packages/workbox-navigation-preload/src/disable.ts rename to packages/workbox-sw/src/navigation-preload/disable.ts index cd3d2f0..c9b5b44 100644 --- a/packages/workbox-navigation-preload/src/disable.ts +++ b/packages/workbox-sw/src/navigation-preload/disable.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { logger } from '@vite-pwa/workbox-core/internals' +import { logger } from '../core/internals' import { isSupported } from './isSupported' // Give TypeScript the correct global. diff --git a/packages/workbox-navigation-preload/src/enable.ts b/packages/workbox-sw/src/navigation-preload/enable.ts similarity index 96% rename from packages/workbox-navigation-preload/src/enable.ts rename to packages/workbox-sw/src/navigation-preload/enable.ts index 89fddb1..2e0c149 100644 --- a/packages/workbox-navigation-preload/src/enable.ts +++ b/packages/workbox-sw/src/navigation-preload/enable.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { logger } from '@vite-pwa/workbox-core/internals' +import { logger } from '../core/internals' import { isSupported } from './isSupported' // Give TypeScript the correct global. diff --git a/packages/workbox-navigation-preload/src/index.ts b/packages/workbox-sw/src/navigation-preload/index.ts similarity index 100% rename from packages/workbox-navigation-preload/src/index.ts rename to packages/workbox-sw/src/navigation-preload/index.ts diff --git a/packages/workbox-navigation-preload/src/isSupported.ts b/packages/workbox-sw/src/navigation-preload/isSupported.ts similarity index 100% rename from packages/workbox-navigation-preload/src/isSupported.ts rename to packages/workbox-sw/src/navigation-preload/isSupported.ts diff --git a/packages/workbox-precaching/src/PrecacheController.ts b/packages/workbox-sw/src/precaching/PrecacheController.ts similarity index 98% rename from packages/workbox-precaching/src/PrecacheController.ts rename to packages/workbox-sw/src/precaching/PrecacheController.ts index 1481c09..9e07553 100644 --- a/packages/workbox-precaching/src/PrecacheController.ts +++ b/packages/workbox-sw/src/precaching/PrecacheController.ts @@ -6,10 +6,10 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandlerCallback, WorkboxPlugin } from '@vite-pwa/workbox-core' -import type { Strategy } from '@vite-pwa/workbox-strategies' -import type { CleanupResult, InstallResult, PrecacheEntry } from './_types' -import { assert, privateCacheNames as cacheNames, logger, waitUntil, WorkboxError } from '@vite-pwa/workbox-core/internals' +import type { RouteHandlerCallback, WorkboxPlugin } from '../core/types' +import type { Strategy } from '../strategies/Strategy' +import type { CleanupResult, InstallResult, PrecacheEntry } from './types' +import { assert, privateCacheNames as cacheNames, logger, waitUntil, WorkboxError } from '../core/internals' import { PrecacheStrategy } from './PrecacheStrategy' import { createCacheKey } from './utils/createCacheKey' import { PrecacheCacheKeyPlugin } from './utils/PrecacheCacheKeyPlugin' diff --git a/packages/workbox-precaching/src/PrecacheFallbackPlugin.ts b/packages/workbox-sw/src/precaching/PrecacheFallbackPlugin.ts similarity index 97% rename from packages/workbox-precaching/src/PrecacheFallbackPlugin.ts rename to packages/workbox-sw/src/precaching/PrecacheFallbackPlugin.ts index 290602c..51db859 100644 --- a/packages/workbox-precaching/src/PrecacheFallbackPlugin.ts +++ b/packages/workbox-sw/src/precaching/PrecacheFallbackPlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../core/types' import type { PrecacheController } from './PrecacheController' import { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController' diff --git a/packages/workbox-precaching/src/PrecacheRoute.ts b/packages/workbox-sw/src/precaching/PrecacheRoute.ts similarity index 92% rename from packages/workbox-precaching/src/PrecacheRoute.ts rename to packages/workbox-sw/src/precaching/PrecacheRoute.ts index 9047ba5..be18891 100644 --- a/packages/workbox-precaching/src/PrecacheRoute.ts +++ b/packages/workbox-sw/src/precaching/PrecacheRoute.ts @@ -9,12 +9,12 @@ import type { RouteMatchCallback, RouteMatchCallbackOptions, -} from '@vite-pwa/workbox-core' -import type { PrecacheRouteOptions } from './_types' +} from '../core/types' import type { PrecacheController } from './PrecacheController' -import { getFriendlyURL, logger } from '@vite-pwa/workbox-core/internals' +import type { PrecacheRouteOptions } from './types' +import { getFriendlyURL, logger } from '../core/internals' -import { Route } from '@vite-pwa/workbox-routing' +import { Route } from '../routing/Route' import { generateURLVariations } from './utils/generateURLVariations' /** diff --git a/packages/workbox-precaching/src/PrecacheStrategy.ts b/packages/workbox-sw/src/precaching/PrecacheStrategy.ts similarity index 97% rename from packages/workbox-precaching/src/PrecacheStrategy.ts rename to packages/workbox-sw/src/precaching/PrecacheStrategy.ts index 88f3314..c2405aa 100644 --- a/packages/workbox-precaching/src/PrecacheStrategy.ts +++ b/packages/workbox-sw/src/precaching/PrecacheStrategy.ts @@ -6,11 +6,12 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' -import type { StrategyHandler, StrategyOptions } from '@vite-pwa/workbox-strategies' -import { copyResponse } from '@vite-pwa/workbox-core' -import { privateCacheNames as cacheNames, getFriendlyURL, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' -import { Strategy } from '@vite-pwa/workbox-strategies' +import type { WorkboxPlugin } from '../core/types' +import type { StrategyOptions } from '../strategies/Strategy' +import type { StrategyHandler } from '../strategies/StrategyHandler' +import { copyResponse } from '../core/index' +import { privateCacheNames as cacheNames, getFriendlyURL, logger, WorkboxError } from '../core/internals' +import { Strategy } from '../strategies/Strategy' interface PrecacheStrategyOptions extends StrategyOptions { fallbackToNetwork?: boolean diff --git a/packages/workbox-precaching/src/addPlugins.ts b/packages/workbox-sw/src/precaching/addPlugins.ts similarity index 90% rename from packages/workbox-precaching/src/addPlugins.ts rename to packages/workbox-sw/src/precaching/addPlugins.ts index 476185d..0d224f5 100644 --- a/packages/workbox-precaching/src/addPlugins.ts +++ b/packages/workbox-sw/src/precaching/addPlugins.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../core/types' import { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController' /** diff --git a/packages/workbox-precaching/src/addRoute.ts b/packages/workbox-sw/src/precaching/addRoute.ts similarity index 90% rename from packages/workbox-precaching/src/addRoute.ts rename to packages/workbox-sw/src/precaching/addRoute.ts index 3c5e1e1..5ecb835 100644 --- a/packages/workbox-precaching/src/addRoute.ts +++ b/packages/workbox-sw/src/precaching/addRoute.ts @@ -5,9 +5,9 @@ https://opensource.org/licenses/MIT. */ -import type { PrecacheRouteOptions } from './_types' +import type { PrecacheRouteOptions } from './types' -import { registerRoute } from '@vite-pwa/workbox-routing' +import { registerRoute } from '../routing/registerRoute' import { PrecacheRoute } from './PrecacheRoute' import { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController' diff --git a/packages/workbox-precaching/src/cleanupOutdatedCaches.ts b/packages/workbox-sw/src/precaching/cleanupOutdatedCaches.ts similarity index 93% rename from packages/workbox-precaching/src/cleanupOutdatedCaches.ts rename to packages/workbox-sw/src/precaching/cleanupOutdatedCaches.ts index 0815f7a..9ca8fd5 100644 --- a/packages/workbox-precaching/src/cleanupOutdatedCaches.ts +++ b/packages/workbox-sw/src/precaching/cleanupOutdatedCaches.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { privateCacheNames as cacheNames, logger } from '@vite-pwa/workbox-core/internals' +import { privateCacheNames as cacheNames, logger } from '../core/internals' import { deleteOutdatedCaches } from './utils/deleteOutdatedCaches' /** diff --git a/packages/workbox-precaching/src/createHandlerBoundToURL.ts b/packages/workbox-sw/src/precaching/createHandlerBoundToURL.ts similarity index 93% rename from packages/workbox-precaching/src/createHandlerBoundToURL.ts rename to packages/workbox-sw/src/precaching/createHandlerBoundToURL.ts index ddbaf35..49c0a93 100644 --- a/packages/workbox-precaching/src/createHandlerBoundToURL.ts +++ b/packages/workbox-sw/src/precaching/createHandlerBoundToURL.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandlerCallback } from '@vite-pwa/workbox-core' +import type { RouteHandlerCallback } from '../core/types' import { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController' /** diff --git a/packages/workbox-precaching/src/getCacheKeyForURL.ts b/packages/workbox-sw/src/precaching/getCacheKeyForURL.ts similarity index 100% rename from packages/workbox-precaching/src/getCacheKeyForURL.ts rename to packages/workbox-sw/src/precaching/getCacheKeyForURL.ts diff --git a/packages/workbox-precaching/src/index.ts b/packages/workbox-sw/src/precaching/index.ts similarity index 98% rename from packages/workbox-precaching/src/index.ts rename to packages/workbox-sw/src/precaching/index.ts index f409f01..a9a1990 100644 --- a/packages/workbox-precaching/src/index.ts +++ b/packages/workbox-sw/src/precaching/index.ts @@ -47,4 +47,4 @@ export { PrecacheStrategy, } -export * from './_types' +export * from './types' diff --git a/packages/workbox-precaching/src/matchPrecache.ts b/packages/workbox-sw/src/precaching/matchPrecache.ts similarity index 100% rename from packages/workbox-precaching/src/matchPrecache.ts rename to packages/workbox-sw/src/precaching/matchPrecache.ts diff --git a/packages/workbox-precaching/src/precache.ts b/packages/workbox-sw/src/precaching/precache.ts similarity index 95% rename from packages/workbox-precaching/src/precache.ts rename to packages/workbox-sw/src/precaching/precache.ts index b27afd8..88125a0 100644 --- a/packages/workbox-precaching/src/precache.ts +++ b/packages/workbox-sw/src/precaching/precache.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { PrecacheEntry } from './_types' +import type { PrecacheEntry } from './types' import { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController' /** diff --git a/packages/workbox-precaching/src/precacheAndRoute.ts b/packages/workbox-sw/src/precaching/precacheAndRoute.ts similarity index 92% rename from packages/workbox-precaching/src/precacheAndRoute.ts rename to packages/workbox-sw/src/precaching/precacheAndRoute.ts index b126a66..bde9ac2 100644 --- a/packages/workbox-precaching/src/precacheAndRoute.ts +++ b/packages/workbox-sw/src/precaching/precacheAndRoute.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { PrecacheEntry, PrecacheRouteOptions } from './_types' +import type { PrecacheEntry, PrecacheRouteOptions } from './types' import { addRoute } from './addRoute' import { precache } from './precache' diff --git a/packages/workbox-precaching/src/_types.ts b/packages/workbox-sw/src/precaching/types.ts similarity index 100% rename from packages/workbox-precaching/src/_types.ts rename to packages/workbox-sw/src/precaching/types.ts diff --git a/packages/workbox-precaching/src/utils/PrecacheCacheKeyPlugin.ts b/packages/workbox-sw/src/precaching/utils/PrecacheCacheKeyPlugin.ts similarity index 98% rename from packages/workbox-precaching/src/utils/PrecacheCacheKeyPlugin.ts rename to packages/workbox-sw/src/precaching/utils/PrecacheCacheKeyPlugin.ts index 1278e4d..5fd069c 100644 --- a/packages/workbox-precaching/src/utils/PrecacheCacheKeyPlugin.ts +++ b/packages/workbox-sw/src/precaching/utils/PrecacheCacheKeyPlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin, WorkboxPluginCallbackParam } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin, WorkboxPluginCallbackParam } from '../../core/types' import type { PrecacheController } from '../PrecacheController' /** diff --git a/packages/workbox-precaching/src/utils/PrecacheInstallReportPlugin.ts b/packages/workbox-sw/src/precaching/utils/PrecacheInstallReportPlugin.ts similarity index 98% rename from packages/workbox-precaching/src/utils/PrecacheInstallReportPlugin.ts rename to packages/workbox-sw/src/precaching/utils/PrecacheInstallReportPlugin.ts index e0e7607..19f2c2f 100644 --- a/packages/workbox-precaching/src/utils/PrecacheInstallReportPlugin.ts +++ b/packages/workbox-sw/src/precaching/utils/PrecacheInstallReportPlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin, WorkboxPluginCallbackParam } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin, WorkboxPluginCallbackParam } from '../../core/types' /** * A plugin, designed to be used with PrecacheController, to determine the diff --git a/packages/workbox-precaching/src/utils/createCacheKey.ts b/packages/workbox-sw/src/precaching/utils/createCacheKey.ts similarity index 94% rename from packages/workbox-precaching/src/utils/createCacheKey.ts rename to packages/workbox-sw/src/precaching/utils/createCacheKey.ts index 8228969..2d9da10 100644 --- a/packages/workbox-precaching/src/utils/createCacheKey.ts +++ b/packages/workbox-sw/src/precaching/utils/createCacheKey.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import type { PrecacheEntry } from '../_types' -import { WorkboxError } from '@vite-pwa/workbox-core/internals' +import type { PrecacheEntry } from '../types' +import { WorkboxError } from '../../core/internals' interface CacheKey { cacheKey: string diff --git a/packages/workbox-precaching/src/utils/deleteOutdatedCaches.ts b/packages/workbox-sw/src/precaching/utils/deleteOutdatedCaches.ts similarity index 100% rename from packages/workbox-precaching/src/utils/deleteOutdatedCaches.ts rename to packages/workbox-sw/src/precaching/utils/deleteOutdatedCaches.ts diff --git a/packages/workbox-precaching/src/utils/generateURLVariations.ts b/packages/workbox-sw/src/precaching/utils/generateURLVariations.ts similarity index 91% rename from packages/workbox-precaching/src/utils/generateURLVariations.ts rename to packages/workbox-sw/src/precaching/utils/generateURLVariations.ts index 7afaa7d..893bf2f 100644 --- a/packages/workbox-precaching/src/utils/generateURLVariations.ts +++ b/packages/workbox-sw/src/precaching/utils/generateURLVariations.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { PrecacheRouteOptions } from '../_types' +import type { PrecacheRouteOptions } from '../types' import { removeIgnoredSearchParams } from './removeIgnoredSearchParams' /** @@ -14,7 +14,7 @@ import { removeIgnoredSearchParams } from './removeIgnoredSearchParams' * check, one at a time. * * @param {string} url - * @param {import('../_types').PrecacheRouteOptions} [options]')} options + * @param {import('../types').PrecacheRouteOptions} [options]')} options * * @private */ diff --git a/packages/workbox-precaching/src/utils/getCacheKeyForURL.ts b/packages/workbox-sw/src/precaching/utils/getCacheKeyForURL.ts similarity index 95% rename from packages/workbox-precaching/src/utils/getCacheKeyForURL.ts rename to packages/workbox-sw/src/precaching/utils/getCacheKeyForURL.ts index 8876143..7dea71f 100644 --- a/packages/workbox-precaching/src/utils/getCacheKeyForURL.ts +++ b/packages/workbox-sw/src/precaching/utils/getCacheKeyForURL.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { PrecacheRouteOptions } from '../_types' +import type { PrecacheRouteOptions } from '../types' import { generateURLVariations } from './generateURLVariations' import { getOrCreatePrecacheController } from './getOrCreatePrecacheController' diff --git a/packages/workbox-precaching/src/utils/getOrCreatePrecacheController.ts b/packages/workbox-sw/src/precaching/utils/getOrCreatePrecacheController.ts similarity index 100% rename from packages/workbox-precaching/src/utils/getOrCreatePrecacheController.ts rename to packages/workbox-sw/src/precaching/utils/getOrCreatePrecacheController.ts diff --git a/packages/workbox-precaching/src/utils/printCleanupDetails.ts b/packages/workbox-sw/src/precaching/utils/printCleanupDetails.ts similarity index 94% rename from packages/workbox-precaching/src/utils/printCleanupDetails.ts rename to packages/workbox-sw/src/precaching/utils/printCleanupDetails.ts index 026948a..21b3cc5 100644 --- a/packages/workbox-precaching/src/utils/printCleanupDetails.ts +++ b/packages/workbox-sw/src/precaching/utils/printCleanupDetails.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { logger } from '@vite-pwa/workbox-core/internals' +import { logger } from '../../core/internals' /** * @param {string} groupTitle diff --git a/packages/workbox-precaching/src/utils/printInstallDetails.ts b/packages/workbox-sw/src/precaching/utils/printInstallDetails.ts similarity index 96% rename from packages/workbox-precaching/src/utils/printInstallDetails.ts rename to packages/workbox-sw/src/precaching/utils/printInstallDetails.ts index d81ff3f..f55a134 100644 --- a/packages/workbox-precaching/src/utils/printInstallDetails.ts +++ b/packages/workbox-sw/src/precaching/utils/printInstallDetails.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { logger } from '@vite-pwa/workbox-core/internals' +import { logger } from '../../core/internals' /** * @param {string} groupTitle diff --git a/packages/workbox-precaching/src/utils/removeIgnoredSearchParams.ts b/packages/workbox-sw/src/precaching/utils/removeIgnoredSearchParams.ts similarity index 100% rename from packages/workbox-precaching/src/utils/removeIgnoredSearchParams.ts rename to packages/workbox-sw/src/precaching/utils/removeIgnoredSearchParams.ts diff --git a/packages/workbox-range-requests/src/RangeRequestsPlugin.ts b/packages/workbox-sw/src/range-requests/RangeRequestsPlugin.ts similarity index 96% rename from packages/workbox-range-requests/src/RangeRequestsPlugin.ts rename to packages/workbox-sw/src/range-requests/RangeRequestsPlugin.ts index 35cce9b..a5d551d 100644 --- a/packages/workbox-range-requests/src/RangeRequestsPlugin.ts +++ b/packages/workbox-sw/src/range-requests/RangeRequestsPlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../core/types' import { createPartialResponse } from './createPartialResponse' /** diff --git a/packages/workbox-range-requests/src/createPartialResponse.ts b/packages/workbox-sw/src/range-requests/createPartialResponse.ts similarity index 97% rename from packages/workbox-range-requests/src/createPartialResponse.ts rename to packages/workbox-sw/src/range-requests/createPartialResponse.ts index 4282ab9..5467897 100644 --- a/packages/workbox-range-requests/src/createPartialResponse.ts +++ b/packages/workbox-sw/src/range-requests/createPartialResponse.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { assert, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, logger, WorkboxError } from '../core/internals' import { calculateEffectiveBoundaries } from './utils/calculateEffectiveBoundaries' import { parseRangeHeader } from './utils/parseRangeHeader' diff --git a/packages/workbox-range-requests/src/index.ts b/packages/workbox-sw/src/range-requests/index.ts similarity index 100% rename from packages/workbox-range-requests/src/index.ts rename to packages/workbox-sw/src/range-requests/index.ts diff --git a/packages/workbox-range-requests/src/utils/calculateEffectiveBoundaries.ts b/packages/workbox-sw/src/range-requests/utils/calculateEffectiveBoundaries.ts similarity index 95% rename from packages/workbox-range-requests/src/utils/calculateEffectiveBoundaries.ts rename to packages/workbox-sw/src/range-requests/utils/calculateEffectiveBoundaries.ts index bf1bc68..b3c4cb9 100644 --- a/packages/workbox-range-requests/src/utils/calculateEffectiveBoundaries.ts +++ b/packages/workbox-sw/src/range-requests/utils/calculateEffectiveBoundaries.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { assert, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, WorkboxError } from '../../core/internals' /** * @param {Blob} blob A source blob. diff --git a/packages/workbox-range-requests/src/utils/parseRangeHeader.ts b/packages/workbox-sw/src/range-requests/utils/parseRangeHeader.ts similarity index 96% rename from packages/workbox-range-requests/src/utils/parseRangeHeader.ts rename to packages/workbox-sw/src/range-requests/utils/parseRangeHeader.ts index 5fdb2d5..26ac8c9 100644 --- a/packages/workbox-range-requests/src/utils/parseRangeHeader.ts +++ b/packages/workbox-sw/src/range-requests/utils/parseRangeHeader.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { assert, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, WorkboxError } from '../../core/internals' /** * @param {string} rangeHeader A Range: header value. diff --git a/packages/workbox-recipes/src/googleFontsCache.ts b/packages/workbox-sw/src/recipes/googleFontsCache.ts similarity index 84% rename from packages/workbox-recipes/src/googleFontsCache.ts rename to packages/workbox-sw/src/recipes/googleFontsCache.ts index a23f40a..2f17b0f 100644 --- a/packages/workbox-recipes/src/googleFontsCache.ts +++ b/packages/workbox-sw/src/recipes/googleFontsCache.ts @@ -1,5 +1,5 @@ -import { CacheableResponsePlugin } from '@vite-pwa/workbox-cacheable-response' -import { ExpirationPlugin } from '@vite-pwa/workbox-expiration' +import { CacheableResponsePlugin } from '../cacheable-response/CacheableResponsePlugin' +import { ExpirationPlugin } from '../expiration/ExpirationPlugin' /* Copyright 2020 Google LLC, Vite PWA's Team @@ -7,8 +7,9 @@ import { ExpirationPlugin } from '@vite-pwa/workbox-expiration' license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT. */ -import { registerRoute } from '@vite-pwa/workbox-routing' -import { CacheFirst, StaleWhileRevalidate } from '@vite-pwa/workbox-strategies' +import { registerRoute } from '../routing/registerRoute' +import { CacheFirst } from '../strategies/CacheFirst' +import { StaleWhileRevalidate } from '../strategies/StaleWhileRevalidate' export interface GoogleFontCacheOptions { cachePrefix?: string diff --git a/packages/workbox-recipes/src/imageCache.ts b/packages/workbox-sw/src/recipes/imageCache.ts similarity index 88% rename from packages/workbox-recipes/src/imageCache.ts rename to packages/workbox-sw/src/recipes/imageCache.ts index 19d2fc4..4e0264c 100644 --- a/packages/workbox-recipes/src/imageCache.ts +++ b/packages/workbox-sw/src/recipes/imageCache.ts @@ -10,11 +10,11 @@ import type { RouteMatchCallback, RouteMatchCallbackOptions, WorkboxPlugin, -} from '@vite-pwa/workbox-core' -import { CacheableResponsePlugin } from '@vite-pwa/workbox-cacheable-response' -import { ExpirationPlugin } from '@vite-pwa/workbox-expiration' -import { registerRoute } from '@vite-pwa/workbox-routing' -import { CacheFirst } from '@vite-pwa/workbox-strategies' +} from '../core/types' +import { CacheableResponsePlugin } from '../cacheable-response/CacheableResponsePlugin' +import { ExpirationPlugin } from '../expiration/ExpirationPlugin' +import { registerRoute } from '../routing/registerRoute' +import { CacheFirst } from '../strategies/CacheFirst' import { warmStrategyCache } from './warmStrategyCache' export interface ImageCacheOptions { diff --git a/packages/workbox-recipes/src/index.ts b/packages/workbox-sw/src/recipes/index.ts similarity index 100% rename from packages/workbox-recipes/src/index.ts rename to packages/workbox-sw/src/recipes/index.ts diff --git a/packages/workbox-recipes/src/offlineFallback.ts b/packages/workbox-sw/src/recipes/offlineFallback.ts similarity index 94% rename from packages/workbox-recipes/src/offlineFallback.ts rename to packages/workbox-sw/src/recipes/offlineFallback.ts index bd2b952..2501aea 100644 --- a/packages/workbox-recipes/src/offlineFallback.ts +++ b/packages/workbox-sw/src/recipes/offlineFallback.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandler, RouteHandlerCallbackOptions } from '@vite-pwa/workbox-core' -import { matchPrecache } from '@vite-pwa/workbox-precaching' -import { setCatchHandler } from '@vite-pwa/workbox-routing' +import type { RouteHandler, RouteHandlerCallbackOptions } from '../core/types' +import { matchPrecache } from '../precaching/matchPrecache' +import { setCatchHandler } from '../routing/setCatchHandler' export interface OfflineFallbackOptions { pageFallback?: string diff --git a/packages/workbox-recipes/src/pageCache.ts b/packages/workbox-sw/src/recipes/pageCache.ts similarity index 89% rename from packages/workbox-recipes/src/pageCache.ts rename to packages/workbox-sw/src/recipes/pageCache.ts index c150a4b..0449de9 100644 --- a/packages/workbox-recipes/src/pageCache.ts +++ b/packages/workbox-sw/src/recipes/pageCache.ts @@ -10,10 +10,10 @@ import type { RouteMatchCallback, RouteMatchCallbackOptions, WorkboxPlugin, -} from '@vite-pwa/workbox-core' -import { CacheableResponsePlugin } from '@vite-pwa/workbox-cacheable-response' -import { registerRoute } from '@vite-pwa/workbox-routing' -import { NetworkFirst } from '@vite-pwa/workbox-strategies' +} from '../core/types' +import { CacheableResponsePlugin } from '../cacheable-response/CacheableResponsePlugin' +import { registerRoute } from '../routing/registerRoute' +import { NetworkFirst } from '../strategies/NetworkFirst' import { warmStrategyCache } from './warmStrategyCache' export interface PageCacheOptions { diff --git a/packages/workbox-recipes/src/staticResourceCache.ts b/packages/workbox-sw/src/recipes/staticResourceCache.ts similarity index 89% rename from packages/workbox-recipes/src/staticResourceCache.ts rename to packages/workbox-sw/src/recipes/staticResourceCache.ts index 82a4f4d..9bac097 100644 --- a/packages/workbox-recipes/src/staticResourceCache.ts +++ b/packages/workbox-sw/src/recipes/staticResourceCache.ts @@ -10,10 +10,10 @@ import type { RouteMatchCallback, RouteMatchCallbackOptions, WorkboxPlugin, -} from '@vite-pwa/workbox-core' -import { CacheableResponsePlugin } from '@vite-pwa/workbox-cacheable-response' -import { registerRoute } from '@vite-pwa/workbox-routing' -import { StaleWhileRevalidate } from '@vite-pwa/workbox-strategies' +} from '../core/types' +import { CacheableResponsePlugin } from '../cacheable-response/CacheableResponsePlugin' +import { registerRoute } from '../routing/registerRoute' +import { StaleWhileRevalidate } from '../strategies/StaleWhileRevalidate' import { warmStrategyCache } from './warmStrategyCache' export interface StaticResourceOptions { diff --git a/packages/workbox-recipes/src/warmStrategyCache.ts b/packages/workbox-sw/src/recipes/warmStrategyCache.ts similarity index 93% rename from packages/workbox-recipes/src/warmStrategyCache.ts rename to packages/workbox-sw/src/recipes/warmStrategyCache.ts index 0fada5e..8b2b438 100644 --- a/packages/workbox-recipes/src/warmStrategyCache.ts +++ b/packages/workbox-sw/src/recipes/warmStrategyCache.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { Strategy } from '@vite-pwa/workbox-strategies' +import type { Strategy } from '../strategies/Strategy' export interface WarmStrategyCacheOptions { urls: Array diff --git a/packages/workbox-routing/src/NavigationRoute.ts b/packages/workbox-sw/src/routing/NavigationRoute.ts similarity index 96% rename from packages/workbox-routing/src/NavigationRoute.ts rename to packages/workbox-sw/src/routing/NavigationRoute.ts index 9a9cf19..e35714c 100644 --- a/packages/workbox-routing/src/NavigationRoute.ts +++ b/packages/workbox-sw/src/routing/NavigationRoute.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandler, RouteMatchCallbackOptions } from '@vite-pwa/workbox-core' -import { assert, logger } from '@vite-pwa/workbox-core/internals' +import type { RouteHandler, RouteMatchCallbackOptions } from '../core/types' +import { assert, logger } from '../core/internals' import { Route } from './Route' diff --git a/packages/workbox-routing/src/RegExpRoute.ts b/packages/workbox-sw/src/routing/RegExpRoute.ts similarity index 96% rename from packages/workbox-routing/src/RegExpRoute.ts rename to packages/workbox-sw/src/routing/RegExpRoute.ts index ebd3548..e3dd351 100644 --- a/packages/workbox-routing/src/RegExpRoute.ts +++ b/packages/workbox-sw/src/routing/RegExpRoute.ts @@ -10,9 +10,9 @@ import type { RouteHandler, RouteMatchCallback, RouteMatchCallbackOptions, -} from '@vite-pwa/workbox-core' +} from '../core/types' import type { HTTPMethod } from './utils/constants' -import { assert, logger } from '@vite-pwa/workbox-core/internals' +import { assert, logger } from '../core/internals' import { Route } from './Route' /** diff --git a/packages/workbox-routing/src/Route.ts b/packages/workbox-sw/src/routing/Route.ts similarity index 96% rename from packages/workbox-routing/src/Route.ts rename to packages/workbox-sw/src/routing/Route.ts index 1926035..0e4ffe4 100644 --- a/packages/workbox-routing/src/Route.ts +++ b/packages/workbox-sw/src/routing/Route.ts @@ -10,9 +10,9 @@ import type { RouteHandler, RouteHandlerObject, RouteMatchCallback, -} from '@vite-pwa/workbox-core' +} from '../core/types' import type { HTTPMethod } from './utils/constants' -import { assert } from '@vite-pwa/workbox-core/internals' +import { assert } from '../core/internals' import { defaultMethod, validMethods } from './utils/constants' import { normalizeHandler } from './utils/normalizeHandler' diff --git a/packages/workbox-routing/src/Router.ts b/packages/workbox-sw/src/routing/Router.ts similarity index 99% rename from packages/workbox-routing/src/Router.ts rename to packages/workbox-sw/src/routing/Router.ts index 5f97d67..1edc0b4 100644 --- a/packages/workbox-routing/src/Router.ts +++ b/packages/workbox-sw/src/routing/Router.ts @@ -11,10 +11,10 @@ import type { RouteHandlerCallbackOptions, RouteHandlerObject, RouteMatchCallbackOptions, -} from '@vite-pwa/workbox-core' +} from '../core/types' import type { Route } from './Route' import type { HTTPMethod } from './utils/constants' -import { assert, getFriendlyURL, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, getFriendlyURL, logger, WorkboxError } from '../core/internals' import { defaultMethod } from './utils/constants' import { normalizeHandler } from './utils/normalizeHandler' diff --git a/packages/workbox-routing/src/_types.ts b/packages/workbox-sw/src/routing/_types.ts similarity index 100% rename from packages/workbox-routing/src/_types.ts rename to packages/workbox-sw/src/routing/_types.ts diff --git a/packages/workbox-routing/src/index.ts b/packages/workbox-sw/src/routing/index.ts similarity index 100% rename from packages/workbox-routing/src/index.ts rename to packages/workbox-sw/src/routing/index.ts diff --git a/packages/workbox-routing/src/registerRoute.ts b/packages/workbox-sw/src/routing/registerRoute.ts similarity index 96% rename from packages/workbox-routing/src/registerRoute.ts rename to packages/workbox-sw/src/routing/registerRoute.ts index 0b256bf..83e0cf8 100644 --- a/packages/workbox-routing/src/registerRoute.ts +++ b/packages/workbox-sw/src/routing/registerRoute.ts @@ -6,9 +6,9 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandler, RouteMatchCallback } from '@vite-pwa/workbox-core' +import type { RouteHandler, RouteMatchCallback } from '../core/types' import type { HTTPMethod } from './utils/constants' -import { logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { logger, WorkboxError } from '../core/internals' import { RegExpRoute } from './RegExpRoute' import { Route } from './Route' diff --git a/packages/workbox-routing/src/setCatchHandler.ts b/packages/workbox-sw/src/routing/setCatchHandler.ts similarity index 92% rename from packages/workbox-routing/src/setCatchHandler.ts rename to packages/workbox-sw/src/routing/setCatchHandler.ts index ed8ecb6..afdf28f 100644 --- a/packages/workbox-routing/src/setCatchHandler.ts +++ b/packages/workbox-sw/src/routing/setCatchHandler.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandler } from '@vite-pwa/workbox-core' +import type { RouteHandler } from '../core/types' import { getOrCreateDefaultRouter } from './utils/getOrCreateDefaultRouter' diff --git a/packages/workbox-routing/src/setDefaultHandler.ts b/packages/workbox-sw/src/routing/setDefaultHandler.ts similarity index 93% rename from packages/workbox-routing/src/setDefaultHandler.ts rename to packages/workbox-sw/src/routing/setDefaultHandler.ts index 20d8a2f..b978d8c 100644 --- a/packages/workbox-routing/src/setDefaultHandler.ts +++ b/packages/workbox-sw/src/routing/setDefaultHandler.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandler } from '@vite-pwa/workbox-core' +import type { RouteHandler } from '../core/types' import { getOrCreateDefaultRouter } from './utils/getOrCreateDefaultRouter' diff --git a/packages/workbox-routing/src/types.ts b/packages/workbox-sw/src/routing/types.ts similarity index 100% rename from packages/workbox-routing/src/types.ts rename to packages/workbox-sw/src/routing/types.ts diff --git a/packages/workbox-routing/src/utils/constants.ts b/packages/workbox-sw/src/routing/utils/constants.ts similarity index 100% rename from packages/workbox-routing/src/utils/constants.ts rename to packages/workbox-sw/src/routing/utils/constants.ts diff --git a/packages/workbox-routing/src/utils/getOrCreateDefaultRouter.ts b/packages/workbox-sw/src/routing/utils/getOrCreateDefaultRouter.ts similarity index 100% rename from packages/workbox-routing/src/utils/getOrCreateDefaultRouter.ts rename to packages/workbox-sw/src/routing/utils/getOrCreateDefaultRouter.ts diff --git a/packages/workbox-routing/src/utils/normalizeHandler.ts b/packages/workbox-sw/src/routing/utils/normalizeHandler.ts similarity index 89% rename from packages/workbox-routing/src/utils/normalizeHandler.ts rename to packages/workbox-sw/src/routing/utils/normalizeHandler.ts index b4c8bcc..56e471d 100644 --- a/packages/workbox-routing/src/utils/normalizeHandler.ts +++ b/packages/workbox-sw/src/routing/utils/normalizeHandler.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import type { RouteHandler, RouteHandlerObject } from '@vite-pwa/workbox-core' -import { assert } from '@vite-pwa/workbox-core/internals' +import type { RouteHandler, RouteHandlerObject } from '../../core/types' +import { assert } from '../../core/internals' /** * @param {function() | object} handler Either a function, or an object with a diff --git a/packages/workbox-strategies/src/CacheFirst.ts b/packages/workbox-sw/src/strategies/CacheFirst.ts similarity index 94% rename from packages/workbox-strategies/src/CacheFirst.ts rename to packages/workbox-sw/src/strategies/CacheFirst.ts index fc52bb6..dd158db 100644 --- a/packages/workbox-strategies/src/CacheFirst.ts +++ b/packages/workbox-sw/src/strategies/CacheFirst.ts @@ -7,7 +7,7 @@ */ import type { StrategyHandler } from './StrategyHandler' -import { assert, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, logger, WorkboxError } from '../core/internals' import { Strategy } from './Strategy' import { messages } from './utils/messages' @@ -26,7 +26,7 @@ class CacheFirst extends Strategy { /** * @private * @param request {Request|string} A request to run this strategy for. - * @param handler {import('@vite-pwa/workbox-strategies').StrategyHandler} The event that + * @param handler {import('./index').StrategyHandler} The event that * triggered the request. * @return {Promise} */ diff --git a/packages/workbox-strategies/src/CacheOnly.ts b/packages/workbox-sw/src/strategies/CacheOnly.ts similarity index 96% rename from packages/workbox-strategies/src/CacheOnly.ts rename to packages/workbox-sw/src/strategies/CacheOnly.ts index 5fa7504..374f9dc 100644 --- a/packages/workbox-strategies/src/CacheOnly.ts +++ b/packages/workbox-sw/src/strategies/CacheOnly.ts @@ -8,7 +8,7 @@ import type { StrategyHandler } from './StrategyHandler' -import { assert, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, logger, WorkboxError } from '../core/internals' import { Strategy } from './Strategy' import { messages } from './utils/messages' diff --git a/packages/workbox-strategies/src/NetworkFirst.ts b/packages/workbox-sw/src/strategies/NetworkFirst.ts similarity index 99% rename from packages/workbox-strategies/src/NetworkFirst.ts rename to packages/workbox-sw/src/strategies/NetworkFirst.ts index 5a34382..af10b78 100644 --- a/packages/workbox-strategies/src/NetworkFirst.ts +++ b/packages/workbox-sw/src/strategies/NetworkFirst.ts @@ -9,7 +9,7 @@ import type { StrategyOptions } from './Strategy' import type { StrategyHandler } from './StrategyHandler' -import { assert, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, logger, WorkboxError } from '../core/internals' import { cacheOkAndOpaquePlugin } from './plugins/cacheOkAndOpaquePlugin' import { Strategy } from './Strategy' import { messages } from './utils/messages' diff --git a/packages/workbox-strategies/src/NetworkOnly.ts b/packages/workbox-sw/src/strategies/NetworkOnly.ts similarity index 97% rename from packages/workbox-strategies/src/NetworkOnly.ts rename to packages/workbox-sw/src/strategies/NetworkOnly.ts index 7f1a305..b35a5ed 100644 --- a/packages/workbox-strategies/src/NetworkOnly.ts +++ b/packages/workbox-sw/src/strategies/NetworkOnly.ts @@ -8,7 +8,7 @@ import type { StrategyOptions } from './Strategy' import type { StrategyHandler } from './StrategyHandler' -import { assert, logger, timeout, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, logger, timeout, WorkboxError } from '../core/internals' import { Strategy } from './Strategy' import { messages } from './utils/messages' diff --git a/packages/workbox-strategies/src/StaleWhileRevalidate.ts b/packages/workbox-sw/src/strategies/StaleWhileRevalidate.ts similarity index 98% rename from packages/workbox-strategies/src/StaleWhileRevalidate.ts rename to packages/workbox-sw/src/strategies/StaleWhileRevalidate.ts index 2f92af2..ca99208 100644 --- a/packages/workbox-strategies/src/StaleWhileRevalidate.ts +++ b/packages/workbox-sw/src/strategies/StaleWhileRevalidate.ts @@ -8,7 +8,7 @@ import type { StrategyOptions } from './Strategy' import type { StrategyHandler } from './StrategyHandler' -import { assert, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import { assert, logger, WorkboxError } from '../core/internals' import { cacheOkAndOpaquePlugin } from './plugins/cacheOkAndOpaquePlugin' import { Strategy } from './Strategy' import { messages } from './utils/messages' diff --git a/packages/workbox-strategies/src/Strategy.ts b/packages/workbox-sw/src/strategies/Strategy.ts similarity index 99% rename from packages/workbox-strategies/src/Strategy.ts rename to packages/workbox-sw/src/strategies/Strategy.ts index 55786d1..fd765ad 100644 --- a/packages/workbox-strategies/src/Strategy.ts +++ b/packages/workbox-sw/src/strategies/Strategy.ts @@ -10,13 +10,13 @@ import type { HandlerCallbackOptions, RouteHandlerObject, WorkboxPlugin, -} from '@vite-pwa/workbox-core' +} from '../core/types' import { privateCacheNames as cacheNames, getFriendlyURL, logger, WorkboxError, -} from '@vite-pwa/workbox-core/internals' +} from '../core/internals' import { StrategyHandler } from './StrategyHandler' export interface StrategyOptions { diff --git a/packages/workbox-strategies/src/StrategyHandler.ts b/packages/workbox-sw/src/strategies/StrategyHandler.ts similarity index 99% rename from packages/workbox-strategies/src/StrategyHandler.ts rename to packages/workbox-sw/src/strategies/StrategyHandler.ts index 6d3e2c0..8f96e77 100644 --- a/packages/workbox-strategies/src/StrategyHandler.ts +++ b/packages/workbox-sw/src/strategies/StrategyHandler.ts @@ -11,7 +11,7 @@ import type { MapLikeObject, WorkboxPlugin, WorkboxPluginCallbackParam, -} from '@vite-pwa/workbox-core' +} from '../core/types' import type { Strategy } from './Strategy' import { assert, @@ -22,7 +22,7 @@ import { logger, timeout, WorkboxError, -} from '@vite-pwa/workbox-core/internals' +} from '../core/internals' function toRequest(input: RequestInfo) { return typeof input === 'string' ? new Request(input) : input diff --git a/packages/workbox-strategies/src/index.ts b/packages/workbox-sw/src/strategies/index.ts similarity index 100% rename from packages/workbox-strategies/src/index.ts rename to packages/workbox-sw/src/strategies/index.ts diff --git a/packages/workbox-strategies/src/plugins/cacheOkAndOpaquePlugin.ts b/packages/workbox-sw/src/strategies/plugins/cacheOkAndOpaquePlugin.ts similarity index 91% rename from packages/workbox-strategies/src/plugins/cacheOkAndOpaquePlugin.ts rename to packages/workbox-sw/src/strategies/plugins/cacheOkAndOpaquePlugin.ts index fb3fdd7..eb0f2ba 100644 --- a/packages/workbox-strategies/src/plugins/cacheOkAndOpaquePlugin.ts +++ b/packages/workbox-sw/src/strategies/plugins/cacheOkAndOpaquePlugin.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { WorkboxPlugin } from '@vite-pwa/workbox-core' +import type { WorkboxPlugin } from '../../core/types' export const cacheOkAndOpaquePlugin: WorkboxPlugin = { /** diff --git a/packages/workbox-strategies/src/utils/messages.ts b/packages/workbox-sw/src/strategies/utils/messages.ts similarity index 89% rename from packages/workbox-strategies/src/utils/messages.ts rename to packages/workbox-sw/src/strategies/utils/messages.ts index c794053..efe6578 100644 --- a/packages/workbox-strategies/src/utils/messages.ts +++ b/packages/workbox-sw/src/strategies/utils/messages.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { getFriendlyURL, logger } from '@vite-pwa/workbox-core/internals' +import { getFriendlyURL, logger } from '../../core/internals' export const messages = { strategyStart: (strategyName: string, request: Request): string => diff --git a/packages/workbox-streams/src/concatenate.ts b/packages/workbox-sw/src/streams/concatenate.ts similarity index 97% rename from packages/workbox-streams/src/concatenate.ts rename to packages/workbox-sw/src/streams/concatenate.ts index 519b0f8..642ea22 100644 --- a/packages/workbox-streams/src/concatenate.ts +++ b/packages/workbox-sw/src/streams/concatenate.ts @@ -6,8 +6,8 @@ https://opensource.org/licenses/MIT. */ -import type { StreamSource } from './_types' -import { assert, Deferred, logger, WorkboxError } from '@vite-pwa/workbox-core/internals' +import type { StreamSource } from './types' +import { assert, Deferred, logger, WorkboxError } from '../core/internals' /** * Takes either a Response, a ReadableStream, or a diff --git a/packages/workbox-streams/src/concatenateToResponse.ts b/packages/workbox-sw/src/streams/concatenateToResponse.ts similarity index 97% rename from packages/workbox-streams/src/concatenateToResponse.ts rename to packages/workbox-sw/src/streams/concatenateToResponse.ts index 5caf6ce..71c4bb3 100644 --- a/packages/workbox-streams/src/concatenateToResponse.ts +++ b/packages/workbox-sw/src/streams/concatenateToResponse.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import type { StreamSource } from './_types' +import type { StreamSource } from './types' import { concatenate } from './concatenate' import { createHeaders } from './utils/createHeaders' diff --git a/packages/workbox-streams/src/index.ts b/packages/workbox-sw/src/streams/index.ts similarity index 95% rename from packages/workbox-streams/src/index.ts rename to packages/workbox-sw/src/streams/index.ts index 4940f23..2acf7b7 100644 --- a/packages/workbox-streams/src/index.ts +++ b/packages/workbox-sw/src/streams/index.ts @@ -20,4 +20,4 @@ export { StreamsHandlerCallback, } -export * from './_types' +export * from './types' diff --git a/packages/workbox-streams/src/isSupported.ts b/packages/workbox-sw/src/streams/isSupported.ts similarity index 90% rename from packages/workbox-streams/src/isSupported.ts rename to packages/workbox-sw/src/streams/isSupported.ts index 17020b3..f10c46f 100644 --- a/packages/workbox-streams/src/isSupported.ts +++ b/packages/workbox-sw/src/streams/isSupported.ts @@ -6,7 +6,7 @@ https://opensource.org/licenses/MIT. */ -import { canConstructReadableStream } from '@vite-pwa/workbox-core/internals' +import { canConstructReadableStream } from '../core/internals' /** * This is a utility method that determines whether the current browser supports diff --git a/packages/workbox-streams/src/strategy.ts b/packages/workbox-sw/src/streams/strategy.ts similarity index 95% rename from packages/workbox-streams/src/strategy.ts rename to packages/workbox-sw/src/streams/strategy.ts index 880df49..180269d 100644 --- a/packages/workbox-streams/src/strategy.ts +++ b/packages/workbox-sw/src/streams/strategy.ts @@ -9,9 +9,9 @@ import type { RouteHandlerCallback, RouteHandlerCallbackOptions, -} from '@vite-pwa/workbox-core' -import type { StreamSource } from './_types' -import { logger } from '@vite-pwa/workbox-core/internals' +} from '../core/types' +import type { StreamSource } from './types' +import { logger } from '../core/internals' import { concatenateToResponse } from './concatenateToResponse' import { isSupported } from './isSupported' import { createHeaders } from './utils/createHeaders' diff --git a/packages/workbox-streams/src/_types.ts b/packages/workbox-sw/src/streams/types.ts similarity index 100% rename from packages/workbox-streams/src/_types.ts rename to packages/workbox-sw/src/streams/types.ts diff --git a/packages/workbox-streams/src/utils/createHeaders.ts b/packages/workbox-sw/src/streams/utils/createHeaders.ts similarity index 100% rename from packages/workbox-streams/src/utils/createHeaders.ts rename to packages/workbox-sw/src/streams/utils/createHeaders.ts diff --git a/packages/workbox-sw/src/sw/index.ts b/packages/workbox-sw/src/sw/index.ts new file mode 100644 index 0000000..52ea9ec --- /dev/null +++ b/packages/workbox-sw/src/sw/index.ts @@ -0,0 +1,13 @@ +/* + Copyright 2018 Google LLC, Vite PWA's Team + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + +import { WorkboxSw } from './workbox-sw' + +// Don't export anything, just expose a global. +// eslint-disable-next-line no-restricted-globals +self.workbox = new WorkboxSw() diff --git a/packages/workbox-sw/src/workbox-sw.ts b/packages/workbox-sw/src/sw/workbox-sw.ts similarity index 100% rename from packages/workbox-sw/src/workbox-sw.ts rename to packages/workbox-sw/src/sw/workbox-sw.ts diff --git a/packages/workbox-sw/src/types.ts b/packages/workbox-sw/src/types.ts new file mode 100644 index 0000000..0fca4a5 --- /dev/null +++ b/packages/workbox-sw/src/types.ts @@ -0,0 +1,13 @@ +export type * from './background-sync/types' +export type * from './broadcast-update/types' +export type * from './cacheable-response/types' +export type * from './core/types' +export type * from './expiration/types' +export type * from './precaching/index' +export type * from './range-requests/index' +export type * from './recipes/index' +export type * from './routing/types' +export type * from './strategies/index' +export type * from './streams/types' +export type * from './sw/index' +export type * from './types' diff --git a/packages/workbox-sw/tsdown.config.ts b/packages/workbox-sw/tsdown.config.ts index 944e76e..118a6cd 100644 --- a/packages/workbox-sw/tsdown.config.ts +++ b/packages/workbox-sw/tsdown.config.ts @@ -1,7 +1,32 @@ import { defineConfig } from 'tsdown' export default defineConfig({ - entry: './src/{index,workbox-sw}.ts', + entry: [ + 'src/index.ts', // <== barrel + 'src/types.ts', // <== all types + 'src/background-sync/index.ts', + 'src/background-sync/types.ts', + 'src/broadcast-update/index.ts', + 'src/broadcast-update/types.ts', + 'src/cacheable-response/index.ts', + 'src/cacheable-response/types.ts', + 'src/core/index.ts', + 'src/core/types.ts', + 'src/expiration/index.ts', + 'src/expiration/types.ts', + 'src/navigation-preload/index.ts', + 'src/precaching/index.ts', + 'src/precaching/types.ts', + 'src/range-requests/index.ts', + 'src/recipes/index.ts', + 'src/routing/index.ts', + 'src/routing/types.ts', + 'src/strategies/index.ts', + 'src/streams/index.ts', + 'src/streams/types.ts', + 'src/sw/index.ts', + ], + exports: true, platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team @@ -13,4 +38,24 @@ export default defineConfig({ define: { 'process.env.NODE_ENV': 'process.env.NODE_ENV', }, + noExternal: ['idb'], + exports: { + customExports(exp, { pkg }) { + // **/types contains only types: just replace the entry + const typesVersions: Record = {} + for (const [key, value] of Object.entries(exp)) { + // add typesVersion entry + if (key !== '.' && key !== './package.json') { + typesVersions[key.slice(2)] = [value.replace(/\.js$/, '.d.ts')] + } + if (key.endsWith('/types')) { + exp[key] = { types: value.replace(/\.js$/, '.d.ts') } + } + } + + pkg.typesVersions = { '*': typesVersions } + + return exp + }, + }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d8fdb9..43d2f01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,8 +10,8 @@ catalogs: specifier: ^10.3.1 version: 10.3.1 tsdown: - specifier: ^0.15.7 - version: 0.15.7 + specifier: https://pkg.pr.new/tsdown@c4f9764 + version: 0.15.9 eslint: '@antfu/eslint-config': specifier: ^6.0.0 @@ -111,7 +111,7 @@ importers: version: 19.7.0 tsdown: specifier: catalog:build - version: 0.15.7(typescript@5.9.3) + version: https://pkg.pr.new/tsdown@c4f9764(typescript@5.9.3) tsx: specifier: ^4.20.6 version: 4.20.6 @@ -122,64 +122,14 @@ importers: specifier: catalog:testing version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(@vitest/ui@3.2.4)(jiti@2.6.1)(tsx@4.20.6)(yaml@2.8.1) - packages/sw-tooltkit: {} - - packages/toolkit: - dependencies: - magicast: - specifier: file:magicast-0.3.5.tgz - version: file:packages/toolkit/magicast-0.3.5.tgz - pathe: - specifier: catalog:utils - version: 2.0.3 - pkg-types: - specifier: catalog:utils - version: 2.3.0 - proper-tags: - specifier: catalog:utils - version: 2.0.2 - recast: - specifier: catalog:utils - version: 0.23.11 - valibot: - specifier: catalog:utils - version: 1.1.0(typescript@5.9.3) - - packages/workbox-background-sync: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - idb: - specifier: catalog:utils - version: 8.0.3 - - packages/workbox-broadcast-update: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - packages/workbox-build: dependencies: - '@vite-pwa/workbox-background-sync': - specifier: workspace:* - version: link:../workbox-background-sync - '@vite-pwa/workbox-broadcast-update': + '@vite-pwa/workbox-sw': specifier: workspace:* - version: link:../workbox-broadcast-update - '@vite-pwa/workbox-cacheable-response': + version: link:../workbox-sw + '@vite-pwa/workbox-window': specifier: workspace:* - version: link:../workbox-cacheable-response - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - '@vite-pwa/workbox-expiration': - specifier: workspace:* - version: link:../workbox-expiration - '@vite-pwa/workbox-routing': - specifier: workspace:* - version: link:../workbox-routing + version: link:../workbox-window magicast: specifier: file:magicast-0.3.5.tgz version: file:packages/workbox-build/magicast-0.3.5.tgz @@ -199,91 +149,12 @@ importers: specifier: catalog:utils version: 1.1.0(typescript@5.9.3) - packages/workbox-cacheable-response: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - - packages/workbox-core: {} - - packages/workbox-expiration: + packages/workbox-sw: dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core idb: specifier: catalog:utils version: 8.0.3 - packages/workbox-navigation-preload: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - - packages/workbox-precaching: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - '@vite-pwa/workbox-routing': - specifier: workspace:* - version: link:../workbox-routing - '@vite-pwa/workbox-strategies': - specifier: workspace:* - version: link:../workbox-strategies - - packages/workbox-range-requests: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - - packages/workbox-recipes: - dependencies: - '@vite-pwa/workbox-cacheable-response': - specifier: workspace:* - version: link:../workbox-cacheable-response - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - '@vite-pwa/workbox-expiration': - specifier: workspace:* - version: link:../workbox-expiration - '@vite-pwa/workbox-precaching': - specifier: workspace:* - version: link:../workbox-precaching - '@vite-pwa/workbox-routing': - specifier: workspace:* - version: link:../workbox-routing - '@vite-pwa/workbox-strategies': - specifier: workspace:* - version: link:../workbox-strategies - - packages/workbox-routing: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - - packages/workbox-strategies: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - - packages/workbox-streams: - dependencies: - '@vite-pwa/workbox-core': - specifier: workspace:* - version: link:../workbox-core - '@vite-pwa/workbox-routing': - specifier: workspace:* - version: link:../workbox-routing - - packages/workbox-sw: {} - packages/workbox-window: dependencies: '@types/trusted-types': @@ -1662,6 +1533,9 @@ packages: get-tsconfig@4.12.0: resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + giget@2.0.0: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true @@ -1839,10 +1713,6 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - magicast@file:packages/toolkit/magicast-0.3.5.tgz: - resolution: {integrity: sha512-z7mBYVtGx5aFIFqOa6lBYuyKJqYUsvS85uB8zMDcBIZTtN3j7xlYh67g2WmHTZJbFX8i74T2t9g4KcZF9U5XGA==, tarball: file:packages/toolkit/magicast-0.3.5.tgz} - version: 0.3.5 - magicast@file:packages/workbox-build/magicast-0.3.5.tgz: resolution: {integrity: sha512-z7mBYVtGx5aFIFqOa6lBYuyKJqYUsvS85uB8zMDcBIZTtN3j7xlYh67g2WmHTZJbFX8i74T2t9g4KcZF9U5XGA==, tarball: file:packages/workbox-build/magicast-0.3.5.tgz} version: 0.3.5 @@ -2215,13 +2085,13 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rolldown-plugin-dts@0.16.11: - resolution: {integrity: sha512-9IQDaPvPqTx3RjG2eQCK5GYZITo203BxKunGI80AGYicu1ySFTUyugicAaTZWRzFWh9DSnzkgNeMNbDWBbSs0w==} + rolldown-plugin-dts@0.17.0: + resolution: {integrity: sha512-dZlFs5D6Qemo8Ch4Pz2tD2lhmfh74XSq8r+fUvVXJaP8iiunrF5wIQaoT7QIB/Q3HLMn8ETMR9mRn+Zh96FdNg==} engines: {node: '>=20.18.0'} peerDependencies: '@ts-macro/tsc': ^0.3.6 '@typescript/native-preview': '>=7.0.0-dev.20250601.1' - rolldown: ^1.0.0-beta.9 + rolldown: ^1.0.0-beta.44 typescript: ^5.0.0 vue-tsc: ~3.1.0 peerDependenciesMeta: @@ -2406,8 +2276,9 @@ packages: peerDependencies: typescript: '>=4.0.0' - tsdown@0.15.7: - resolution: {integrity: sha512-uFaVgWAogjOMqjY+CQwrUt3C6wzy6ynt82CIoXymnbS17ipUZ8WDXUceJjkislUahF/BZc5+W44Ue3p2oWtqUg==} + tsdown@https://pkg.pr.new/tsdown@c4f9764: + resolution: {tarball: https://pkg.pr.new/tsdown@c4f9764} + version: 0.15.9 engines: {node: '>=20.19.0'} hasBin: true peerDependencies: @@ -4001,6 +3872,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + giget@2.0.0: dependencies: citty: 0.1.6 @@ -4142,12 +4017,6 @@ snapshots: source-map-js: 1.2.1 optional: true - magicast@file:packages/toolkit/magicast-0.3.5.tgz: - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - source-map-js: 1.2.1 - magicast@file:packages/workbox-build/magicast-0.3.5.tgz: dependencies: '@babel/parser': 7.28.4 @@ -4698,7 +4567,7 @@ snapshots: reusify@1.1.0: {} - rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.44)(typescript@5.9.3): + rolldown-plugin-dts@0.17.0(rolldown@1.0.0-beta.44)(typescript@5.9.3): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -4707,7 +4576,7 @@ snapshots: birpc: 2.6.1 debug: 4.4.3 dts-resolver: 2.1.2 - get-tsconfig: 4.12.0 + get-tsconfig: 4.13.0 magic-string: 0.30.19 rolldown: 1.0.0-beta.44 optionalDependencies: @@ -4910,7 +4779,7 @@ snapshots: picomatch: 4.0.3 typescript: 5.9.3 - tsdown@0.15.7(typescript@5.9.3): + tsdown@https://pkg.pr.new/tsdown@c4f9764(typescript@5.9.3): dependencies: ansis: 4.2.0 cac: 6.7.14 @@ -4920,7 +4789,7 @@ snapshots: empathic: 2.0.0 hookable: 5.5.3 rolldown: 1.0.0-beta.44 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.44)(typescript@5.9.3) + rolldown-plugin-dts: 0.17.0(rolldown@1.0.0-beta.44)(typescript@5.9.3) semver: 7.7.3 tinyexec: 1.0.1 tinyglobby: 0.2.15 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 12c7add..e8a1ac0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,7 +6,7 @@ autoInstallPeers: true catalogs: build: bumpp: ^10.3.1 - tsdown: ^0.15.7 + tsdown: https://pkg.pr.new/tsdown@c4f9764 eslint: '@antfu/eslint-config': ^6.0.0 eslint: ^9.37.0 diff --git a/tsconfig.json b/tsconfig.json index 867b7ac..123d86a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,72 +5,24 @@ "module": "ESNext", "moduleResolution": "Bundler", "paths": { - "@vite-pwa/workbox-background-sync": [ - "./packages/workbox-background-sync/src/index.ts" - ], - "@vite-pwa/workbox-background-sync/*": [ - "./packages/workbox-background-sync/src/*.ts" - ], - "@vite-pwa/workbox-broadcast-update": [ - "./packages/workbox-broadcast-update/src/index.ts" - ], - "@vite-pwa/workbox-broadcast-update/*": [ - "./packages/workbox-broadcast-update/src/*.ts" - ], "@vite-pwa/workbox-build": [ "./packages/workbox-build/src/index.ts" ], "@vite-pwa/workbox-build/*": [ "./packages/workbox-build/src/*.ts" ], - "@vite-pwa/workbox-cacheable-response": [ - "./packages/workbox-cacheable-response/src/index.ts" - ], - "@vite-pwa/workbox-cacheable-response/*": [ - "./packages/workbox-cacheable-response/src/*.ts" - ], - "@vite-pwa/workbox-core": [ - "./packages/workbox-core/src/index.ts" - ], - "@vite-pwa/workbox-core/*": [ - "./packages/workbox-core/src/*.ts" - ], - "@vite-pwa/workbox-expiration": [ - "./packages/workbox-expiration/src/index.ts" - ], - "@vite-pwa/workbox-expiration/*": [ - "./packages/workbox-expiration/src/*.index" - ], - "@vite-pwa/workbox-navigation-preload": [ - "./packages/workbox-navigation-preload/src/" - ], - "@vite-pwa/workbox-precaching": [ - "./packages/workbox-precaching/src/" - ], - "@vite-pwa/workbox-range-requests": [ - "./packages/workbox-range-requests/src/" - ], - "@vite-pwa/workbox-recipes": [ - "./packages/workbox-recipes/src/" - ], - "@vite-pwa/workbox-routing": [ - "./packages/workbox-routing/src/index.ts" - ], - "@vite-pwa/workbox-routing/*": [ - "./packages/workbox-routing/src/*.ts" + "@vite-pwa/workbox-sw": [ + "./packages/workbox-sw/src/index.ts" ], - "@vite-pwa/workbox-strategies": [ - "./packages/workbox-strategies/src/" + "@vite-pwa/workbox-sw/*": [ + "./packages/workbox-sw/src/*.ts" ], - "@vite-pwa/workbox-streams": [ - "./packages/workbox-streams/src/" + "@vite-pwa/workbox-window": [ + "./packages/workbox-window/src/index.ts" ], - "@vite-pwa/workbox-sw": [ - "./packages/workbox-sw/src/" + "@vite-pwa/workbox-window/*": [ + "./packages/workbox-window/src/*.ts" ], - "@vite-pwa/workbox-window": [ - "./packages/workbox-window/src/" - ] }, "resolveJsonModule": true, "types": [ From cd6b952c5e244a1ff98d2e5099e1d4f6eaf69391 Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 25 Oct 2025 14:26:38 +0200 Subject: [PATCH 16/20] chore: use async valibot schemas --- packages/workbox-build/src/generate-sw.ts | 2 +- packages/workbox-build/src/types.ts | 13 +- .../src/validation/async-get-manifest.ts | 134 +----- .../src/validation/async-inject-manifest.ts | 11 +- .../src/validation/generate-sw.ts | 439 ------------------ .../src/validation/get-manifest.ts | 89 ---- .../src/validation/inject-manifest.ts | 115 ----- .../src/validation/validation-helper.ts | 45 +- .../workbox-build/test/validation.test.ts | 110 ++--- packages/workbox-sw/tsdown.config.ts | 7 +- tsconfig.json | 2 +- 11 files changed, 94 insertions(+), 873 deletions(-) delete mode 100644 packages/workbox-build/src/validation/generate-sw.ts delete mode 100644 packages/workbox-build/src/validation/get-manifest.ts delete mode 100644 packages/workbox-build/src/validation/inject-manifest.ts diff --git a/packages/workbox-build/src/generate-sw.ts b/packages/workbox-build/src/generate-sw.ts index bf2ca50..713a303 100644 --- a/packages/workbox-build/src/generate-sw.ts +++ b/packages/workbox-build/src/generate-sw.ts @@ -1,9 +1,9 @@ import type { ArrayExpression, Identifier, ObjectExpression, Program } from '@babel/types' import type { ProxifiedModule, ProxifiedObject } from 'magicast' import type { GenerateSWOptions } from './types' -import { AsyncGenerateSWOptionsSchema } from '@vite-pwa/workbox-build/validation/async-generate-sw' import { builders, generateCode, parseModule } from 'magicast' import { deepMergeObject, getDefaultExportOptions } from 'magicast/helpers' +import { AsyncGenerateSWOptionsSchema } from './validation/async-generate-sw' import { validateAsync } from './validation/validation-helper' // Helper para capitalizar: NetworkFirst -> NetworkFirst diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts index 027ce17..375f1ce 100644 --- a/packages/workbox-build/src/types.ts +++ b/packages/workbox-build/src/types.ts @@ -1,10 +1,9 @@ -import type { QueueOptions } from '@vite-pwa/workbox-background-sync/types' -import type { ExpirationPluginOptions } from '@vite-pwa/workbox-expiration/types' -import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-sw/src/broadcast-update/types' -import type { CacheableResponseOptions } from '@vite-pwa/workbox-sw/src/cacheable-response/types' -import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-sw/src/core/types' -// import type { GoogleAnalyticsInitializeOptions } from 'workbox-google-analytics/initialize' -import type { HTTPMethod } from '@vite-pwa/workbox-sw/src/routing/types' +import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-sw/broadcast-update/types' +import type { CacheableResponseOptions } from '@vite-pwa/workbox-sw/cacheable-response/types' +import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-sw/core/types' +import type { ExpirationPluginOptions } from '@vite-pwa/workbox-sw/expiration/types' +import type { HTTPMethod } from '@vite-pwa/workbox-sw/routing/types' +import type { QueueOptions } from '@vite-pwa/workbox-sw/types' import type { PackageJson } from 'pkg-types' export interface ManifestEntry { diff --git a/packages/workbox-build/src/validation/async-get-manifest.ts b/packages/workbox-build/src/validation/async-get-manifest.ts index 1408bc0..6d886d7 100644 --- a/packages/workbox-build/src/validation/async-get-manifest.ts +++ b/packages/workbox-build/src/validation/async-get-manifest.ts @@ -1,6 +1,12 @@ -import { errors } from '@vite-pwa/workbox-build/validation/errors' import * as v from 'valibot' -import { AsyncManifestEntrySchema, AsyncRuntimeCachingEntrySchema } from './utils' +import { AsyncNumericExpressionSchema } from './utils' + +// ManifestEntry is used in additionalManifestEntries +const AsyncManifestEntrySchema = v.strictObjectAsync({ + integrity: v.optionalAsync(v.string()), + revision: v.nullable(v.string()), + url: v.string(), +}) export const AsyncGetManifestOptionsSchema = v.pipeAsync( v.strictObjectAsync({ @@ -30,7 +36,7 @@ export const AsyncGetManifestOptionsSchema = v.pipeAsync( * that might have accidentally matched one of your patterns. * @default 2097152 (2MiB) */ - maximumFileSizeToCacheInBytes: v.optionalAsync(v.number(), 2097152), + maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), /** * An object mapping string prefixes to replacement string values. This can be * used to, e.g., remove or add a path prefix from a manifest entry if your @@ -75,130 +81,10 @@ export const AsyncGetManifestOptionsSchema = v.pipeAsync( * information that you've generated for a given URL. */ templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), - /** - * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. - */ - babelPresetEnvTargets: v.optionalAsync(v.arrayAsync(v.string()), ['chrome >= 56']), - /** - * An optional ID to be prepended to cache names. This is primarily useful for local development where multiple sites may be served from the same `http://localhost:port` origin. - */ - cacheId: v.optionalAsync(v.nullable(v.string())), - /** - * Whether or not Workbox should attempt to identify and delete any precaches created by older, incompatible versions. - */ - cleanupOutdatedCaches: v.optionalAsync(v.boolean(), false), - /** - * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) any existing clients as soon as it activates. - */ - clientsClaim: v.optionalAsync(v.boolean(), false), - /** - * If a navigation request for a URL ending in `/` fails to match a precached URL, this value will be appended to the URL and that will be checked for a precache match. This should be set to what your web server is using for its directory index. - */ - directoryIndex: v.optionalAsync(v.nullable(v.string())), - /** - * Whether to disable logging of warnings and errors. - */ - disableDevLogs: v.optionalAsync(v.boolean(), false), - /** - * Any search parameter names that match against one of the RegExp in this array will be removed before looking for a precache match. This is useful if your users might request URLs that contain, for example, URL parameters used to track the source of the traffic. If not provided, the default value is `[/^utm_/, /^fbclid$/]`. - */ - ignoreURLParametersMatching: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), - /** - * A list of JavaScript files that should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) inside the generated service worker file. This is useful when you want to let Workbox create your top-level service worker file, but want to include some additional code, such as a push event listener. - */ - importScripts: v.optionalAsync(v.arrayAsync(v.string())), - /** - * Whether the runtime code for the Workbox library should be included in the top-level service worker, or split into a separate file that needs to be deployed alongside the service worker. Keeping the runtime separate means that users will not have to re-download the Workbox code each time your top-level service worker changes. - */ - inlineWorkboxRuntime: v.optionalAsync(v.boolean(), false), - /** - * If set to 'production', then an optimized service worker bundle that excludes debugging info will be produced. If not explicitly configured here, the `process.env.NODE_ENV` value will be used, and failing that, it will fall back to `'production'`. - */ - mode: v.optionalAsync(v.nullable(v.string()), 'production'), - /** - * If specified, all [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). - */ - navigateFallback: v.optionalAsync(v.nullable(v.string()), null), - /** - * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. - */ - navigateFallbackAllowlist: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), - /** - * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. - */ - navigateFallbackDenylist: v.optionalAsync(v.arrayAsync(v.instance(RegExp))), - /** - * Whether or not to enable [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) in the generated service worker. When set to true, you must also use `runtimeCaching` to set up an appropriate response strategy that will match navigation requests, and make use of the preloaded response. - */ - navigationPreload: v.optionalAsync(v.boolean(), false), - /** - * Controls whether or not to include support for [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics). When `true`, the call to `workbox-google-analytics`'s `initialize()` will be added to your generated service worker. When set to an `Object`, that object will be passed in to the `initialize()` call, allowing you to customize the behavior. - */ - /* offlineGoogleAnalytics: v.optionalAsync(v.unionAsync([ - v.objectAsync({ // Inlined GoogleAnalyticsInitializeOptionsSchema - cacheName: v.optionalAsync(v.string()), - parameterOverrides: v.optionalAsync(v.recordAsync(v.string(), v.string())), - hitFilter: v.optionalAsync(v.function()), - }), - v.boolean(), - ]), false), */ - /** - * When using Workbox's build tools to generate your service worker, you can specify one or more runtime caching configurations. These are then translated to {@link workbox-routing.registerRoute} calls using the match and handler configuration you define. - */ - runtimeCaching: v.optionalAsync(v.arrayAsync(AsyncRuntimeCachingEntrySchema)), - /** - * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) to the generated service worker. If `false`, then a `message` listener will be added instead, allowing client pages to trigger `skipWaiting()` by calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. - */ - skipWaiting: v.optionalAsync(v.boolean(), false), - /** - * Whether to create a sourcemap for the generated service worker files. - */ - sourcemap: v.optionalAsync(v.boolean(), true), - /** - * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. - */ - swDest: v.pipeAsync( - v.string(errors['missing-sw-dest']), - v.endsWith('.js', errors['invalid-sw-dest-js-ext']), - ), /** * The local directory you wish to match `globPatterns` against. The path is * relative to the current directory. */ - globDirectory: v.optionalAsync(v.string()), + globDirectory: v.string(), }), - v.forwardAsync( - v.checkAsync( - async input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), - errors['no-manifest-entries-or-runtime-caching'], - ), - ['globDirectory'], - ), - v.forwardAsync( - v.checkAsync( - async input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), - errors['nav-preload-runtime-caching'], - ), - ['navigationPreload'], - ), - // This is the cross-field validation for cacheName when expiration is present. - v.forwardAsync( - v.checkAsync( - async (options) => { - const runtimeCaching = options.runtimeCaching - if (!runtimeCaching) { - return true - } - for (const runtime of runtimeCaching) { - if (runtime.options?.expiration && !runtime.options?.cacheName) { - return false - } - } - - return true - }, - errors['cache-name-required'], - ), - ['runtimeCaching'], - ), ) diff --git a/packages/workbox-build/src/validation/async-inject-manifest.ts b/packages/workbox-build/src/validation/async-inject-manifest.ts index 9df3b0c..62b0e4b 100644 --- a/packages/workbox-build/src/validation/async-inject-manifest.ts +++ b/packages/workbox-build/src/validation/async-inject-manifest.ts @@ -1,6 +1,13 @@ import * as v from 'valibot' import { errors } from './errors' -import { AsyncManifestEntrySchema } from './utils' +import { AsyncNumericExpressionSchema } from './utils' + +// ManifestEntry is used in additionalManifestEntries +const AsyncManifestEntrySchema = v.strictObjectAsync({ + integrity: v.optionalAsync(v.string()), + revision: v.nullable(v.string()), + url: v.string(), +}) export const AsyncInjectManifestOptionsSchema = v.pipeAsync( v.strictObjectAsync({ @@ -30,7 +37,7 @@ export const AsyncInjectManifestOptionsSchema = v.pipeAsync( * that might have accidentally matched one of your patterns. * @default 2MiB (2097152 bytes) */ - maximumFileSizeToCacheInBytes: v.optionalAsync(v.number(), 2097152), + maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), /** * An object mapping string prefixes to replacement string values. This can be * used to, e.g., remove or add a path prefix from a manifest entry if your diff --git a/packages/workbox-build/src/validation/generate-sw.ts b/packages/workbox-build/src/validation/generate-sw.ts deleted file mode 100644 index 6f8a632..0000000 --- a/packages/workbox-build/src/validation/generate-sw.ts +++ /dev/null @@ -1,439 +0,0 @@ -import * as v from 'valibot' -import { errors } from './errors' -import { NumericExpressionSchema } from './utils' - -// Reused schema -const CacheQueryOptionsSchema = v.strictObject({ - ignoreMethod: v.optional(v.boolean()), - ignoreSearch: v.optional(v.boolean()), - ignoreVary: v.optional(v.boolean()), -}) - -/* -export const GenerateSWOptionsSchema = v.pipe( - v.strictObject({ - /!** - * A list of entries to be precached, in addition to any entries that are generated as part of the build configuration. - *!/ - additionalManifestEntries: v.optional(v.array(v.union([ - v.object({ // Inlined ManifestEntrySchema - integrity: v.optional(v.string()), - revision: v.nullable(v.string()), - url: v.string(), - }), - v.string(), - ]))), - /!** - * Assets that match this will be assumed to be uniquely versioned via their URL, and exempted from the normal HTTP cache-busting that's done when populating the precache. While not required, it's recommended that if your existing build process already inserts a `[hash]` value into each filename, you provide a RegExp that will detect that, as it will reduce the bandwidth consumed when precaching. - *!/ - dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - /!** - * One or more functions which will be applied sequentially against the generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their corresponding transformations will be applied first. - *!/ - manifestTransforms: v.optional(v.array(v.function())), - /!** - * This value can be used to determine the maximum size of files that will be precached. This prevents you from inadvertently precaching very large files that might have accidentally matched one of your patterns. - *!/ - maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), - /!** - * An object mapping string prefixes to replacement string values. This can be used to, e.g., remove or add a path prefix from a manifest entry if your web hosting setup doesn't match your local filesystem setup. As an alternative with more flexibility, you can use the `manifestTransforms` option and provide a function that modifies the entries in the manifest using whatever logic you provide. - *!/ - modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - /!** - * Determines whether or not symlinks are followed when generating the precache manifest. For more information, see the definition of `follow` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - *!/ - globFollow: v.optional(v.boolean(), true), - /!** - * A set of patterns matching files to always exclude when generating the precache manifest. For more information, see the definition of `ignore` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - *!/ - globIgnores: v.optional(v.array(v.string()), ['**!/node_modules/!**!/!*']), - /!** - * Files matching any of these patterns will be included in the precache manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - *!/ - globPatterns: v.optional(v.array(v.string()), ['**!/!*.{js,css,html}']), - /!** - * If true, an error reading a directory when generating a precache manifest will cause the build to fail. If false, the problematic directory will be skipped. For more information, see the definition of `strict` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - *!/ - globStrict: v.optional(v.boolean(), true), - /!** - * If a URL is rendered based on some server-side logic, its contents may depend on multiple files or on some other unique string value. The keys in this object are server-rendered URLs. If the values are an array of strings, they will be interpreted as `glob` patterns, and the contents of any files matching the patterns will be used to uniquely version the URL. If used with a single string, it will be interpreted as unique versioning information that you've generated for a given URL. - *!/ - templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - /!** - * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. - *!/ - babelPresetEnvTargets: v.optional(v.array(v.string()), ['chrome >= 56']), - /!** - * An optional ID to be prepended to cache names. This is primarily useful for local development where multiple sites may be served from the same `http://localhost:port` origin. - *!/ - cacheId: v.optional(v.nullable(v.string())), - /!** - * Whether or not Workbox should attempt to identify and delete any precaches created by older, incompatible versions. - *!/ - cleanupOutdatedCaches: v.optional(v.boolean(), false), - /!** - * Whether or not the service worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim) any existing clients as soon as it activates. - *!/ - clientsClaim: v.optional(v.boolean(), false), - /!** - * If a navigation request for a URL ending in `/` fails to match a precached URL, this value will be appended to the URL and that will be checked for a precache match. This should be set to what your web server is using for its directory index. - *!/ - directoryIndex: v.optional(v.nullable(v.string())), - disableDevLogs: v.optional(v.boolean(), false), - /!** - * Any search parameter names that match against one of the RegExp in this array will be removed before looking for a precache match. This is useful if your users might request URLs that contain, for example, URL parameters used to track the source of the traffic. If not provided, the default value is `[/^utm_/, /^fbclid$/]`. - *!/ - ignoreURLParametersMatching: v.optional(v.array(v.instance(RegExp))), - /!** - * A list of JavaScript files that should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts) inside the generated service worker file. This is useful when you want to let Workbox create your top-level service worker file, but want to include some additional code, such as a push event listener. - *!/ - importScripts: v.optional(v.array(v.string())), - /!** - * Whether the runtime code for the Workbox library should be included in the top-level service worker, or split into a separate file that needs to be deployed alongside the service worker. Keeping the runtime separate means that users will not have to re-download the Workbox code each time your top-level service worker changes. - *!/ - inlineWorkboxRuntime: v.optional(v.boolean(), false), - /!** - * If set to 'production', then an optimized service worker bundle that excludes debugging info will be produced. If not explicitly configured here, the `process.env.NODE_ENV` value will be used, and failing that, it will fall back to `'production'`. - *!/ - mode: v.optional(v.nullable(v.string()), 'production'), - /!** - * If specified, all [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests) for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell). - *!/ - navigateFallback: v.optional(v.nullable(v.string()), null), - /!** - * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. - *!/ - navigateFallbackAllowlist: v.optional(v.array(v.instance(RegExp))), - /!** - * An optional array of regular expressions that restricts which URLs the configured `navigateFallback` behavior applies to. This is useful if only a subset of your site's URLs should be treated as being part of a [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are configured, the denylist takes precedent. - *!/ - navigateFallbackDenylist: v.optional(v.array(v.instance(RegExp))), - /!** - * Whether or not to enable [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload) in the generated service worker. When set to true, you must also use `runtimeCaching` to set up an appropriate response strategy that will match navigation requests, and make use of the preloaded response. - *!/ - navigationPreload: v.optional(v.boolean(), false), - /!** - * Controls whether or not to include support for [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics). When `true`, the call to `workbox-google-analytics`'s `initialize()` will be added to your generated service worker. When set to an `Object`, that object will be passed in to the `initialize()` call, allowing you to customize the behavior. - *!/ - /!* offlineGoogleAnalytics: v.optional(v.union([ - v.object({ // Inlined GoogleAnalyticsInitializeOptionsSchema - cacheName: v.optional(v.string()), - parameterOverrides: v.optional(v.record(v.string(), v.string())), - hitFilter: v.optional(v.function()), - }), - v.boolean(), - ]), false), *!/ - /!** - * When using Workbox's build tools to generate your service worker, you can specify one or more runtime caching configurations. These are then translated to {@link workbox-routing.registerRoute} calls using the match and handler configuration you define. - *!/ - runtimeCaching: v.optional(v.array(v.object({ - /!** - * This determines how the runtime route will generate a response. - * To use one of the built-in workbox-strategies, provide its name, like 'NetworkFirst'. - * Alternatively, this can be a workbox-core.RouteHandler callback function with custom response logic. - *!/ - handler: v.union([ - v.function(), // RouteHandlerCallback - v.object({ handle: v.function() }), // Inlined RouteHandlerObjectSchema - v.picklist([ - 'CacheFirst', - 'CacheOnly', - 'NetworkFirst', - 'NetworkOnly', - 'StaleWhileRevalidate', - ]), - ]), - /!** - * The HTTP method to match against. The default value of 'GET' is normally sufficient, unless you explicitly need to match 'POST', 'PUT', or another type of request. - *!/ - method: v.optional(v.picklist([ - 'DELETE', - 'GET', - 'HEAD', - 'PATCH', - 'POST', - 'PUT', - ]), 'GET'), - options: v.optional(v.object({ - /!** - * Configuring this will add a workbox-background-sync.BackgroundSyncPlugin instance to the workbox-strategies configured in `handler`. - *!/ - backgroundSync: v.optional(v.object({ - name: v.string(), - options: v.optional(v.object({ // Inlined QueueOptionsSchema - forceSyncFallback: v.optional(v.boolean()), - maxRetentionTime: v.optional(v.number()), - onSync: v.optional(v.function()), - })), - })), - /!** - * Configuring this will add a workbox-broadcast-update.BroadcastUpdatePlugin instance to the workbox-strategies configured in `handler`. - *!/ - broadcastUpdate: v.optional(v.object({ - channelName: v.string(), - options: v.object({ // Inlined BroadcastCacheUpdateOptionsSchema - headersToCheck: v.optional(v.array(v.string())), - generatePayload: v.optional(v.function()), - notifyAllClients: v.optional(v.boolean()), - }), - })), - /!** - * Configuring this will add a workbox-cacheable-response.CacheableResponsePlugin instance to the workbox-strategies configured in `handler`. - *!/ - cacheableResponse: v.optional(v.object({ // Inlined CacheableResponseOptionsSchema - statuses: v.optional(v.array(v.number())), - headers: v.optional(v.record(v.string(), v.string())), - })), - /!** - * If provided, this will set the `cacheName` property of the workbox-strategies configured in `handler`. - *!/ - cacheName: v.optional(v.nullable(v.string())), - /!** - * Configuring this will add a workbox-expiration.ExpirationPlugin instance to the workbox-strategies configured in `handler`. - *!/ - expiration: v.optional(v.object({ // Inlined ExpirationPluginOptionsSchema - maxEntries: v.optional(v.number()), - maxAgeSeconds: v.optional(v.number()), - matchOptions: v.optional(CacheQueryOptionsSchema), // Reused - purgeOnQuotaError: v.optional(v.boolean()), - })), - /!** - * If provided, this will set the `networkTimeoutSeconds` property of the workbox-strategies configured in `handler`. Note that only 'NetworkFirst' and 'NetworkOnly' support `networkTimeoutSeconds`. - *!/ - networkTimeoutSeconds: v.optional(v.number()), - /!** - * Configuring this allows the use of one or more Workbox plugins that don't have "shortcut" options (like `expiration` for workbox-expiration.ExpirationPlugin). The plugins provided here will be added to the workbox-strategies configured in `handler`. - *!/ - plugins: v.optional(v.array(v.object({ // Inlined WorkboxPluginSchema - cacheDidUpdate: v.optional(v.function()), - cachedResponseWillBeUsed: v.optional(v.function()), - cacheKeyWillBeUsed: v.optional(v.function()), - cacheWillUpdate: v.optional(v.function()), - fetchDidFail: v.optional(v.function()), - fetchDidSucceed: v.optional(v.function()), - handlerDidComplete: v.optional(v.function()), - handlerDidError: v.optional(v.function()), - handlerDidRespond: v.optional(v.function()), - handlerWillRespond: v.optional(v.function()), - handlerWillStart: v.optional(v.function()), - requestWillFetch: v.optional(v.function()), - }))), - /!** - * Configuring this will add a workbox-precaching.PrecacheFallbackPlugin instance to the workbox-strategies configured in `handler`. - *!/ - precacheFallback: v.optional(v.object({ fallbackURL: v.string() })), - /!** - * Enabling this will add a workbox-range-requests.RangeRequestsPlugin instance to the workbox-strategies configured in `handler`. - *!/ - rangeRequests: v.optional(v.boolean()), - /!** - * Configuring this will pass along the `fetchOptions` value to the workbox-strategies configured in `handler`. - *!/ - fetchOptions: v.optional(v.any()), // RequestInit is too complex to define for now - /!** - * Configuring this will pass along the `matchOptions` value to the workbox-strategies configured in `handler`. - *!/ - matchOptions: v.optional(CacheQueryOptionsSchema), // Reused - })), - /!** - * This match criteria determines whether the configured handler will generate a response for any requests that don't match one of the precached URLs. If multiple `RuntimeCaching` routes are defined, then the first one whose `urlPattern` matches will be the one that responds. - * This value directly maps to the first parameter passed to workbox-routing.registerRoute. It's recommended to use a workbox-core.RouteMatchCallback function for greatest flexibility. - *!/ - urlPattern: v.union([v.instance(RegExp), v.function(), v.string()]), - }))), - /!** - * Whether to add an unconditional call to [`skipWaiting()`](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase) to the generated service worker. If `false`, then a `message` listener will be added instead, allowing client pages to trigger `skipWaiting()` by calling `postMessage({type: 'SKIP_WAITING'})` on a waiting service worker. - *!/ - skipWaiting: v.optional(v.boolean(), false), - /!** - * Whether to create a sourcemap for the generated service worker files. - *!/ - sourcemap: v.optional(v.boolean(), true), - /!** - * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. - *!/ - swDest: v.pipe( - v.string(errors['missing-sw-dest']), - v.endsWith('.js', errors['invalid-sw-dest-js-ext']), - ), - /!** - * The local directory you wish to match `globPatterns` against. The path is - * relative to the current directory. - *!/ - globDirectory: v.optional(v.string()), - }), - // This is the cross-field validation rule. - v.forward( - v.check( - input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), - errors['no-manifest-entries-or-runtime-caching'], - ), - // If the check fails, assign the error to the 'globDirectory' field. - ['globDirectory'], - ), - // This is the cross-field validation rule for navigationPreload. - v.forward( - v.check( - input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), - errors['nav-preload-runtime-caching'], - ), - ['navigationPreload'], - ), -) -*/ - -// Schema for the `options` property within a runtimeCaching entry. -// This allows us to attach cross-field validation rules to it. -export const RuntimeCachingOptionsSchema = v.strictObject({ - backgroundSync: v.optional(v.strictObject({ - name: v.string(), - options: v.optional(v.strictObject({ - forceSyncFallback: v.optional(v.boolean()), - maxRetentionTime: v.optional(NumericExpressionSchema), - onSync: v.optional(v.function()), - })), - })), - broadcastUpdate: v.optional(v.strictObject({ - channelName: v.string(), - options: v.strictObject({ - headersToCheck: v.optional(v.array(v.string())), - generatePayload: v.optional(v.function()), - notifyAllClients: v.optional(v.boolean()), - }), - })), - cacheableResponse: v.optional(v.strictObject({ - statuses: v.optional(v.array(NumericExpressionSchema)), - headers: v.optional(v.record(v.string(), v.string())), - })), - cacheName: v.optional(v.nullable(v.string())), - expiration: v.optional(v.strictObject({ - maxEntries: v.optional(NumericExpressionSchema), - maxAgeSeconds: v.optional(NumericExpressionSchema), - matchOptions: v.optional(CacheQueryOptionsSchema), - purgeOnQuotaError: v.optional(v.boolean()), - })), - networkTimeoutSeconds: v.optional(NumericExpressionSchema), - plugins: v.optional(v.array(v.strictObject({ - cacheDidUpdate: v.optional(v.function()), - cachedResponseWillBeUsed: v.optional(v.function()), - cacheKeyWillBeUsed: v.optional(v.function()), - cacheWillUpdate: v.optional(v.function()), - fetchDidFail: v.optional(v.function()), - fetchDidSucceed: v.optional(v.function()), - handlerDidComplete: v.optional(v.function()), - handlerDidError: v.optional(v.function()), - handlerDidRespond: v.optional(v.function()), - handlerWillRespond: v.optional(v.function()), - handlerWillStart: v.optional(v.function()), - requestWillFetch: v.optional(v.function()), - }))), - precacheFallback: v.optional(v.strictObject({ fallbackURL: v.string() })), - rangeRequests: v.optional(v.boolean()), - fetchOptions: v.optional(v.any()), - matchOptions: v.optional(CacheQueryOptionsSchema), -}) - -// Schema for a single entry in the runtimeCaching array. -// It's a strict object to prevent unknown properties. -export const RuntimeCachingEntrySchema = v.strictObject({ - handler: v.union([ - v.function(), - v.strictObject({ handle: v.function() }), - v.picklist([ - 'CacheFirst', - 'CacheOnly', - 'NetworkFirst', - 'NetworkOnly', - 'StaleWhileRevalidate', - ]), - ]), - method: v.optional(v.picklist([ - 'DELETE', - 'GET', - 'HEAD', - 'PATCH', - 'POST', - 'PUT', - ]), 'GET'), - options: v.optional(RuntimeCachingOptionsSchema), - urlPattern: v.union([v.instance(RegExp), v.function(), v.string()]), -}) - -export const GenerateSWOptionsSchema = v.pipe( - v.strictObject({ - additionalManifestEntries: v.optional(v.array(v.union([ - v.strictObject({ - integrity: v.optional(v.string()), - revision: v.nullable(v.string()), - url: v.string(), - }), - v.string(), - ]))), - dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - manifestTransforms: v.optional(v.array(v.function())), - maximumFileSizeToCacheInBytes: v.optional(NumericExpressionSchema, 2097152), - modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - globFollow: v.optional(v.boolean(), true), - globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), - globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), - globStrict: v.optional(v.boolean(), true), - templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - babelPresetEnvTargets: v.optional(v.array(v.string()), ['chrome >= 56']), - cacheId: v.optional(v.nullable(v.string())), - cleanupOutdatedCaches: v.optional(v.boolean(), false), - clientsClaim: v.optional(v.boolean(), false), - directoryIndex: v.optional(v.nullable(v.string())), - disableDevLogs: v.optional(v.boolean(), false), - ignoreURLParametersMatching: v.optional(v.array(v.instance(RegExp))), - importScripts: v.optional(v.array(v.string())), - inlineWorkboxRuntime: v.optional(v.boolean(), false), - mode: v.optional(v.nullable(v.string()), 'production'), - navigateFallback: v.optional(v.nullable(v.string()), null), - navigateFallbackAllowlist: v.optional(v.array(v.instance(RegExp))), - navigateFallbackDenylist: v.optional(v.array(v.instance(RegExp))), - navigationPreload: v.optional(v.boolean(), false), - runtimeCaching: v.optional(v.array(RuntimeCachingEntrySchema)), - skipWaiting: v.optional(v.boolean(), false), - sourcemap: v.optional(v.boolean(), true), - swDest: v.pipe( - v.string(errors['invalid-sw-dest']), - v.endsWith('.js', errors['invalid-sw-dest-js-ext']), - ), - globDirectory: v.optional(v.string()), - }), - v.forward( - v.check( - input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), - errors['no-manifest-entries-or-runtime-caching'], - ), - ['globDirectory'], - ), - v.forward( - v.check( - input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), - errors['nav-preload-runtime-caching'], - ), - ['navigationPreload'], - ), - // This is the cross-field validation for cacheName when expiration is present. - v.forward( - v.check( - (options) => { - const runtimeCaching = options.runtimeCaching - if (!runtimeCaching) { - return true - } - for (const runtime of runtimeCaching) { - if (runtime.options?.expiration && !runtime.options?.cacheName) { - return false - } - } - - return true - }, - errors['cache-name-required'], - ), - ['runtimeCaching'], - ), -) diff --git a/packages/workbox-build/src/validation/get-manifest.ts b/packages/workbox-build/src/validation/get-manifest.ts deleted file mode 100644 index 24a2646..0000000 --- a/packages/workbox-build/src/validation/get-manifest.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as v from 'valibot' - -// ManifestEntry is used in additionalManifestEntries -const ManifestEntrySchema = v.strictObject({ - integrity: v.optional(v.string()), - revision: v.nullable(v.string()), - url: v.string(), -}) - -export const GetManifestOptionsSchema = v.pipe( - v.strictObject({ - /** - * A list of entries to be precached, in addition to any entries that are - * generated as part of the build configuration. - */ - additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their - * URL, and exempted from the normal HTTP cache-busting that's done when - * populating the precache. While not required, it's recommended that if your - * existing build process already inserts a `[hash]` value into each filename, - * you provide a RegExp that will detect that, as it will reduce the bandwidth - * consumed when precaching. - */ - dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the - * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are - * also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optional(v.array(v.function())), - /** - * This value can be used to determine the maximum size of files that will be - * precached. This prevents you from inadvertently precaching very large files - * that might have accidentally matched one of your patterns. - * @default 2097152 (2MiB) - */ - maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be - * used to, e.g., remove or add a path prefix from a manifest entry if your - * web hosting setup doesn't match your local filesystem setup. As an - * alternative with more flexibility, you can use the `manifestTransforms` - * option and provide a function that modifies the entries in the manifest - * using whatever logic you provide. - */ - modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - /** - * Determines whether or not symlinks are followed when generating the - * precache manifest. For more information, see the definition of `follow` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globFollow: v.optional(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the - * precache manifest. For more information, see the definition of `ignore` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache - * manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest - * will cause the build to fail. If false, the problematic directory will be - * skipped. For more information, see the definition of `strict` in the `glob` - * [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optional(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may - * depend on multiple files or on some other unique string value. The keys in - * this object are server-rendered URLs. If the values are an array of - * strings, they will be interpreted as `glob` patterns, and the contents of - * any files matching the patterns will be used to uniquely version the URL. - * If used with a single string, it will be interpreted as unique versioning - * information that you've generated for a given URL. - */ - templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - /** - * The local directory you wish to match `globPatterns` against. The path is - * relative to the current directory. - */ - globDirectory: v.string(), - }), -) diff --git a/packages/workbox-build/src/validation/inject-manifest.ts b/packages/workbox-build/src/validation/inject-manifest.ts deleted file mode 100644 index 71612f1..0000000 --- a/packages/workbox-build/src/validation/inject-manifest.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as v from 'valibot' -import { errors } from './errors' - -// ManifestEntry is used in additionalManifestEntries -const ManifestEntrySchema = v.strictObject({ - integrity: v.optional(v.string()), - revision: v.nullable(v.string()), - url: v.string(), -}) - -export const InjectManifestOptionsSchema = v.pipe( - v.strictObject({ - /** - * A list of entries to be precached, in addition to any entries that are - * generated as part of the build configuration. - */ - additionalManifestEntries: v.optional(v.array(v.union([ManifestEntrySchema, v.string()]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their - * URL, and exempted from the normal HTTP cache-busting that's done when - * populating the precache. While not required, it's recommended that if your - * existing build process already inserts a `[hash]` value into each filename, - * you provide a RegExp that will detect that, as it will reduce the bandwidth - * consumed when precaching. - */ - dontCacheBustURLsMatching: v.optional(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the - * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are - * also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optional(v.array(v.function())), - /** - * This value can be used to determine the maximum size of files that will be - * precached. This prevents you from inadvertently precaching very large files - * that might have accidentally matched one of your patterns. - * @default 2MiB (2097152 bytes) - */ - maximumFileSizeToCacheInBytes: v.optional(v.number(), 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be - * used to, e.g., remove or add a path prefix from a manifest entry if your - * web hosting setup doesn't match your local filesystem setup. As an - * alternative with more flexibility, you can use the `manifestTransforms` - * option and provide a function that modifies the entries in the manifest - * using whatever logic you provide. - */ - modifyURLPrefix: v.optional(v.record(v.string(), v.string())), - - /** - * Determines whether or not symlinks are followed when generating the - * precache manifest. For more information, see the definition of `follow` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - * @default true - */ - globFollow: v.optional(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the - * precache manifest. For more information, see the definition of `ignore` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optional(v.array(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache - * manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optional(v.array(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest - * will cause the build to fail. If false, the problematic directory will be - * skipped. For more information, see the definition of `strict` in the `glob` - * [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optional(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may - * depend on multiple files or on some other unique string value. The keys in - * this object are server-rendered URLs. If the values are an array of - * strings, they will be interpreted as `glob` patterns, and the contents of - * any files matching the patterns will be used to uniquely version the URL. - * If used with a single string, it will be interpreted as unique versioning - * information that you've generated for a given URL. - */ - templatedURLs: v.optional(v.record(v.string(), v.union([v.array(v.string()), v.string()]))), - - /** - * The string to find inside of the `swSrc` file. Once found, it will be - * replaced by the generated precache manifest. - * @default "self.__WB_MANIFEST" - */ - injectionPoint: v.optional(v.string(), 'self.__WB_MANIFEST'), - /** - * The path and filename of the service worker file that will be read during - * the build process, relative to the current working directory. - */ - swSrc: v.string(), - - /** - * The path and filename of the service worker file that will be created by - * the build process, relative to the current working directory. It must end - * in '.js'. - */ - swDest: v.pipe( - v.string(errors['missing-sw-dest']), - v.endsWith('.js', errors['invalid-sw-dest-js-ext']), - ), - - /** - * The local directory you wish to match `globPatterns` against. The path is - * relative to the current directory. - */ - globDirectory: v.string(), - }), -) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index f9860ec..1fa6f8d 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -1,6 +1,6 @@ // import type { ArrayExpression, Program } from '@babel/types' -import type { BaseIssue, BaseSchema, BaseSchemaAsync, InferOutput, IssuePathItem } from 'valibot' -import { getDotPath, safeParse, safeParseAsync } from 'valibot' +import type { BaseIssue, BaseSchemaAsync, InferOutput, IssuePathItem } from 'valibot' +import { getDotPath, safeParseAsync } from 'valibot' // This helper function traverses the issue's path to find the top-level object key // that contains the error. This is crucial for errors nested inside arrays. @@ -16,7 +16,7 @@ function getTopLevelKey(path: IssuePathItem[] | undefined): string | undefined { // see [Path Key not Available in safeParse](https://github.com/fabian-hiller/valibot/discussions/696). // custom Valibot's message mapping function extractIssueMessage(issue: BaseIssue) { - console.log(issue) + // console.log(issue) const path = getDotPath(issue) const topLevelKey = getTopLevelKey(issue.path) const lastKey = issue.path?.[issue.path.length - 1]?.key @@ -88,7 +88,7 @@ function extractIssueMessage(issue: BaseIssue) { } // This function intelligently sanitizes the options object from magicast. -function sanitizeMagicastOptions(options: any): any { +function _sanitizeMagicastOptions(options: any): any { if (options === null || typeof options !== 'object') return options @@ -98,10 +98,10 @@ function sanitizeMagicastOptions(options: any): any { } if (Array.isArray(options)) { - console.log('PASO') + // console.log('PASO') const sanitizedArray: any[] = [] for (const option of options) { - sanitizedArray.push(sanitizeMagicastOptions(option)) + sanitizedArray.push(_sanitizeMagicastOptions(option)) } return sanitizedArray // return options.map(sanitizeMagicastOptions) @@ -118,43 +118,12 @@ function sanitizeMagicastOptions(options: any): any { const sanitized: { [key: string]: any } = {} for (const key in options) { if (Object.prototype.hasOwnProperty.call(options, key) && !key.startsWith('$')) - sanitized[key] = sanitizeMagicastOptions(options[key]) + sanitized[key] = _sanitizeMagicastOptions(options[key]) } return sanitized } -/** - * A wrapper around Valibot's `safeParse` that throws a user-friendly error - * if validation fails. - * @param schema The Valibot schema to use. - * @param options The options object to validate. - * @param methodName The name of the Workbox method being validated, for context. - */ -export function validate>( - schema: TSchema, - options: unknown, - methodName: string, -): InferOutput { - /* const result = safeParse( - schema, - '$ast' in (options as any) - ? sanitizeMagicastOptions(options) - : options, - ) */ - const result = safeParse( - schema, - options, - ) - // const result = safeParse(schema, options) - if (!result.success) { - const errorMessages = result.issues.map(extractIssueMessage) - throw new Error( - `${methodName}() options validation failed: \n- ${errorMessages.join('\n- ')}`, - ) - } - return result.output -} /** * A wrapper around Valibot's `safeParseAsync` that throws a user-friendly error * if validation fails. diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index 3be914e..b24b0b4 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -1,15 +1,15 @@ import type * as v from 'valibot' import type { GenerateSWOptions, GetManifestOptions, InjectManifestOptions } from '../src/types' import { describe, expect, expectTypeOf, it } from 'vitest' +import { AsyncGenerateSWOptionsSchema } from '../src/validation/async-generate-sw' +import { AsyncGetManifestOptionsSchema } from '../src/validation/async-get-manifest' +import { AsyncInjectManifestOptionsSchema } from '../src/validation/async-inject-manifest' import { errors } from '../src/validation/errors' -import { GenerateSWOptionsSchema } from '../src/validation/generate-sw' -import { GetManifestOptionsSchema } from '../src/validation/get-manifest' -import { InjectManifestOptionsSchema } from '../src/validation/inject-manifest' -import { validate } from '../src/validation/validation-helper' +import { validateAsync } from '../src/validation/validation-helper' -type GenerateSWOptionsSchemaType = v.InferInput -type GetManifestOptionsSchemaType = v.InferInput -type InjectManifestOptionsSchemaType = v.InferInput +type GenerateSWOptionsSchemaType = v.InferInput +type GetManifestOptionsSchemaType = v.InferInput +type InjectManifestOptionsSchemaType = v.InferInput describe('schema Type Inference Validation', () => { it('should correctly infer types that are compatible with the original Workbox types', () => { @@ -25,27 +25,27 @@ describe('schema Type Inference Validation', () => { }) describe('generateSWOptions Schema Validation', () => { - it('should pass with valid minimal options', () => { + it('should pass with valid minimal options', async () => { const options = { swDest: 'sw.js', globDirectory: './', } satisfies GenerateSWOptions // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).resolves.not.toThrow() }) - it('should fail if swDest is missing', () => { + it('should fail if swDest is missing', async () => { const options = { globDirectory: './', } satisfies Partial expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( `generateSW() options validation failed: \n- The option "swDest" is required.`, ) }) - it('should pass with a function for manifestTransforms', () => { + it('should pass with a function for manifestTransforms', async () => { const options = { swDest: 'sw.js', globDirectory: './', @@ -57,34 +57,34 @@ describe('generateSWOptions Schema Validation', () => { } satisfies GenerateSWOptions // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).resolves.not.toThrow() }) - it('should fail if manifestTransforms contains a non-function', () => { + it('should fail if manifestTransforms contains a non-function', async () => { const options = { swDest: 'sw.js', globDirectory: './', manifestTransforms: ['not-a-function'], } satisfies Omit & { manifestTransforms: string[] } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) }) - it('should fail if manifestTransforms is a function instead of an array', () => { + it('should fail if manifestTransforms is a function instead of an array', async () => { const options = { swDest: 'sw.js', globDirectory: './', manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array.', ) }) - it.only('should pass if globDirectory is missing but runtimeCaching is present', () => { + it('should pass if globDirectory is missing but runtimeCaching is present', async () => { const options = { swDest: 'sw.js', runtimeCaching: [ @@ -96,22 +96,22 @@ describe('generateSWOptions Schema Validation', () => { } satisfies GenerateSWOptions // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).resolves.not.toThrow() }) - it('should fail if an unknown property is present', () => { + it('should fail if an unknown property is present', async () => { const options = { swDest: 'sw.js', globDirectory: './', importWorkboxFrom: 'cdn', } satisfies GenerateSWOptions & { importWorkboxFrom?: string } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options as any, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The option "importWorkboxFrom" is unknown or has been deprecated.', ) }) - it('should fail if swDest does not end with .js', () => { + it('should fail if swDest does not end with .js', async () => { const options = { swDest: 'sw.txt', globDirectory: './', @@ -119,13 +119,13 @@ describe('generateSWOptions Schema Validation', () => { // The shape is valid, but the content is not. // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( `generateSW() options validation failed: \n- ${errors['invalid-sw-dest-js-ext']}`, ) }) describe('runtimeCaching validation', () => { - it('should pass with a valid runtimeCaching entry', () => { + it('should pass with a valid runtimeCaching entry', async () => { const options = { swDest: 'sw.js', globDirectory: './', @@ -136,22 +136,22 @@ describe('generateSWOptions Schema Validation', () => { } satisfies GenerateSWOptions // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).not.toThrow() + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).resolves.not.toThrow() }) - it('should fail if runtimeCaching is not an array (string)', () => { + it('should fail if runtimeCaching is not an array (string)', async () => { const options = { swDest: 'sw.js', globDirectory: './', runtimeCaching: 'a-string-not-an-array', } satisfies Omit & { runtimeCaching: string } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The "runtimeCaching" option must be an array.', ) }) - it('should fail if a runtimeCaching entry is missing a handler', () => { + it('should fail if a runtimeCaching entry is missing a handler', async () => { const options = { swDest: 'sw.js', globDirectory: './', @@ -160,12 +160,12 @@ describe('generateSWOptions Schema Validation', () => { }], } satisfies Omit & { runtimeCaching: { urlPattern: RegExp }[] } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The option "runtimeCaching.0.handler" is required.', ) }) - it('should fail if a runtimeCaching entry has an invalid handler type', () => { + it('should fail if a runtimeCaching entry has an invalid handler type', async () => { const options = { swDest: 'sw.js', globDirectory: './', @@ -175,12 +175,12 @@ describe('generateSWOptions Schema Validation', () => { }], } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: number }[] } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received 123.', ) }) - it('should fail if a runtimeCaching entry has an invalid handler string value', () => { + it('should fail if a runtimeCaching entry has an invalid handler string value', async () => { const options = { swDest: 'sw.js', globDirectory: './', @@ -190,7 +190,7 @@ describe('generateSWOptions Schema Validation', () => { }], } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: 'InvalidStrategy' }[] } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GenerateSWOptionsSchema, options, 'generateSW')).toThrow( + await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received "InvalidStrategy"', ) }) @@ -198,59 +198,59 @@ describe('generateSWOptions Schema Validation', () => { }) describe('getManifestOptions Schema Validation', () => { - it('should pass with valid minimal options', () => { + it('should pass with valid minimal options', async () => { const options = { globDirectory: './', } satisfies GetManifestOptions // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(GetManifestOptionsSchema, options, 'getManifest')).not.toThrow() + await expect(validateAsync(AsyncGetManifestOptionsSchema, options, 'getManifest')).resolves.not.toThrow() }) - it('should fail if globDirectory is missing', () => { + it('should fail if globDirectory is missing', async () => { const options = {} satisfies Partial expectTypeOf().not.toMatchObjectType() - expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- The option "globDirectory" is required.', ) }) - it('should fail if manifestTransforms contains a non-function', () => { + it('should fail if manifestTransforms contains a non-function', async () => { const options = { globDirectory: './', manifestTransforms: ['not-a-function'], } satisfies Omit & { manifestTransforms: string[] } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) }) - it('should fail if manifestTransforms is a function instead of an array', () => { + it('should fail if manifestTransforms is a function instead of an array', async () => { const options = { globDirectory: './', manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', ) }) - it('should fail if an unknown property is present', () => { + it('should fail if an unknown property is present', async () => { const options = { globDirectory: './', anotherUnknown: 'noop', } satisfies GetManifestOptions & { anotherUnknown?: string } expectTypeOf().not.toMatchObjectType() - expect(() => validate(GetManifestOptionsSchema, options as any, 'getManifest')).toThrow( + await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', ) }) }) describe('injectManifestOptions Schema Validation', () => { - it('should pass with valid minimal options', () => { + it('should pass with valid minimal options', async () => { const options = { swSrc: 'sw.js', swDest: 'sw-injected.js', @@ -258,21 +258,21 @@ describe('injectManifestOptions Schema Validation', () => { } satisfies InjectManifestOptions // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).not.toThrow() + await expect(validateAsync(AsyncInjectManifestOptionsSchema, options, 'injectManifest')).resolves.not.toThrow() }) - it('should fail if swSrc is missing', () => { + it('should fail if swSrc is missing', async () => { const options = { swDest: 'sw-injected.js', globDirectory: './', } satisfies Partial expectTypeOf().not.toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- The option "swSrc" is required.', ) }) - it('should fail if manifestTransforms contains a non-function', () => { + it('should fail if manifestTransforms contains a non-function', async () => { const options = { swSrc: 'sw.js', swDest: 'sw-injected.js', @@ -280,12 +280,12 @@ describe('injectManifestOptions Schema Validation', () => { manifestTransforms: ['not-a-function'], } satisfies Omit & { manifestTransforms: string[] } expectTypeOf().not.toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) }) - it('should fail if manifestTransforms is a function instead of an array', () => { + it('should fail if manifestTransforms is a function instead of an array', async () => { const options = { swSrc: 'sw.js', swDest: 'sw-injected.js', @@ -293,12 +293,12 @@ describe('injectManifestOptions Schema Validation', () => { manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), } satisfies Omit & { manifestTransforms: (manifest: any) => void } expectTypeOf().not.toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', ) }) - it('should fail if swDest does not end with .js', () => { + it('should fail if swDest does not end with .js', async () => { const options = { swSrc: 'sw.js', swDest: 'sw-injected.txt', @@ -307,12 +307,12 @@ describe('injectManifestOptions Schema Validation', () => { // The shape is valid, but the content is not. // @ts-expect-error - schema Type Inference Validation. expectTypeOf().toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options, 'injectManifest')).toThrow( + await expect(validateAsync(AsyncInjectManifestOptionsSchema, options, 'injectManifest')).rejects.toThrow( `injectManifest() options validation failed: \n- ${errors['invalid-sw-dest-js-ext']}`, ) }) - it('should fail if an unknown property is present', () => { + it('should fail if an unknown property is present', async () => { const options = { swSrc: 'sw.js', swDest: 'sw-injected.js', @@ -320,7 +320,7 @@ describe('injectManifestOptions Schema Validation', () => { anotherUnknown: 'noop', } satisfies InjectManifestOptions & { anotherUnknown?: string } expectTypeOf().not.toMatchObjectType() - expect(() => validate(InjectManifestOptionsSchema, options as any, 'injectManifest')).toThrow( + await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', ) }) diff --git a/packages/workbox-sw/tsdown.config.ts b/packages/workbox-sw/tsdown.config.ts index 118a6cd..ebf6d1e 100644 --- a/packages/workbox-sw/tsdown.config.ts +++ b/packages/workbox-sw/tsdown.config.ts @@ -1,5 +1,9 @@ +import { createRequire } from 'node:module' import { defineConfig } from 'tsdown' +const require = createRequire(import.meta.url) +const packageJson = require('./package.json') + export default defineConfig({ entry: [ 'src/index.ts', // <== barrel @@ -26,7 +30,6 @@ export default defineConfig({ 'src/streams/types.ts', 'src/sw/index.ts', ], - exports: true, platform: 'browser', banner: `/* Copyright 2019 Google LLC, Vite PWA's Team @@ -44,7 +47,7 @@ export default defineConfig({ // **/types contains only types: just replace the entry const typesVersions: Record = {} for (const [key, value] of Object.entries(exp)) { - // add typesVersion entry + // add typesVersions entry if (key !== '.' && key !== './package.json') { typesVersions[key.slice(2)] = [value.replace(/\.js$/, '.d.ts')] } diff --git a/tsconfig.json b/tsconfig.json index 123d86a..49dc7fa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,7 @@ ], "@vite-pwa/workbox-window/*": [ "./packages/workbox-window/src/*.ts" - ], + ] }, "resolveJsonModule": true, "types": [ From 03e72feb63a22cf08c2f29c673a59c755455cc0c Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 25 Oct 2025 15:12:40 +0200 Subject: [PATCH 17/20] chore: add tsdown helper --- helper.ts | 31 ++++++++++++++++++++++ packages/workbox-build/package.json | 28 +++++++++++++------- packages/workbox-build/tsdown.config.ts | 10 +++---- packages/workbox-sw/tsdown.config.ts | 33 +++--------------------- packages/workbox-window/tsdown.config.ts | 13 +++------- tsconfig.json | 1 + 6 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 helper.ts diff --git a/helper.ts b/helper.ts new file mode 100644 index 0000000..96833a5 --- /dev/null +++ b/helper.ts @@ -0,0 +1,31 @@ +export const banner = `/* + Copyright 2019 Google LLC, Vite PWA's Team + + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/` + +export const define = { + 'process.env.NODE_ENV': 'process.env.NODE_ENV', +} satisfies import('tsdown').Options['define'] + +export const exports = { + customExports(exp, { pkg }) { + // **/types contains only types: just replace the entry + const typesVersions: Record = {} + for (const [key, value] of Object.entries(exp)) { + // add typesVersions entry + if (key !== '.' && key !== './package.json') { + typesVersions[key.slice(2)] = [value.replace(/\.js$/, '.d.ts')] + } + if (key.endsWith('/types')) { + exp[key] = { types: value.replace(/\.js$/, '.d.ts') } + } + } + + pkg.typesVersions = { '*': typesVersions } + + return exp + }, +} satisfies import('tsdown').ExportsOptions diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index 57dd9a4..390eec7 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -28,21 +28,31 @@ "sideEffects": false, "exports": { ".": "./dist/index.js", + "./generate-sw": "./dist/generate-sw.js", + "./get-manifest": "./dist/get-manifest.js", + "./inject-manifest": "./dist/inject-manifest.js", "./types": { "types": "./dist/types.d.ts" }, - "./generate-sw": "./dist/generate-sw.js", - "./inject-manifest": "./dist/inject-manifest.js" + "./package.json": "./package.json" }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "typesVersions": { "*": { - "types": ["dist/types.d.ts"], - "generate-sw": ["dist/generate-sw.d.ts"], - "get-manifest": ["dist/get-manifest.d.ts"], - "inject-manifest": ["dist/inject-manifest.d.ts"] + "generate-sw": [ + "./dist/generate-sw.d.ts" + ], + "get-manifest": [ + "./dist/get-manifest.d.ts" + ], + "inject-manifest": [ + "./dist/inject-manifest.d.ts" + ], + "types": [ + "./dist/types.d.ts" + ] } }, "files": [ diff --git a/packages/workbox-build/tsdown.config.ts b/packages/workbox-build/tsdown.config.ts index 84e6a58..a67e051 100644 --- a/packages/workbox-build/tsdown.config.ts +++ b/packages/workbox-build/tsdown.config.ts @@ -1,13 +1,9 @@ import { defineConfig } from 'tsdown' +import { banner, exports } from '../../helper.ts' export default defineConfig({ entry: './src/{index,types,generate-sw,get-manifest,inject-manifest}.ts', platform: 'node', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, + banner, + exports, }) diff --git a/packages/workbox-sw/tsdown.config.ts b/packages/workbox-sw/tsdown.config.ts index ebf6d1e..296d944 100644 --- a/packages/workbox-sw/tsdown.config.ts +++ b/packages/workbox-sw/tsdown.config.ts @@ -1,5 +1,6 @@ import { createRequire } from 'node:module' import { defineConfig } from 'tsdown' +import { banner, define, exports } from '../../helper.ts' const require = createRequire(import.meta.url) const packageJson = require('./package.json') @@ -31,34 +32,8 @@ export default defineConfig({ 'src/sw/index.ts', ], platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, + banner, + define, noExternal: ['idb'], - exports: { - customExports(exp, { pkg }) { - // **/types contains only types: just replace the entry - const typesVersions: Record = {} - for (const [key, value] of Object.entries(exp)) { - // add typesVersions entry - if (key !== '.' && key !== './package.json') { - typesVersions[key.slice(2)] = [value.replace(/\.js$/, '.d.ts')] - } - if (key.endsWith('/types')) { - exp[key] = { types: value.replace(/\.js$/, '.d.ts') } - } - } - - pkg.typesVersions = { '*': typesVersions } - - return exp - }, - }, + exports, }) diff --git a/packages/workbox-window/tsdown.config.ts b/packages/workbox-window/tsdown.config.ts index 1ba9d65..e2a6727 100644 --- a/packages/workbox-window/tsdown.config.ts +++ b/packages/workbox-window/tsdown.config.ts @@ -1,16 +1,9 @@ import { defineConfig } from 'tsdown' +import { banner, define } from '../../helper.ts' export default defineConfig({ entry: './src/index.ts', platform: 'browser', - banner: `/* - Copyright 2019 Google LLC, Vite PWA's Team - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. -*/`, - define: { - 'process.env.NODE_ENV': 'process.env.NODE_ENV', - }, + banner, + define, }) diff --git a/tsconfig.json b/tsconfig.json index 49dc7fa..b9adb24 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "lib": ["DOM", "ESNext", "DOM.Iterable", "ESNext.AsyncIterable", "WebWorker"], "module": "ESNext", "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, "paths": { "@vite-pwa/workbox-build": [ "./packages/workbox-build/src/index.ts" From 93fffe05c67a3b29341fec326bcd3bae30fe880c Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 25 Oct 2025 16:08:08 +0200 Subject: [PATCH 18/20] chore: rename `workbox-sw` package to `workbox-swkit` --- packages/workbox-build/package.json | 2 +- packages/workbox-build/src/generate-sw.ts | 8 ++++---- packages/workbox-build/src/types.ts | 12 ++++++------ packages/workbox-sw/src/index.ts | 12 ------------ packages/{workbox-sw => workbox-swkit}/package.json | 6 +++--- .../src/background-sync/BackgroundSyncPlugin.ts | 0 .../src/background-sync/Queue.ts | 0 .../src/background-sync/QueueStore.ts | 0 .../src/background-sync/StorableRequest.ts | 0 .../src/background-sync/index.ts | 0 .../src/background-sync/lib/QueueDb.ts | 0 .../src/background-sync/lib/QueueStore.ts | 0 .../src/background-sync/lib/StorableRequest.ts | 0 .../src/background-sync/types.ts | 0 .../src/broadcast-update/BroadcastCacheUpdate.ts | 0 .../src/broadcast-update/BroadcastUpdatePlugin.ts | 0 .../src/broadcast-update/index.ts | 0 .../src/broadcast-update/responsesAreSame.ts | 0 .../src/broadcast-update/types.ts | 0 .../src/broadcast-update/utils/constants.ts | 0 .../src/cacheable-response/CacheableResponse.ts | 0 .../cacheable-response/CacheableResponsePlugin.ts | 0 .../src/cacheable-response/index.ts | 0 .../src/cacheable-response/types.ts | 0 .../src/core/_private.ts | 0 .../src/core/_private/Deferred.ts | 0 .../src/core/_private/WorkboxError.ts | 0 .../src/core/_private/assert.ts | 0 .../src/core/_private/cacheMatchIgnoreParams.ts | 0 .../src/core/_private/cacheNames.ts | 0 .../src/core/_private/canConstructReadableStream.ts | 0 .../_private/canConstructResponseFromBodyStream.ts | 0 .../src/core/_private/dontWaitFor.ts | 0 .../src/core/_private/executeQuotaErrorCallbacks.ts | 0 .../src/core/_private/getFriendlyURL.ts | 0 .../src/core/_private/logger.ts | 0 .../src/core/_private/resultingClientExists.ts | 0 .../src/core/_private/timeout.ts | 0 .../src/core/_private/waitUntil.ts | 0 .../src/core/cacheNames.ts | 0 .../src/core/clientsClaim.ts | 0 .../src/core/copyResponse.ts | 0 .../{workbox-sw => workbox-swkit}/src/core/index.ts | 0 .../src/core/internals.ts | 0 .../src/core/models/messages/messageGenerator.ts | 0 .../src/core/models/messages/messages.ts | 0 .../src/core/models/pluginEvents.ts | 0 .../src/core/models/quotaErrorCallbacks.ts | 0 .../src/core/registerQuotaErrorCallback.ts | 0 .../src/core/setCacheNameDetails.ts | 0 .../src/core/skipWaiting.ts | 0 .../{workbox-sw => workbox-swkit}/src/core/types.ts | 0 .../src/core/utils/pluginUtils.ts | 0 .../src/core/utils/welcome.ts | 0 .../src/expiration/CacheExpiration.ts | 0 .../src/expiration/ExpirationPlugin.ts | 0 .../src/expiration/index.ts | 0 .../src/expiration/models/CacheTimestampsModel.ts | 0 .../src/expiration/types.ts | 0 packages/workbox-swkit/src/index.ts | 12 ++++++++++++ .../src/navigation-preload/disable.ts | 0 .../src/navigation-preload/enable.ts | 0 .../src/navigation-preload/index.ts | 0 .../src/navigation-preload/isSupported.ts | 0 .../src/precaching/PrecacheController.ts | 0 .../src/precaching/PrecacheFallbackPlugin.ts | 0 .../src/precaching/PrecacheRoute.ts | 0 .../src/precaching/PrecacheStrategy.ts | 0 .../src/precaching/addPlugins.ts | 0 .../src/precaching/addRoute.ts | 0 .../src/precaching/cleanupOutdatedCaches.ts | 0 .../src/precaching/createHandlerBoundToURL.ts | 0 .../src/precaching/getCacheKeyForURL.ts | 0 .../src/precaching/index.ts | 0 .../src/precaching/matchPrecache.ts | 0 .../src/precaching/precache.ts | 0 .../src/precaching/precacheAndRoute.ts | 0 .../src/precaching/types.ts | 0 .../src/precaching/utils/PrecacheCacheKeyPlugin.ts | 0 .../precaching/utils/PrecacheInstallReportPlugin.ts | 0 .../src/precaching/utils/createCacheKey.ts | 0 .../src/precaching/utils/deleteOutdatedCaches.ts | 0 .../src/precaching/utils/generateURLVariations.ts | 2 +- .../src/precaching/utils/getCacheKeyForURL.ts | 0 .../utils/getOrCreatePrecacheController.ts | 0 .../src/precaching/utils/printCleanupDetails.ts | 0 .../src/precaching/utils/printInstallDetails.ts | 0 .../precaching/utils/removeIgnoredSearchParams.ts | 0 .../src/range-requests/RangeRequestsPlugin.ts | 0 .../src/range-requests/createPartialResponse.ts | 0 .../src/range-requests/index.ts | 0 .../utils/calculateEffectiveBoundaries.ts | 0 .../src/range-requests/utils/parseRangeHeader.ts | 0 .../src/recipes/googleFontsCache.ts | 0 .../src/recipes/imageCache.ts | 0 .../src/recipes/index.ts | 0 .../src/recipes/offlineFallback.ts | 0 .../src/recipes/pageCache.ts | 0 .../src/recipes/staticResourceCache.ts | 0 .../src/recipes/warmStrategyCache.ts | 0 .../src/routing/NavigationRoute.ts | 0 .../src/routing/RegExpRoute.ts | 0 .../src/routing/Route.ts | 0 .../src/routing/Router.ts | 0 .../src/routing/_types.ts | 0 .../src/routing/index.ts | 0 .../src/routing/registerRoute.ts | 0 .../src/routing/setCatchHandler.ts | 0 .../src/routing/setDefaultHandler.ts | 0 .../src/routing/types.ts | 0 .../src/routing/utils/constants.ts | 0 .../src/routing/utils/getOrCreateDefaultRouter.ts | 0 .../src/routing/utils/normalizeHandler.ts | 0 .../src/strategies/CacheFirst.ts | 2 +- .../src/strategies/CacheOnly.ts | 0 .../src/strategies/NetworkFirst.ts | 0 .../src/strategies/NetworkOnly.ts | 0 .../src/strategies/StaleWhileRevalidate.ts | 0 .../src/strategies/Strategy.ts | 0 .../src/strategies/StrategyHandler.ts | 0 .../src/strategies/index.ts | 0 .../src/strategies/plugins/cacheOkAndOpaquePlugin.ts | 0 .../src/strategies/utils/messages.ts | 0 .../src/streams/concatenate.ts | 0 .../src/streams/concatenateToResponse.ts | 0 .../src/streams/index.ts | 0 .../src/streams/isSupported.ts | 0 .../src/streams/strategy.ts | 0 .../src/streams/types.ts | 0 .../src/streams/utils/createHeaders.ts | 0 .../{workbox-sw => workbox-swkit}/src/sw/index.ts | 0 .../src/sw/workbox-sw.ts | 0 packages/{workbox-sw => workbox-swkit}/src/types.ts | 10 +++++----- .../{workbox-sw => workbox-swkit}/tsdown.config.ts | 0 pnpm-lock.yaml | 6 +++--- tsconfig.json | 8 ++++---- 136 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 packages/workbox-sw/src/index.ts rename packages/{workbox-sw => workbox-swkit}/package.json (97%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/BackgroundSyncPlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/Queue.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/QueueStore.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/StorableRequest.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/lib/QueueDb.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/lib/QueueStore.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/lib/StorableRequest.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/background-sync/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/broadcast-update/BroadcastCacheUpdate.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/broadcast-update/BroadcastUpdatePlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/broadcast-update/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/broadcast-update/responsesAreSame.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/broadcast-update/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/broadcast-update/utils/constants.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/cacheable-response/CacheableResponse.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/cacheable-response/CacheableResponsePlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/cacheable-response/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/cacheable-response/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/Deferred.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/WorkboxError.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/assert.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/cacheMatchIgnoreParams.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/cacheNames.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/canConstructReadableStream.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/canConstructResponseFromBodyStream.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/dontWaitFor.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/executeQuotaErrorCallbacks.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/getFriendlyURL.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/logger.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/resultingClientExists.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/timeout.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/_private/waitUntil.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/cacheNames.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/clientsClaim.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/copyResponse.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/internals.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/models/messages/messageGenerator.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/models/messages/messages.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/models/pluginEvents.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/models/quotaErrorCallbacks.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/registerQuotaErrorCallback.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/setCacheNameDetails.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/skipWaiting.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/utils/pluginUtils.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/core/utils/welcome.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/expiration/CacheExpiration.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/expiration/ExpirationPlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/expiration/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/expiration/models/CacheTimestampsModel.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/expiration/types.ts (100%) create mode 100644 packages/workbox-swkit/src/index.ts rename packages/{workbox-sw => workbox-swkit}/src/navigation-preload/disable.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/navigation-preload/enable.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/navigation-preload/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/navigation-preload/isSupported.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/PrecacheController.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/PrecacheFallbackPlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/PrecacheRoute.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/PrecacheStrategy.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/addPlugins.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/addRoute.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/cleanupOutdatedCaches.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/createHandlerBoundToURL.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/getCacheKeyForURL.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/matchPrecache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/precache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/precacheAndRoute.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/PrecacheCacheKeyPlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/PrecacheInstallReportPlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/createCacheKey.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/deleteOutdatedCaches.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/generateURLVariations.ts (95%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/getCacheKeyForURL.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/getOrCreatePrecacheController.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/printCleanupDetails.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/printInstallDetails.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/precaching/utils/removeIgnoredSearchParams.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/range-requests/RangeRequestsPlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/range-requests/createPartialResponse.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/range-requests/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/range-requests/utils/calculateEffectiveBoundaries.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/range-requests/utils/parseRangeHeader.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/googleFontsCache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/imageCache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/offlineFallback.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/pageCache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/staticResourceCache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/recipes/warmStrategyCache.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/NavigationRoute.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/RegExpRoute.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/Route.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/Router.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/_types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/registerRoute.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/setCatchHandler.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/setDefaultHandler.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/utils/constants.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/utils/getOrCreateDefaultRouter.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/routing/utils/normalizeHandler.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/CacheFirst.ts (97%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/CacheOnly.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/NetworkFirst.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/NetworkOnly.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/StaleWhileRevalidate.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/Strategy.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/StrategyHandler.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/plugins/cacheOkAndOpaquePlugin.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/strategies/utils/messages.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/concatenate.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/concatenateToResponse.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/isSupported.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/strategy.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/types.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/streams/utils/createHeaders.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/sw/index.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/sw/workbox-sw.ts (100%) rename packages/{workbox-sw => workbox-swkit}/src/types.ts (62%) rename packages/{workbox-sw => workbox-swkit}/tsdown.config.ts (100%) diff --git a/packages/workbox-build/package.json b/packages/workbox-build/package.json index 390eec7..73fd187 100644 --- a/packages/workbox-build/package.json +++ b/packages/workbox-build/package.json @@ -65,7 +65,7 @@ "tsx:test": "tsx ./src/generate-sw.ts" }, "dependencies": { - "@vite-pwa/workbox-sw": "workspace:*", + "@vite-pwa/workbox-swkit": "workspace:*", "@vite-pwa/workbox-window": "workspace:*", "magicast": "file:magicast-0.3.5.tgz", "pathe": "catalog:utils", diff --git a/packages/workbox-build/src/generate-sw.ts b/packages/workbox-build/src/generate-sw.ts index 713a303..8eb67ee 100644 --- a/packages/workbox-build/src/generate-sw.ts +++ b/packages/workbox-build/src/generate-sw.ts @@ -45,17 +45,17 @@ export async function generateSW( } if (proxifiedOptions.skipWaiting) { - swModule.imports.$append({ from: '@vite-pwa/workbox-core', imported: 'skipWaiting' }) + swModule.imports.$append({ from: '@vite-pwa/workbox-swkit/core', imported: 'skipWaiting' }) } if (proxifiedOptions.clientsClaim) { - swModule.imports.$append({ from: '@vite-pwa/workbox-core', imported: 'clientsClaim' }) + swModule.imports.$append({ from: '@vite-pwa/workbox-swkit/core', imported: 'clientsClaim' }) } if (proxifiedOptions.runtimeCaching?.length) { - swModule.imports.$append({ from: '@vite-pwa/workbox-routing', imported: 'registerRoute' }) + swModule.imports.$append({ from: '@vite-pwa/workbox-swkit/routing', imported: 'registerRoute' }) if (strategyImports.size > 0) { for (const strategyImport of strategyImports) { swModule.imports.$append({ - from: '@vite-pwa/workbox-strategies', + from: '@vite-pwa/workbox-swkit/strategies', imported: strategyImport, }) } diff --git a/packages/workbox-build/src/types.ts b/packages/workbox-build/src/types.ts index 375f1ce..5a2cfe8 100644 --- a/packages/workbox-build/src/types.ts +++ b/packages/workbox-build/src/types.ts @@ -1,9 +1,9 @@ -import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-sw/broadcast-update/types' -import type { CacheableResponseOptions } from '@vite-pwa/workbox-sw/cacheable-response/types' -import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-sw/core/types' -import type { ExpirationPluginOptions } from '@vite-pwa/workbox-sw/expiration/types' -import type { HTTPMethod } from '@vite-pwa/workbox-sw/routing/types' -import type { QueueOptions } from '@vite-pwa/workbox-sw/types' +import type { BroadcastCacheUpdateOptions } from '@vite-pwa/workbox-swkit/broadcast-update/types' +import type { CacheableResponseOptions } from '@vite-pwa/workbox-swkit/cacheable-response/types' +import type { RouteHandler, RouteMatchCallback, WorkboxPlugin } from '@vite-pwa/workbox-swkit/core/types' +import type { ExpirationPluginOptions } from '@vite-pwa/workbox-swkit/expiration/types' +import type { HTTPMethod } from '@vite-pwa/workbox-swkit/routing/types' +import type { QueueOptions } from '@vite-pwa/workbox-swkit/types' import type { PackageJson } from 'pkg-types' export interface ManifestEntry { diff --git a/packages/workbox-sw/src/index.ts b/packages/workbox-sw/src/index.ts deleted file mode 100644 index 93ab306..0000000 --- a/packages/workbox-sw/src/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export * from './background-sync/index' -export * from './broadcast-update/index' -export * from './cacheable-response/index' -export * from './core/index' -export * from './expiration/index' -export * from './precaching/index' -export * from './range-requests/index' -export * from './recipes/index' -export * from './routing/index' -export * from './strategies/index' -export * from './streams/index' -export * from './sw/index' diff --git a/packages/workbox-sw/package.json b/packages/workbox-swkit/package.json similarity index 97% rename from packages/workbox-sw/package.json rename to packages/workbox-swkit/package.json index fd8af45..d81ca09 100644 --- a/packages/workbox-sw/package.json +++ b/packages/workbox-swkit/package.json @@ -1,5 +1,5 @@ { - "name": "@vite-pwa/workbox-sw", + "name": "@vite-pwa/workbox-swkit", "type": "module", "version": "0.0.0", "description": "This library contains all the packages from workbox packages for the service worker.", @@ -13,7 +13,7 @@ "repository": { "type": "git", "url": "git+https://github.com/vite-pwa/toolkit.git", - "directory": "packages/workbox-sw" + "directory": "packages/workbox-swkit" }, "bugs": "https://github.com/vite-pwa/toolkit/issues", "keywords": [ @@ -153,7 +153,7 @@ } }, "files": [ - "dist" + "./dist" ], "scripts": { "build": "tsdown", diff --git a/packages/workbox-sw/src/background-sync/BackgroundSyncPlugin.ts b/packages/workbox-swkit/src/background-sync/BackgroundSyncPlugin.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/BackgroundSyncPlugin.ts rename to packages/workbox-swkit/src/background-sync/BackgroundSyncPlugin.ts diff --git a/packages/workbox-sw/src/background-sync/Queue.ts b/packages/workbox-swkit/src/background-sync/Queue.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/Queue.ts rename to packages/workbox-swkit/src/background-sync/Queue.ts diff --git a/packages/workbox-sw/src/background-sync/QueueStore.ts b/packages/workbox-swkit/src/background-sync/QueueStore.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/QueueStore.ts rename to packages/workbox-swkit/src/background-sync/QueueStore.ts diff --git a/packages/workbox-sw/src/background-sync/StorableRequest.ts b/packages/workbox-swkit/src/background-sync/StorableRequest.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/StorableRequest.ts rename to packages/workbox-swkit/src/background-sync/StorableRequest.ts diff --git a/packages/workbox-sw/src/background-sync/index.ts b/packages/workbox-swkit/src/background-sync/index.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/index.ts rename to packages/workbox-swkit/src/background-sync/index.ts diff --git a/packages/workbox-sw/src/background-sync/lib/QueueDb.ts b/packages/workbox-swkit/src/background-sync/lib/QueueDb.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/lib/QueueDb.ts rename to packages/workbox-swkit/src/background-sync/lib/QueueDb.ts diff --git a/packages/workbox-sw/src/background-sync/lib/QueueStore.ts b/packages/workbox-swkit/src/background-sync/lib/QueueStore.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/lib/QueueStore.ts rename to packages/workbox-swkit/src/background-sync/lib/QueueStore.ts diff --git a/packages/workbox-sw/src/background-sync/lib/StorableRequest.ts b/packages/workbox-swkit/src/background-sync/lib/StorableRequest.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/lib/StorableRequest.ts rename to packages/workbox-swkit/src/background-sync/lib/StorableRequest.ts diff --git a/packages/workbox-sw/src/background-sync/types.ts b/packages/workbox-swkit/src/background-sync/types.ts similarity index 100% rename from packages/workbox-sw/src/background-sync/types.ts rename to packages/workbox-swkit/src/background-sync/types.ts diff --git a/packages/workbox-sw/src/broadcast-update/BroadcastCacheUpdate.ts b/packages/workbox-swkit/src/broadcast-update/BroadcastCacheUpdate.ts similarity index 100% rename from packages/workbox-sw/src/broadcast-update/BroadcastCacheUpdate.ts rename to packages/workbox-swkit/src/broadcast-update/BroadcastCacheUpdate.ts diff --git a/packages/workbox-sw/src/broadcast-update/BroadcastUpdatePlugin.ts b/packages/workbox-swkit/src/broadcast-update/BroadcastUpdatePlugin.ts similarity index 100% rename from packages/workbox-sw/src/broadcast-update/BroadcastUpdatePlugin.ts rename to packages/workbox-swkit/src/broadcast-update/BroadcastUpdatePlugin.ts diff --git a/packages/workbox-sw/src/broadcast-update/index.ts b/packages/workbox-swkit/src/broadcast-update/index.ts similarity index 100% rename from packages/workbox-sw/src/broadcast-update/index.ts rename to packages/workbox-swkit/src/broadcast-update/index.ts diff --git a/packages/workbox-sw/src/broadcast-update/responsesAreSame.ts b/packages/workbox-swkit/src/broadcast-update/responsesAreSame.ts similarity index 100% rename from packages/workbox-sw/src/broadcast-update/responsesAreSame.ts rename to packages/workbox-swkit/src/broadcast-update/responsesAreSame.ts diff --git a/packages/workbox-sw/src/broadcast-update/types.ts b/packages/workbox-swkit/src/broadcast-update/types.ts similarity index 100% rename from packages/workbox-sw/src/broadcast-update/types.ts rename to packages/workbox-swkit/src/broadcast-update/types.ts diff --git a/packages/workbox-sw/src/broadcast-update/utils/constants.ts b/packages/workbox-swkit/src/broadcast-update/utils/constants.ts similarity index 100% rename from packages/workbox-sw/src/broadcast-update/utils/constants.ts rename to packages/workbox-swkit/src/broadcast-update/utils/constants.ts diff --git a/packages/workbox-sw/src/cacheable-response/CacheableResponse.ts b/packages/workbox-swkit/src/cacheable-response/CacheableResponse.ts similarity index 100% rename from packages/workbox-sw/src/cacheable-response/CacheableResponse.ts rename to packages/workbox-swkit/src/cacheable-response/CacheableResponse.ts diff --git a/packages/workbox-sw/src/cacheable-response/CacheableResponsePlugin.ts b/packages/workbox-swkit/src/cacheable-response/CacheableResponsePlugin.ts similarity index 100% rename from packages/workbox-sw/src/cacheable-response/CacheableResponsePlugin.ts rename to packages/workbox-swkit/src/cacheable-response/CacheableResponsePlugin.ts diff --git a/packages/workbox-sw/src/cacheable-response/index.ts b/packages/workbox-swkit/src/cacheable-response/index.ts similarity index 100% rename from packages/workbox-sw/src/cacheable-response/index.ts rename to packages/workbox-swkit/src/cacheable-response/index.ts diff --git a/packages/workbox-sw/src/cacheable-response/types.ts b/packages/workbox-swkit/src/cacheable-response/types.ts similarity index 100% rename from packages/workbox-sw/src/cacheable-response/types.ts rename to packages/workbox-swkit/src/cacheable-response/types.ts diff --git a/packages/workbox-sw/src/core/_private.ts b/packages/workbox-swkit/src/core/_private.ts similarity index 100% rename from packages/workbox-sw/src/core/_private.ts rename to packages/workbox-swkit/src/core/_private.ts diff --git a/packages/workbox-sw/src/core/_private/Deferred.ts b/packages/workbox-swkit/src/core/_private/Deferred.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/Deferred.ts rename to packages/workbox-swkit/src/core/_private/Deferred.ts diff --git a/packages/workbox-sw/src/core/_private/WorkboxError.ts b/packages/workbox-swkit/src/core/_private/WorkboxError.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/WorkboxError.ts rename to packages/workbox-swkit/src/core/_private/WorkboxError.ts diff --git a/packages/workbox-sw/src/core/_private/assert.ts b/packages/workbox-swkit/src/core/_private/assert.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/assert.ts rename to packages/workbox-swkit/src/core/_private/assert.ts diff --git a/packages/workbox-sw/src/core/_private/cacheMatchIgnoreParams.ts b/packages/workbox-swkit/src/core/_private/cacheMatchIgnoreParams.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/cacheMatchIgnoreParams.ts rename to packages/workbox-swkit/src/core/_private/cacheMatchIgnoreParams.ts diff --git a/packages/workbox-sw/src/core/_private/cacheNames.ts b/packages/workbox-swkit/src/core/_private/cacheNames.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/cacheNames.ts rename to packages/workbox-swkit/src/core/_private/cacheNames.ts diff --git a/packages/workbox-sw/src/core/_private/canConstructReadableStream.ts b/packages/workbox-swkit/src/core/_private/canConstructReadableStream.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/canConstructReadableStream.ts rename to packages/workbox-swkit/src/core/_private/canConstructReadableStream.ts diff --git a/packages/workbox-sw/src/core/_private/canConstructResponseFromBodyStream.ts b/packages/workbox-swkit/src/core/_private/canConstructResponseFromBodyStream.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/canConstructResponseFromBodyStream.ts rename to packages/workbox-swkit/src/core/_private/canConstructResponseFromBodyStream.ts diff --git a/packages/workbox-sw/src/core/_private/dontWaitFor.ts b/packages/workbox-swkit/src/core/_private/dontWaitFor.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/dontWaitFor.ts rename to packages/workbox-swkit/src/core/_private/dontWaitFor.ts diff --git a/packages/workbox-sw/src/core/_private/executeQuotaErrorCallbacks.ts b/packages/workbox-swkit/src/core/_private/executeQuotaErrorCallbacks.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/executeQuotaErrorCallbacks.ts rename to packages/workbox-swkit/src/core/_private/executeQuotaErrorCallbacks.ts diff --git a/packages/workbox-sw/src/core/_private/getFriendlyURL.ts b/packages/workbox-swkit/src/core/_private/getFriendlyURL.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/getFriendlyURL.ts rename to packages/workbox-swkit/src/core/_private/getFriendlyURL.ts diff --git a/packages/workbox-sw/src/core/_private/logger.ts b/packages/workbox-swkit/src/core/_private/logger.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/logger.ts rename to packages/workbox-swkit/src/core/_private/logger.ts diff --git a/packages/workbox-sw/src/core/_private/resultingClientExists.ts b/packages/workbox-swkit/src/core/_private/resultingClientExists.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/resultingClientExists.ts rename to packages/workbox-swkit/src/core/_private/resultingClientExists.ts diff --git a/packages/workbox-sw/src/core/_private/timeout.ts b/packages/workbox-swkit/src/core/_private/timeout.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/timeout.ts rename to packages/workbox-swkit/src/core/_private/timeout.ts diff --git a/packages/workbox-sw/src/core/_private/waitUntil.ts b/packages/workbox-swkit/src/core/_private/waitUntil.ts similarity index 100% rename from packages/workbox-sw/src/core/_private/waitUntil.ts rename to packages/workbox-swkit/src/core/_private/waitUntil.ts diff --git a/packages/workbox-sw/src/core/cacheNames.ts b/packages/workbox-swkit/src/core/cacheNames.ts similarity index 100% rename from packages/workbox-sw/src/core/cacheNames.ts rename to packages/workbox-swkit/src/core/cacheNames.ts diff --git a/packages/workbox-sw/src/core/clientsClaim.ts b/packages/workbox-swkit/src/core/clientsClaim.ts similarity index 100% rename from packages/workbox-sw/src/core/clientsClaim.ts rename to packages/workbox-swkit/src/core/clientsClaim.ts diff --git a/packages/workbox-sw/src/core/copyResponse.ts b/packages/workbox-swkit/src/core/copyResponse.ts similarity index 100% rename from packages/workbox-sw/src/core/copyResponse.ts rename to packages/workbox-swkit/src/core/copyResponse.ts diff --git a/packages/workbox-sw/src/core/index.ts b/packages/workbox-swkit/src/core/index.ts similarity index 100% rename from packages/workbox-sw/src/core/index.ts rename to packages/workbox-swkit/src/core/index.ts diff --git a/packages/workbox-sw/src/core/internals.ts b/packages/workbox-swkit/src/core/internals.ts similarity index 100% rename from packages/workbox-sw/src/core/internals.ts rename to packages/workbox-swkit/src/core/internals.ts diff --git a/packages/workbox-sw/src/core/models/messages/messageGenerator.ts b/packages/workbox-swkit/src/core/models/messages/messageGenerator.ts similarity index 100% rename from packages/workbox-sw/src/core/models/messages/messageGenerator.ts rename to packages/workbox-swkit/src/core/models/messages/messageGenerator.ts diff --git a/packages/workbox-sw/src/core/models/messages/messages.ts b/packages/workbox-swkit/src/core/models/messages/messages.ts similarity index 100% rename from packages/workbox-sw/src/core/models/messages/messages.ts rename to packages/workbox-swkit/src/core/models/messages/messages.ts diff --git a/packages/workbox-sw/src/core/models/pluginEvents.ts b/packages/workbox-swkit/src/core/models/pluginEvents.ts similarity index 100% rename from packages/workbox-sw/src/core/models/pluginEvents.ts rename to packages/workbox-swkit/src/core/models/pluginEvents.ts diff --git a/packages/workbox-sw/src/core/models/quotaErrorCallbacks.ts b/packages/workbox-swkit/src/core/models/quotaErrorCallbacks.ts similarity index 100% rename from packages/workbox-sw/src/core/models/quotaErrorCallbacks.ts rename to packages/workbox-swkit/src/core/models/quotaErrorCallbacks.ts diff --git a/packages/workbox-sw/src/core/registerQuotaErrorCallback.ts b/packages/workbox-swkit/src/core/registerQuotaErrorCallback.ts similarity index 100% rename from packages/workbox-sw/src/core/registerQuotaErrorCallback.ts rename to packages/workbox-swkit/src/core/registerQuotaErrorCallback.ts diff --git a/packages/workbox-sw/src/core/setCacheNameDetails.ts b/packages/workbox-swkit/src/core/setCacheNameDetails.ts similarity index 100% rename from packages/workbox-sw/src/core/setCacheNameDetails.ts rename to packages/workbox-swkit/src/core/setCacheNameDetails.ts diff --git a/packages/workbox-sw/src/core/skipWaiting.ts b/packages/workbox-swkit/src/core/skipWaiting.ts similarity index 100% rename from packages/workbox-sw/src/core/skipWaiting.ts rename to packages/workbox-swkit/src/core/skipWaiting.ts diff --git a/packages/workbox-sw/src/core/types.ts b/packages/workbox-swkit/src/core/types.ts similarity index 100% rename from packages/workbox-sw/src/core/types.ts rename to packages/workbox-swkit/src/core/types.ts diff --git a/packages/workbox-sw/src/core/utils/pluginUtils.ts b/packages/workbox-swkit/src/core/utils/pluginUtils.ts similarity index 100% rename from packages/workbox-sw/src/core/utils/pluginUtils.ts rename to packages/workbox-swkit/src/core/utils/pluginUtils.ts diff --git a/packages/workbox-sw/src/core/utils/welcome.ts b/packages/workbox-swkit/src/core/utils/welcome.ts similarity index 100% rename from packages/workbox-sw/src/core/utils/welcome.ts rename to packages/workbox-swkit/src/core/utils/welcome.ts diff --git a/packages/workbox-sw/src/expiration/CacheExpiration.ts b/packages/workbox-swkit/src/expiration/CacheExpiration.ts similarity index 100% rename from packages/workbox-sw/src/expiration/CacheExpiration.ts rename to packages/workbox-swkit/src/expiration/CacheExpiration.ts diff --git a/packages/workbox-sw/src/expiration/ExpirationPlugin.ts b/packages/workbox-swkit/src/expiration/ExpirationPlugin.ts similarity index 100% rename from packages/workbox-sw/src/expiration/ExpirationPlugin.ts rename to packages/workbox-swkit/src/expiration/ExpirationPlugin.ts diff --git a/packages/workbox-sw/src/expiration/index.ts b/packages/workbox-swkit/src/expiration/index.ts similarity index 100% rename from packages/workbox-sw/src/expiration/index.ts rename to packages/workbox-swkit/src/expiration/index.ts diff --git a/packages/workbox-sw/src/expiration/models/CacheTimestampsModel.ts b/packages/workbox-swkit/src/expiration/models/CacheTimestampsModel.ts similarity index 100% rename from packages/workbox-sw/src/expiration/models/CacheTimestampsModel.ts rename to packages/workbox-swkit/src/expiration/models/CacheTimestampsModel.ts diff --git a/packages/workbox-sw/src/expiration/types.ts b/packages/workbox-swkit/src/expiration/types.ts similarity index 100% rename from packages/workbox-sw/src/expiration/types.ts rename to packages/workbox-swkit/src/expiration/types.ts diff --git a/packages/workbox-swkit/src/index.ts b/packages/workbox-swkit/src/index.ts new file mode 100644 index 0000000..c80a6d8 --- /dev/null +++ b/packages/workbox-swkit/src/index.ts @@ -0,0 +1,12 @@ +export * from './background-sync/index' +export * from './broadcast-update' +export * from './cacheable-response' +export * from './core' +export * from './expiration' +export * from './precaching' +export * from './range-requests' +export * from './recipes' +export * from './routing' +export * from './strategies' +export * from './streams' +export * from './sw' diff --git a/packages/workbox-sw/src/navigation-preload/disable.ts b/packages/workbox-swkit/src/navigation-preload/disable.ts similarity index 100% rename from packages/workbox-sw/src/navigation-preload/disable.ts rename to packages/workbox-swkit/src/navigation-preload/disable.ts diff --git a/packages/workbox-sw/src/navigation-preload/enable.ts b/packages/workbox-swkit/src/navigation-preload/enable.ts similarity index 100% rename from packages/workbox-sw/src/navigation-preload/enable.ts rename to packages/workbox-swkit/src/navigation-preload/enable.ts diff --git a/packages/workbox-sw/src/navigation-preload/index.ts b/packages/workbox-swkit/src/navigation-preload/index.ts similarity index 100% rename from packages/workbox-sw/src/navigation-preload/index.ts rename to packages/workbox-swkit/src/navigation-preload/index.ts diff --git a/packages/workbox-sw/src/navigation-preload/isSupported.ts b/packages/workbox-swkit/src/navigation-preload/isSupported.ts similarity index 100% rename from packages/workbox-sw/src/navigation-preload/isSupported.ts rename to packages/workbox-swkit/src/navigation-preload/isSupported.ts diff --git a/packages/workbox-sw/src/precaching/PrecacheController.ts b/packages/workbox-swkit/src/precaching/PrecacheController.ts similarity index 100% rename from packages/workbox-sw/src/precaching/PrecacheController.ts rename to packages/workbox-swkit/src/precaching/PrecacheController.ts diff --git a/packages/workbox-sw/src/precaching/PrecacheFallbackPlugin.ts b/packages/workbox-swkit/src/precaching/PrecacheFallbackPlugin.ts similarity index 100% rename from packages/workbox-sw/src/precaching/PrecacheFallbackPlugin.ts rename to packages/workbox-swkit/src/precaching/PrecacheFallbackPlugin.ts diff --git a/packages/workbox-sw/src/precaching/PrecacheRoute.ts b/packages/workbox-swkit/src/precaching/PrecacheRoute.ts similarity index 100% rename from packages/workbox-sw/src/precaching/PrecacheRoute.ts rename to packages/workbox-swkit/src/precaching/PrecacheRoute.ts diff --git a/packages/workbox-sw/src/precaching/PrecacheStrategy.ts b/packages/workbox-swkit/src/precaching/PrecacheStrategy.ts similarity index 100% rename from packages/workbox-sw/src/precaching/PrecacheStrategy.ts rename to packages/workbox-swkit/src/precaching/PrecacheStrategy.ts diff --git a/packages/workbox-sw/src/precaching/addPlugins.ts b/packages/workbox-swkit/src/precaching/addPlugins.ts similarity index 100% rename from packages/workbox-sw/src/precaching/addPlugins.ts rename to packages/workbox-swkit/src/precaching/addPlugins.ts diff --git a/packages/workbox-sw/src/precaching/addRoute.ts b/packages/workbox-swkit/src/precaching/addRoute.ts similarity index 100% rename from packages/workbox-sw/src/precaching/addRoute.ts rename to packages/workbox-swkit/src/precaching/addRoute.ts diff --git a/packages/workbox-sw/src/precaching/cleanupOutdatedCaches.ts b/packages/workbox-swkit/src/precaching/cleanupOutdatedCaches.ts similarity index 100% rename from packages/workbox-sw/src/precaching/cleanupOutdatedCaches.ts rename to packages/workbox-swkit/src/precaching/cleanupOutdatedCaches.ts diff --git a/packages/workbox-sw/src/precaching/createHandlerBoundToURL.ts b/packages/workbox-swkit/src/precaching/createHandlerBoundToURL.ts similarity index 100% rename from packages/workbox-sw/src/precaching/createHandlerBoundToURL.ts rename to packages/workbox-swkit/src/precaching/createHandlerBoundToURL.ts diff --git a/packages/workbox-sw/src/precaching/getCacheKeyForURL.ts b/packages/workbox-swkit/src/precaching/getCacheKeyForURL.ts similarity index 100% rename from packages/workbox-sw/src/precaching/getCacheKeyForURL.ts rename to packages/workbox-swkit/src/precaching/getCacheKeyForURL.ts diff --git a/packages/workbox-sw/src/precaching/index.ts b/packages/workbox-swkit/src/precaching/index.ts similarity index 100% rename from packages/workbox-sw/src/precaching/index.ts rename to packages/workbox-swkit/src/precaching/index.ts diff --git a/packages/workbox-sw/src/precaching/matchPrecache.ts b/packages/workbox-swkit/src/precaching/matchPrecache.ts similarity index 100% rename from packages/workbox-sw/src/precaching/matchPrecache.ts rename to packages/workbox-swkit/src/precaching/matchPrecache.ts diff --git a/packages/workbox-sw/src/precaching/precache.ts b/packages/workbox-swkit/src/precaching/precache.ts similarity index 100% rename from packages/workbox-sw/src/precaching/precache.ts rename to packages/workbox-swkit/src/precaching/precache.ts diff --git a/packages/workbox-sw/src/precaching/precacheAndRoute.ts b/packages/workbox-swkit/src/precaching/precacheAndRoute.ts similarity index 100% rename from packages/workbox-sw/src/precaching/precacheAndRoute.ts rename to packages/workbox-swkit/src/precaching/precacheAndRoute.ts diff --git a/packages/workbox-sw/src/precaching/types.ts b/packages/workbox-swkit/src/precaching/types.ts similarity index 100% rename from packages/workbox-sw/src/precaching/types.ts rename to packages/workbox-swkit/src/precaching/types.ts diff --git a/packages/workbox-sw/src/precaching/utils/PrecacheCacheKeyPlugin.ts b/packages/workbox-swkit/src/precaching/utils/PrecacheCacheKeyPlugin.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/PrecacheCacheKeyPlugin.ts rename to packages/workbox-swkit/src/precaching/utils/PrecacheCacheKeyPlugin.ts diff --git a/packages/workbox-sw/src/precaching/utils/PrecacheInstallReportPlugin.ts b/packages/workbox-swkit/src/precaching/utils/PrecacheInstallReportPlugin.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/PrecacheInstallReportPlugin.ts rename to packages/workbox-swkit/src/precaching/utils/PrecacheInstallReportPlugin.ts diff --git a/packages/workbox-sw/src/precaching/utils/createCacheKey.ts b/packages/workbox-swkit/src/precaching/utils/createCacheKey.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/createCacheKey.ts rename to packages/workbox-swkit/src/precaching/utils/createCacheKey.ts diff --git a/packages/workbox-sw/src/precaching/utils/deleteOutdatedCaches.ts b/packages/workbox-swkit/src/precaching/utils/deleteOutdatedCaches.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/deleteOutdatedCaches.ts rename to packages/workbox-swkit/src/precaching/utils/deleteOutdatedCaches.ts diff --git a/packages/workbox-sw/src/precaching/utils/generateURLVariations.ts b/packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts similarity index 95% rename from packages/workbox-sw/src/precaching/utils/generateURLVariations.ts rename to packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts index 893bf2f..f1f52a4 100644 --- a/packages/workbox-sw/src/precaching/utils/generateURLVariations.ts +++ b/packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts @@ -14,7 +14,7 @@ import { removeIgnoredSearchParams } from './removeIgnoredSearchParams' * check, one at a time. * * @param {string} url - * @param {import('../types').PrecacheRouteOptions} [options]')} options + * @param {import('../types.ts').PrecacheRouteOptions} [options]')} options * * @private */ diff --git a/packages/workbox-sw/src/precaching/utils/getCacheKeyForURL.ts b/packages/workbox-swkit/src/precaching/utils/getCacheKeyForURL.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/getCacheKeyForURL.ts rename to packages/workbox-swkit/src/precaching/utils/getCacheKeyForURL.ts diff --git a/packages/workbox-sw/src/precaching/utils/getOrCreatePrecacheController.ts b/packages/workbox-swkit/src/precaching/utils/getOrCreatePrecacheController.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/getOrCreatePrecacheController.ts rename to packages/workbox-swkit/src/precaching/utils/getOrCreatePrecacheController.ts diff --git a/packages/workbox-sw/src/precaching/utils/printCleanupDetails.ts b/packages/workbox-swkit/src/precaching/utils/printCleanupDetails.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/printCleanupDetails.ts rename to packages/workbox-swkit/src/precaching/utils/printCleanupDetails.ts diff --git a/packages/workbox-sw/src/precaching/utils/printInstallDetails.ts b/packages/workbox-swkit/src/precaching/utils/printInstallDetails.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/printInstallDetails.ts rename to packages/workbox-swkit/src/precaching/utils/printInstallDetails.ts diff --git a/packages/workbox-sw/src/precaching/utils/removeIgnoredSearchParams.ts b/packages/workbox-swkit/src/precaching/utils/removeIgnoredSearchParams.ts similarity index 100% rename from packages/workbox-sw/src/precaching/utils/removeIgnoredSearchParams.ts rename to packages/workbox-swkit/src/precaching/utils/removeIgnoredSearchParams.ts diff --git a/packages/workbox-sw/src/range-requests/RangeRequestsPlugin.ts b/packages/workbox-swkit/src/range-requests/RangeRequestsPlugin.ts similarity index 100% rename from packages/workbox-sw/src/range-requests/RangeRequestsPlugin.ts rename to packages/workbox-swkit/src/range-requests/RangeRequestsPlugin.ts diff --git a/packages/workbox-sw/src/range-requests/createPartialResponse.ts b/packages/workbox-swkit/src/range-requests/createPartialResponse.ts similarity index 100% rename from packages/workbox-sw/src/range-requests/createPartialResponse.ts rename to packages/workbox-swkit/src/range-requests/createPartialResponse.ts diff --git a/packages/workbox-sw/src/range-requests/index.ts b/packages/workbox-swkit/src/range-requests/index.ts similarity index 100% rename from packages/workbox-sw/src/range-requests/index.ts rename to packages/workbox-swkit/src/range-requests/index.ts diff --git a/packages/workbox-sw/src/range-requests/utils/calculateEffectiveBoundaries.ts b/packages/workbox-swkit/src/range-requests/utils/calculateEffectiveBoundaries.ts similarity index 100% rename from packages/workbox-sw/src/range-requests/utils/calculateEffectiveBoundaries.ts rename to packages/workbox-swkit/src/range-requests/utils/calculateEffectiveBoundaries.ts diff --git a/packages/workbox-sw/src/range-requests/utils/parseRangeHeader.ts b/packages/workbox-swkit/src/range-requests/utils/parseRangeHeader.ts similarity index 100% rename from packages/workbox-sw/src/range-requests/utils/parseRangeHeader.ts rename to packages/workbox-swkit/src/range-requests/utils/parseRangeHeader.ts diff --git a/packages/workbox-sw/src/recipes/googleFontsCache.ts b/packages/workbox-swkit/src/recipes/googleFontsCache.ts similarity index 100% rename from packages/workbox-sw/src/recipes/googleFontsCache.ts rename to packages/workbox-swkit/src/recipes/googleFontsCache.ts diff --git a/packages/workbox-sw/src/recipes/imageCache.ts b/packages/workbox-swkit/src/recipes/imageCache.ts similarity index 100% rename from packages/workbox-sw/src/recipes/imageCache.ts rename to packages/workbox-swkit/src/recipes/imageCache.ts diff --git a/packages/workbox-sw/src/recipes/index.ts b/packages/workbox-swkit/src/recipes/index.ts similarity index 100% rename from packages/workbox-sw/src/recipes/index.ts rename to packages/workbox-swkit/src/recipes/index.ts diff --git a/packages/workbox-sw/src/recipes/offlineFallback.ts b/packages/workbox-swkit/src/recipes/offlineFallback.ts similarity index 100% rename from packages/workbox-sw/src/recipes/offlineFallback.ts rename to packages/workbox-swkit/src/recipes/offlineFallback.ts diff --git a/packages/workbox-sw/src/recipes/pageCache.ts b/packages/workbox-swkit/src/recipes/pageCache.ts similarity index 100% rename from packages/workbox-sw/src/recipes/pageCache.ts rename to packages/workbox-swkit/src/recipes/pageCache.ts diff --git a/packages/workbox-sw/src/recipes/staticResourceCache.ts b/packages/workbox-swkit/src/recipes/staticResourceCache.ts similarity index 100% rename from packages/workbox-sw/src/recipes/staticResourceCache.ts rename to packages/workbox-swkit/src/recipes/staticResourceCache.ts diff --git a/packages/workbox-sw/src/recipes/warmStrategyCache.ts b/packages/workbox-swkit/src/recipes/warmStrategyCache.ts similarity index 100% rename from packages/workbox-sw/src/recipes/warmStrategyCache.ts rename to packages/workbox-swkit/src/recipes/warmStrategyCache.ts diff --git a/packages/workbox-sw/src/routing/NavigationRoute.ts b/packages/workbox-swkit/src/routing/NavigationRoute.ts similarity index 100% rename from packages/workbox-sw/src/routing/NavigationRoute.ts rename to packages/workbox-swkit/src/routing/NavigationRoute.ts diff --git a/packages/workbox-sw/src/routing/RegExpRoute.ts b/packages/workbox-swkit/src/routing/RegExpRoute.ts similarity index 100% rename from packages/workbox-sw/src/routing/RegExpRoute.ts rename to packages/workbox-swkit/src/routing/RegExpRoute.ts diff --git a/packages/workbox-sw/src/routing/Route.ts b/packages/workbox-swkit/src/routing/Route.ts similarity index 100% rename from packages/workbox-sw/src/routing/Route.ts rename to packages/workbox-swkit/src/routing/Route.ts diff --git a/packages/workbox-sw/src/routing/Router.ts b/packages/workbox-swkit/src/routing/Router.ts similarity index 100% rename from packages/workbox-sw/src/routing/Router.ts rename to packages/workbox-swkit/src/routing/Router.ts diff --git a/packages/workbox-sw/src/routing/_types.ts b/packages/workbox-swkit/src/routing/_types.ts similarity index 100% rename from packages/workbox-sw/src/routing/_types.ts rename to packages/workbox-swkit/src/routing/_types.ts diff --git a/packages/workbox-sw/src/routing/index.ts b/packages/workbox-swkit/src/routing/index.ts similarity index 100% rename from packages/workbox-sw/src/routing/index.ts rename to packages/workbox-swkit/src/routing/index.ts diff --git a/packages/workbox-sw/src/routing/registerRoute.ts b/packages/workbox-swkit/src/routing/registerRoute.ts similarity index 100% rename from packages/workbox-sw/src/routing/registerRoute.ts rename to packages/workbox-swkit/src/routing/registerRoute.ts diff --git a/packages/workbox-sw/src/routing/setCatchHandler.ts b/packages/workbox-swkit/src/routing/setCatchHandler.ts similarity index 100% rename from packages/workbox-sw/src/routing/setCatchHandler.ts rename to packages/workbox-swkit/src/routing/setCatchHandler.ts diff --git a/packages/workbox-sw/src/routing/setDefaultHandler.ts b/packages/workbox-swkit/src/routing/setDefaultHandler.ts similarity index 100% rename from packages/workbox-sw/src/routing/setDefaultHandler.ts rename to packages/workbox-swkit/src/routing/setDefaultHandler.ts diff --git a/packages/workbox-sw/src/routing/types.ts b/packages/workbox-swkit/src/routing/types.ts similarity index 100% rename from packages/workbox-sw/src/routing/types.ts rename to packages/workbox-swkit/src/routing/types.ts diff --git a/packages/workbox-sw/src/routing/utils/constants.ts b/packages/workbox-swkit/src/routing/utils/constants.ts similarity index 100% rename from packages/workbox-sw/src/routing/utils/constants.ts rename to packages/workbox-swkit/src/routing/utils/constants.ts diff --git a/packages/workbox-sw/src/routing/utils/getOrCreateDefaultRouter.ts b/packages/workbox-swkit/src/routing/utils/getOrCreateDefaultRouter.ts similarity index 100% rename from packages/workbox-sw/src/routing/utils/getOrCreateDefaultRouter.ts rename to packages/workbox-swkit/src/routing/utils/getOrCreateDefaultRouter.ts diff --git a/packages/workbox-sw/src/routing/utils/normalizeHandler.ts b/packages/workbox-swkit/src/routing/utils/normalizeHandler.ts similarity index 100% rename from packages/workbox-sw/src/routing/utils/normalizeHandler.ts rename to packages/workbox-swkit/src/routing/utils/normalizeHandler.ts diff --git a/packages/workbox-sw/src/strategies/CacheFirst.ts b/packages/workbox-swkit/src/strategies/CacheFirst.ts similarity index 97% rename from packages/workbox-sw/src/strategies/CacheFirst.ts rename to packages/workbox-swkit/src/strategies/CacheFirst.ts index dd158db..e5fe103 100644 --- a/packages/workbox-sw/src/strategies/CacheFirst.ts +++ b/packages/workbox-swkit/src/strategies/CacheFirst.ts @@ -26,7 +26,7 @@ class CacheFirst extends Strategy { /** * @private * @param request {Request|string} A request to run this strategy for. - * @param handler {import('./index').StrategyHandler} The event that + * @param handler {import('./index.ts').StrategyHandler} The event that * triggered the request. * @return {Promise} */ diff --git a/packages/workbox-sw/src/strategies/CacheOnly.ts b/packages/workbox-swkit/src/strategies/CacheOnly.ts similarity index 100% rename from packages/workbox-sw/src/strategies/CacheOnly.ts rename to packages/workbox-swkit/src/strategies/CacheOnly.ts diff --git a/packages/workbox-sw/src/strategies/NetworkFirst.ts b/packages/workbox-swkit/src/strategies/NetworkFirst.ts similarity index 100% rename from packages/workbox-sw/src/strategies/NetworkFirst.ts rename to packages/workbox-swkit/src/strategies/NetworkFirst.ts diff --git a/packages/workbox-sw/src/strategies/NetworkOnly.ts b/packages/workbox-swkit/src/strategies/NetworkOnly.ts similarity index 100% rename from packages/workbox-sw/src/strategies/NetworkOnly.ts rename to packages/workbox-swkit/src/strategies/NetworkOnly.ts diff --git a/packages/workbox-sw/src/strategies/StaleWhileRevalidate.ts b/packages/workbox-swkit/src/strategies/StaleWhileRevalidate.ts similarity index 100% rename from packages/workbox-sw/src/strategies/StaleWhileRevalidate.ts rename to packages/workbox-swkit/src/strategies/StaleWhileRevalidate.ts diff --git a/packages/workbox-sw/src/strategies/Strategy.ts b/packages/workbox-swkit/src/strategies/Strategy.ts similarity index 100% rename from packages/workbox-sw/src/strategies/Strategy.ts rename to packages/workbox-swkit/src/strategies/Strategy.ts diff --git a/packages/workbox-sw/src/strategies/StrategyHandler.ts b/packages/workbox-swkit/src/strategies/StrategyHandler.ts similarity index 100% rename from packages/workbox-sw/src/strategies/StrategyHandler.ts rename to packages/workbox-swkit/src/strategies/StrategyHandler.ts diff --git a/packages/workbox-sw/src/strategies/index.ts b/packages/workbox-swkit/src/strategies/index.ts similarity index 100% rename from packages/workbox-sw/src/strategies/index.ts rename to packages/workbox-swkit/src/strategies/index.ts diff --git a/packages/workbox-sw/src/strategies/plugins/cacheOkAndOpaquePlugin.ts b/packages/workbox-swkit/src/strategies/plugins/cacheOkAndOpaquePlugin.ts similarity index 100% rename from packages/workbox-sw/src/strategies/plugins/cacheOkAndOpaquePlugin.ts rename to packages/workbox-swkit/src/strategies/plugins/cacheOkAndOpaquePlugin.ts diff --git a/packages/workbox-sw/src/strategies/utils/messages.ts b/packages/workbox-swkit/src/strategies/utils/messages.ts similarity index 100% rename from packages/workbox-sw/src/strategies/utils/messages.ts rename to packages/workbox-swkit/src/strategies/utils/messages.ts diff --git a/packages/workbox-sw/src/streams/concatenate.ts b/packages/workbox-swkit/src/streams/concatenate.ts similarity index 100% rename from packages/workbox-sw/src/streams/concatenate.ts rename to packages/workbox-swkit/src/streams/concatenate.ts diff --git a/packages/workbox-sw/src/streams/concatenateToResponse.ts b/packages/workbox-swkit/src/streams/concatenateToResponse.ts similarity index 100% rename from packages/workbox-sw/src/streams/concatenateToResponse.ts rename to packages/workbox-swkit/src/streams/concatenateToResponse.ts diff --git a/packages/workbox-sw/src/streams/index.ts b/packages/workbox-swkit/src/streams/index.ts similarity index 100% rename from packages/workbox-sw/src/streams/index.ts rename to packages/workbox-swkit/src/streams/index.ts diff --git a/packages/workbox-sw/src/streams/isSupported.ts b/packages/workbox-swkit/src/streams/isSupported.ts similarity index 100% rename from packages/workbox-sw/src/streams/isSupported.ts rename to packages/workbox-swkit/src/streams/isSupported.ts diff --git a/packages/workbox-sw/src/streams/strategy.ts b/packages/workbox-swkit/src/streams/strategy.ts similarity index 100% rename from packages/workbox-sw/src/streams/strategy.ts rename to packages/workbox-swkit/src/streams/strategy.ts diff --git a/packages/workbox-sw/src/streams/types.ts b/packages/workbox-swkit/src/streams/types.ts similarity index 100% rename from packages/workbox-sw/src/streams/types.ts rename to packages/workbox-swkit/src/streams/types.ts diff --git a/packages/workbox-sw/src/streams/utils/createHeaders.ts b/packages/workbox-swkit/src/streams/utils/createHeaders.ts similarity index 100% rename from packages/workbox-sw/src/streams/utils/createHeaders.ts rename to packages/workbox-swkit/src/streams/utils/createHeaders.ts diff --git a/packages/workbox-sw/src/sw/index.ts b/packages/workbox-swkit/src/sw/index.ts similarity index 100% rename from packages/workbox-sw/src/sw/index.ts rename to packages/workbox-swkit/src/sw/index.ts diff --git a/packages/workbox-sw/src/sw/workbox-sw.ts b/packages/workbox-swkit/src/sw/workbox-sw.ts similarity index 100% rename from packages/workbox-sw/src/sw/workbox-sw.ts rename to packages/workbox-swkit/src/sw/workbox-sw.ts diff --git a/packages/workbox-sw/src/types.ts b/packages/workbox-swkit/src/types.ts similarity index 62% rename from packages/workbox-sw/src/types.ts rename to packages/workbox-swkit/src/types.ts index 0fca4a5..3aa09c8 100644 --- a/packages/workbox-sw/src/types.ts +++ b/packages/workbox-swkit/src/types.ts @@ -3,11 +3,11 @@ export type * from './broadcast-update/types' export type * from './cacheable-response/types' export type * from './core/types' export type * from './expiration/types' -export type * from './precaching/index' -export type * from './range-requests/index' -export type * from './recipes/index' +export type * from './precaching' +export type * from './range-requests' +export type * from './recipes' export type * from './routing/types' -export type * from './strategies/index' +export type * from './strategies' export type * from './streams/types' -export type * from './sw/index' +export type * from './sw' export type * from './types' diff --git a/packages/workbox-sw/tsdown.config.ts b/packages/workbox-swkit/tsdown.config.ts similarity index 100% rename from packages/workbox-sw/tsdown.config.ts rename to packages/workbox-swkit/tsdown.config.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43d2f01..e1b3306 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,9 +124,9 @@ importers: packages/workbox-build: dependencies: - '@vite-pwa/workbox-sw': + '@vite-pwa/workbox-swkit': specifier: workspace:* - version: link:../workbox-sw + version: link:../workbox-swkit '@vite-pwa/workbox-window': specifier: workspace:* version: link:../workbox-window @@ -149,7 +149,7 @@ importers: specifier: catalog:utils version: 1.1.0(typescript@5.9.3) - packages/workbox-sw: + packages/workbox-swkit: dependencies: idb: specifier: catalog:utils diff --git a/tsconfig.json b/tsconfig.json index b9adb24..cfecf35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,11 +12,11 @@ "@vite-pwa/workbox-build/*": [ "./packages/workbox-build/src/*.ts" ], - "@vite-pwa/workbox-sw": [ - "./packages/workbox-sw/src/index.ts" + "@vite-pwa/workbox-swkit": [ + "./packages/workbox-swkit/src/index.ts" ], - "@vite-pwa/workbox-sw/*": [ - "./packages/workbox-sw/src/*.ts" + "@vite-pwa/workbox-swkit/*": [ + "./packages/workbox-swkit/src/*.ts" ], "@vite-pwa/workbox-window": [ "./packages/workbox-window/src/index.ts" From 9c6f16ffbe3ae79ea0196abdedc56903dfe60c9c Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 25 Oct 2025 16:53:00 +0200 Subject: [PATCH 19/20] chore: cleanup imports using `.ts` --- .../workbox-swkit/src/precaching/utils/generateURLVariations.ts | 2 +- packages/workbox-swkit/src/strategies/CacheFirst.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts b/packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts index f1f52a4..893bf2f 100644 --- a/packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts +++ b/packages/workbox-swkit/src/precaching/utils/generateURLVariations.ts @@ -14,7 +14,7 @@ import { removeIgnoredSearchParams } from './removeIgnoredSearchParams' * check, one at a time. * * @param {string} url - * @param {import('../types.ts').PrecacheRouteOptions} [options]')} options + * @param {import('../types').PrecacheRouteOptions} [options]')} options * * @private */ diff --git a/packages/workbox-swkit/src/strategies/CacheFirst.ts b/packages/workbox-swkit/src/strategies/CacheFirst.ts index e5fe103..dd158db 100644 --- a/packages/workbox-swkit/src/strategies/CacheFirst.ts +++ b/packages/workbox-swkit/src/strategies/CacheFirst.ts @@ -26,7 +26,7 @@ class CacheFirst extends Strategy { /** * @private * @param request {Request|string} A request to run this strategy for. - * @param handler {import('./index.ts').StrategyHandler} The event that + * @param handler {import('./index').StrategyHandler} The event that * triggered the request. * @return {Promise} */ From 2bb8ba86f8721394a41214190b938d1d86e8a7c0 Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 25 Oct 2025 19:19:33 +0200 Subject: [PATCH 20/20] chore: simplify valibot schemas --- .../src/validation/async-generate-sw.ts | 64 ++--------- .../src/validation/async-get-manifest.ts | 81 +------------- .../src/validation/async-inject-manifest.ts | 89 +-------------- .../workbox-build/src/validation/utils.ts | 104 ++++++++++++++---- .../src/validation/validation-helper.ts | 21 +++- .../workbox-build/test/validation.test.ts | 56 +++++----- 6 files changed, 148 insertions(+), 267 deletions(-) diff --git a/packages/workbox-build/src/validation/async-generate-sw.ts b/packages/workbox-build/src/validation/async-generate-sw.ts index fddf1e0..2e3610a 100644 --- a/packages/workbox-build/src/validation/async-generate-sw.ts +++ b/packages/workbox-build/src/validation/async-generate-sw.ts @@ -1,57 +1,11 @@ import * as v from 'valibot' -import { errors } from './errors' -import { AsyncNumericExpressionSchema, AsyncRuntimeCachingEntrySchema } from './utils' +import { AsyncManifestOptionsSchema, AsyncRuntimeCachingEntrySchema } from './utils' + +export type AsyncGenerateSWOptionsSchemaType = v.InferInput export const AsyncGenerateSWOptionsSchema = v.pipeAsync( v.strictObjectAsync({ - /** - * A list of entries to be precached, in addition to any entries that are generated as part of the build configuration. - */ - additionalManifestEntries: v.optionalAsync(v.arrayAsync(v.unionAsync([ - v.strictObjectAsync({ - integrity: v.optionalAsync(v.string()), - revision: v.nullable(v.string()), - url: v.string(), - }), - v.string(), - ]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their URL, and exempted from the normal HTTP cache-busting that's done when populating the precache. While not required, it's recommended that if your existing build process already inserts a `[hash]` value into each filename, you provide a RegExp that will detect that, as it will reduce the bandwidth consumed when precaching. - */ - dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), - /** - * This value can be used to determine the maximum size of files that will be precached. This prevents you from inadvertently precaching very large files that might have accidentally matched one of your patterns. - */ - maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be used to, e.g., remove or add a path prefix from a manifest entry if your web hosting setup doesn't match your local filesystem setup. As an alternative with more flexibility, you can use the `manifestTransforms` option and provide a function that modifies the entries in the manifest using whatever logic you provide. - */ - modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), - /** - * Determines whether or not symlinks are followed when generating the precache manifest. For more information, see the definition of `follow` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globFollow: v.optionalAsync(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the precache manifest. For more information, see the definition of `ignore` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest will cause the build to fail. If false, the problematic directory will be skipped. For more information, see the definition of `strict` in the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optionalAsync(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may depend on multiple files or on some other unique string value. The keys in this object are server-rendered URLs. If the values are an array of strings, they will be interpreted as `glob` patterns, and the contents of any files matching the patterns will be used to uniquely version the URL. If used with a single string, it will be interpreted as unique versioning information that you've generated for a given URL. - */ - templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), + ...AsyncManifestOptionsSchema.entries, /** * The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to `babel-preset-env` when transpiling the service worker bundle. */ @@ -124,8 +78,8 @@ export const AsyncGenerateSWOptionsSchema = v.pipeAsync( * The path and filename of the service worker file that will be created by the build process, relative to the current working directory. It must end in '.js'. */ swDest: v.pipeAsync( - v.string(errors['invalid-sw-dest']), - v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + v.string(), + v.endsWith('.js', 'invalid-sw-dest-js-ext'), ), /** * The local directory you wish to match `globPatterns` against. The path is @@ -136,14 +90,14 @@ export const AsyncGenerateSWOptionsSchema = v.pipeAsync( v.forwardAsync( v.checkAsync( async input => !!input.runtimeCaching || (typeof input.globDirectory === 'string'), - errors['no-manifest-entries-or-runtime-caching'], + 'no-manifest-entries-or-runtime-caching', ), ['globDirectory'], ), v.forwardAsync( v.checkAsync( async input => !input.navigationPreload || (Array.isArray(input.runtimeCaching) && input.runtimeCaching.length > 0), - errors['nav-preload-runtime-caching'], + 'nav-preload-runtime-caching', ), ['navigationPreload'], ), @@ -163,7 +117,7 @@ export const AsyncGenerateSWOptionsSchema = v.pipeAsync( return true }, - errors['cache-name-required'], + 'cache-name-required', ), ['runtimeCaching'], ), diff --git a/packages/workbox-build/src/validation/async-get-manifest.ts b/packages/workbox-build/src/validation/async-get-manifest.ts index 6d886d7..87fa26b 100644 --- a/packages/workbox-build/src/validation/async-get-manifest.ts +++ b/packages/workbox-build/src/validation/async-get-manifest.ts @@ -1,86 +1,11 @@ import * as v from 'valibot' -import { AsyncNumericExpressionSchema } from './utils' +import { AsyncManifestOptionsSchema } from './utils' -// ManifestEntry is used in additionalManifestEntries -const AsyncManifestEntrySchema = v.strictObjectAsync({ - integrity: v.optionalAsync(v.string()), - revision: v.nullable(v.string()), - url: v.string(), -}) +export type AsyncGetManifestOptionsSchemaType = v.InferInput export const AsyncGetManifestOptionsSchema = v.pipeAsync( v.strictObjectAsync({ - /** - * A list of entries to be precached, in addition to any entries that are - * generated as part of the build configuration. - */ - additionalManifestEntries: v.optionalAsync(v.arrayAsync(v.unionAsync([AsyncManifestEntrySchema, v.string()]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their - * URL, and exempted from the normal HTTP cache-busting that's done when - * populating the precache. While not required, it's recommended that if your - * existing build process already inserts a `[hash]` value into each filename, - * you provide a RegExp that will detect that, as it will reduce the bandwidth - * consumed when precaching. - */ - dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the - * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are - * also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), - /** - * This value can be used to determine the maximum size of files that will be - * precached. This prevents you from inadvertently precaching very large files - * that might have accidentally matched one of your patterns. - * @default 2097152 (2MiB) - */ - maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be - * used to, e.g., remove or add a path prefix from a manifest entry if your - * web hosting setup doesn't match your local filesystem setup. As an - * alternative with more flexibility, you can use the `manifestTransforms` - * option and provide a function that modifies the entries in the manifest - * using whatever logic you provide. - */ - modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), - /** - * Determines whether or not symlinks are followed when generating the - * precache manifest. For more information, see the definition of `follow` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globFollow: v.optionalAsync(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the - * precache manifest. For more information, see the definition of `ignore` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache - * manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest - * will cause the build to fail. If false, the problematic directory will be - * skipped. For more information, see the definition of `strict` in the `glob` - * [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optionalAsync(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may - * depend on multiple files or on some other unique string value. The keys in - * this object are server-rendered URLs. If the values are an array of - * strings, they will be interpreted as `glob` patterns, and the contents of - * any files matching the patterns will be used to uniquely version the URL. - * If used with a single string, it will be interpreted as unique versioning - * information that you've generated for a given URL. - */ - templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), + ...AsyncManifestOptionsSchema.entries, /** * The local directory you wish to match `globPatterns` against. The path is * relative to the current directory. diff --git a/packages/workbox-build/src/validation/async-inject-manifest.ts b/packages/workbox-build/src/validation/async-inject-manifest.ts index 62b0e4b..2f2c109 100644 --- a/packages/workbox-build/src/validation/async-inject-manifest.ts +++ b/packages/workbox-build/src/validation/async-inject-manifest.ts @@ -1,90 +1,11 @@ import * as v from 'valibot' -import { errors } from './errors' -import { AsyncNumericExpressionSchema } from './utils' +import { AsyncManifestOptionsSchema } from './utils' -// ManifestEntry is used in additionalManifestEntries -const AsyncManifestEntrySchema = v.strictObjectAsync({ - integrity: v.optionalAsync(v.string()), - revision: v.nullable(v.string()), - url: v.string(), -}) +export type AsyncInjectManifestOptionsSchemaType = v.InferInput export const AsyncInjectManifestOptionsSchema = v.pipeAsync( v.strictObjectAsync({ - /** - * A list of entries to be precached, in addition to any entries that are - * generated as part of the build configuration. - */ - additionalManifestEntries: v.optionalAsync(v.arrayAsync(v.unionAsync([AsyncManifestEntrySchema, v.string()]))), - /** - * Assets that match this will be assumed to be uniquely versioned via their - * URL, and exempted from the normal HTTP cache-busting that's done when - * populating the precache. While not required, it's recommended that if your - * existing build process already inserts a `[hash]` value into each filename, - * you provide a RegExp that will detect that, as it will reduce the bandwidth - * consumed when precaching. - */ - dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), - /** - * One or more functions which will be applied sequentially against the - * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are - * also specified, their corresponding transformations will be applied first. - */ - manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), - /** - * This value can be used to determine the maximum size of files that will be - * precached. This prevents you from inadvertently precaching very large files - * that might have accidentally matched one of your patterns. - * @default 2MiB (2097152 bytes) - */ - maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), - /** - * An object mapping string prefixes to replacement string values. This can be - * used to, e.g., remove or add a path prefix from a manifest entry if your - * web hosting setup doesn't match your local filesystem setup. As an - * alternative with more flexibility, you can use the `manifestTransforms` - * option and provide a function that modifies the entries in the manifest - * using whatever logic you provide. - */ - modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), - - /** - * Determines whether or not symlinks are followed when generating the - * precache manifest. For more information, see the definition of `follow` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - * @default true - */ - globFollow: v.optionalAsync(v.boolean(), true), - /** - * A set of patterns matching files to always exclude when generating the - * precache manifest. For more information, see the definition of `ignore` in - * the `glob` [documentation](https://github.com/isaacs/node-glob#options). - */ - globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), - /** - * Files matching any of these patterns will be included in the precache - * manifest. For more information, see the - * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). - */ - globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), - /** - * If true, an error reading a directory when generating a precache manifest - * will cause the build to fail. If false, the problematic directory will be - * skipped. For more information, see the definition of `strict` in the `glob` - * [documentation](https://github.com/isaacs/node-glob#options). - */ - globStrict: v.optionalAsync(v.boolean(), true), - /** - * If a URL is rendered based on some server-side logic, its contents may - * depend on multiple files or on some other unique string value. The keys in - * this object are server-rendered URLs. If the values are an array of - * strings, they will be interpreted as `glob` patterns, and the contents of - * any files matching the patterns will be used to uniquely version the URL. - * If used with a single string, it will be interpreted as unique versioning - * information that you've generated for a given URL. - */ - templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), - + ...AsyncManifestOptionsSchema.entries, /** * The string to find inside of the `swSrc` file. Once found, it will be * replaced by the generated precache manifest. @@ -103,8 +24,8 @@ export const AsyncInjectManifestOptionsSchema = v.pipeAsync( * in '.js'. */ swDest: v.pipeAsync( - v.string(errors['missing-sw-dest']), - v.endsWith('.js', errors['invalid-sw-dest-js-ext']), + v.string(), + v.endsWith('.js', 'invalid-sw-dest-js-ext'), ), /** diff --git a/packages/workbox-build/src/validation/utils.ts b/packages/workbox-build/src/validation/utils.ts index c8d9e81..704187a 100644 --- a/packages/workbox-build/src/validation/utils.ts +++ b/packages/workbox-build/src/validation/utils.ts @@ -25,25 +25,6 @@ function inferExpressionType(node: Node): 'number' | 'unknown' { } } -/** - * A custom Valibot schema that accepts either a primitive number - * or a magicast Proxy representing a numeric expression. - */ -export const NumericExpressionSchema = v.custom( - (input) => { - // Accept primitive numbers - if (typeof input === 'number') - return true - // Accept magicast proxies - if (typeof input === 'object' && input !== null && '$ast' in input) { - // And statically validate that the expression will result in a number - return inferExpressionType((input as any).$ast) === 'number' - } - return false - }, - 'invalid-number-entry-or-expression', -) - // ManifestEntry is used in additionalManifestEntries export const AsyncManifestEntrySchema = v.strictObjectAsync({ integrity: v.optionalAsync(v.string()), @@ -56,7 +37,7 @@ export const AsyncManifestEntrySchema = v.strictObjectAsync({ * or a magicast Proxy representing a numeric expression. */ export const AsyncNumericExpressionSchema = v.customAsync( - (input) => { + async (input) => { // Accept primitive numbers if (typeof input === 'number') return true @@ -198,3 +179,86 @@ export const AsyncRuntimeCachingEntrySchema = v.strictObjectAsync({ */ urlPattern: v.unionAsync([v.instance(RegExp), v.function(), v.string()]), }) + +export const AsyncAdditionalManifestEntriesSchema = v.optionalAsync(v.arrayAsync(v.unionAsync([ + AsyncManifestEntrySchema, + v.string(), +]))) + +export const AsyncGlobOptionsSchema = v.objectAsync({ + /** + * This value can be used to determine the maximum size of files that will be + * precached. This prevents you from inadvertently precaching very large files + * that might have accidentally matched one of your patterns. + * @default 2097152 (2MiB) + */ + maximumFileSizeToCacheInBytes: v.optionalAsync(AsyncNumericExpressionSchema, 2097152), + /** + * Determines whether or not symlinks are followed when generating the + * precache manifest. For more information, see the definition of `follow` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globFollow: v.optionalAsync(v.boolean(), true), + /** + * A set of patterns matching files to always exclude when generating the + * precache manifest. For more information, see the definition of `ignore` in + * the `glob` [documentation](https://github.com/isaacs/node-glob#options). + */ + globIgnores: v.optionalAsync(v.arrayAsync(v.string()), ['**/node_modules/**/*']), + /** + * Files matching any of these patterns will be included in the precache + * manifest. For more information, see the + * [`glob` primer](https://github.com/isaacs/node-glob#glob-primer). + */ + globPatterns: v.optionalAsync(v.arrayAsync(v.string()), ['**/*.{js,css,html}']), + /** + * If true, an error reading a directory when generating a precache manifest + * will cause the build to fail. If false, the problematic directory will be + * skipped. For more information, see the definition of `strict` in the `glob` + * [documentation](https://github.com/isaacs/node-glob#options). + */ + globStrict: v.optionalAsync(v.boolean(), true), +}) + +export const AsyncManifestOptionsSchema = v.strictObjectAsync({ + ...AsyncGlobOptionsSchema.entries, + /** + * A list of entries to be precached, in addition to any entries that are + * generated as part of the build configuration. + */ + additionalManifestEntries: AsyncAdditionalManifestEntriesSchema, + /** + * Assets that match this will be assumed to be uniquely versioned via their + * URL, and exempted from the normal HTTP cache-busting that's done when + * populating the precache. While not required, it's recommended that if your + * existing build process already inserts a `[hash]` value into each filename, + * you provide a RegExp that will detect that, as it will reduce the bandwidth + * consumed when precaching. + */ + dontCacheBustURLsMatching: v.optionalAsync(v.instance(RegExp)), + /** + * One or more functions which will be applied sequentially against the + * generated manifest. If `modifyURLPrefix` or `dontCacheBustURLsMatching` are + * also specified, their corresponding transformations will be applied first. + */ + manifestTransforms: v.optionalAsync(v.arrayAsync(v.function())), + /** + * An object mapping string prefixes to replacement string values. This can be + * used to, e.g., remove or add a path prefix from a manifest entry if your + * web hosting setup doesn't match your local filesystem setup. As an + * alternative with more flexibility, you can use the `manifestTransforms` + * option and provide a function that modifies the entries in the manifest + * using whatever logic you provide. + */ + modifyURLPrefix: v.optionalAsync(v.recordAsync(v.string(), v.string())), + /** + * If a URL is rendered based on some server-side logic, its contents may + * depend on multiple files or on some other unique string value. The keys in + * this object are server-rendered URLs. If the values are an array of + * strings, they will be interpreted as `glob` patterns, and the contents of + * any files matching the patterns will be used to uniquely version the URL. + * If used with a single string, it will be interpreted as unique versioning + * information that you've generated for a given URL. + */ + templatedURLs: v.optionalAsync(v.recordAsync(v.string(), v.unionAsync([v.arrayAsync(v.string()), v.string()]))), +}) diff --git a/packages/workbox-build/src/validation/validation-helper.ts b/packages/workbox-build/src/validation/validation-helper.ts index 1fa6f8d..59e9a81 100644 --- a/packages/workbox-build/src/validation/validation-helper.ts +++ b/packages/workbox-build/src/validation/validation-helper.ts @@ -1,6 +1,7 @@ // import type { ArrayExpression, Program } from '@babel/types' import type { BaseIssue, BaseSchemaAsync, InferOutput, IssuePathItem } from 'valibot' import { getDotPath, safeParseAsync } from 'valibot' +import { errors } from './errors' // This helper function traverses the issue's path to find the top-level object key // that contains the error. This is crucial for errors nested inside arrays. @@ -13,14 +14,31 @@ function getTopLevelKey(path: IssuePathItem[] | undefined): string | undefined { return path[0].key as string | undefined } +const requiredErrorMap: Record = { + swSrc: 'invalid-sw-src', + swDest: 'missing-sw-dest', + globDirectory: 'invalid-glob-directory', +} as const + // see [Path Key not Available in safeParse](https://github.com/fabian-hiller/valibot/discussions/696). // custom Valibot's message mapping function extractIssueMessage(issue: BaseIssue) { - // console.log(issue) + console.log(issue) const path = getDotPath(issue) const topLevelKey = getTopLevelKey(issue.path) const lastKey = issue.path?.[issue.path.length - 1]?.key + // If the message is a key in our errors object, use it. + if (issue.message && issue.message in errors) { + return errors[issue.message as keyof typeof errors] + } + + console.log(path, path && path in requiredErrorMap) + if (path && path in requiredErrorMap) { + console.log('WTF') + return errors[requiredErrorMap[path]] + } + // Priority 1: Custom messages from pipes (e.g., endsWith). // These are the most specific and should always be shown. if (issue.type === 'custom') { @@ -34,6 +52,7 @@ function extractIssueMessage(issue: BaseIssue) { // Priority 2: Missing required key. if (issue.kind === 'schema' && issue.received === 'undefined') { + // Otherwise, fall back to the generic message. return `The option "${path}" is required.` } diff --git a/packages/workbox-build/test/validation.test.ts b/packages/workbox-build/test/validation.test.ts index b24b0b4..027524b 100644 --- a/packages/workbox-build/test/validation.test.ts +++ b/packages/workbox-build/test/validation.test.ts @@ -1,5 +1,7 @@ -import type * as v from 'valibot' import type { GenerateSWOptions, GetManifestOptions, InjectManifestOptions } from '../src/types' +import type { AsyncGenerateSWOptionsSchemaType } from '../src/validation/async-generate-sw' +import type { AsyncGetManifestOptionsSchemaType } from '../src/validation/async-get-manifest' +import type { AsyncInjectManifestOptionsSchemaType } from '../src/validation/async-inject-manifest' import { describe, expect, expectTypeOf, it } from 'vitest' import { AsyncGenerateSWOptionsSchema } from '../src/validation/async-generate-sw' import { AsyncGetManifestOptionsSchema } from '../src/validation/async-get-manifest' @@ -7,20 +9,16 @@ import { AsyncInjectManifestOptionsSchema } from '../src/validation/async-inject import { errors } from '../src/validation/errors' import { validateAsync } from '../src/validation/validation-helper' -type GenerateSWOptionsSchemaType = v.InferInput -type GetManifestOptionsSchemaType = v.InferInput -type InjectManifestOptionsSchemaType = v.InferInput - describe('schema Type Inference Validation', () => { it('should correctly infer types that are compatible with the original Workbox types', () => { // @ts-expect-error - This is a type-level test. The IDE may complain because the nominal // types are different, but `expectTypeOf` checks for structural compatibility, which is what we care about. // This test will fail at COMPILE time if the schema's inferred type is not assignable to the original Workbox type. - expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() // @ts-expect-error - See comment above. - expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() // @ts-expect-error - See comment above. - expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() }) }) @@ -39,9 +37,9 @@ describe('generateSWOptions Schema Validation', () => { const options = { globDirectory: './', } satisfies Partial - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( - `generateSW() options validation failed: \n- The option "swDest" is required.`, + `generateSW() options validation failed: \n- ${errors['missing-sw-dest']}`, ) }) @@ -66,7 +64,7 @@ describe('generateSWOptions Schema Validation', () => { globDirectory: './', manifestTransforms: ['not-a-function'], } satisfies Omit & { manifestTransforms: string[] } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) @@ -78,7 +76,7 @@ describe('generateSWOptions Schema Validation', () => { globDirectory: './', manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), } satisfies Omit & { manifestTransforms: (manifest: any) => void } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The "manifestTransforms" option must be an array.', ) @@ -105,7 +103,7 @@ describe('generateSWOptions Schema Validation', () => { globDirectory: './', importWorkboxFrom: 'cdn', } satisfies GenerateSWOptions & { importWorkboxFrom?: string } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options as any, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The option "importWorkboxFrom" is unknown or has been deprecated.', ) @@ -145,7 +143,7 @@ describe('generateSWOptions Schema Validation', () => { globDirectory: './', runtimeCaching: 'a-string-not-an-array', } satisfies Omit & { runtimeCaching: string } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The "runtimeCaching" option must be an array.', ) @@ -159,7 +157,7 @@ describe('generateSWOptions Schema Validation', () => { urlPattern: /.*/, }], } satisfies Omit & { runtimeCaching: { urlPattern: RegExp }[] } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- The option "runtimeCaching.0.handler" is required.', ) @@ -174,7 +172,7 @@ describe('generateSWOptions Schema Validation', () => { handler: 123, }], } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: number }[] } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received 123.', ) @@ -189,7 +187,7 @@ describe('generateSWOptions Schema Validation', () => { handler: 'InvalidStrategy', }], } satisfies Omit & { runtimeCaching: { urlPattern: RegExp, handler: 'InvalidStrategy' }[] } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGenerateSWOptionsSchema, options, 'generateSW')).rejects.toThrow( 'generateSW() options validation failed: \n- Invalid "handler" option in runtimeCaching[0]. Invalid type: Expected (Function | Object | ("CacheFirst" | "CacheOnly" | "NetworkFirst" | "NetworkOnly" | "StaleWhileRevalidate")) but received "InvalidStrategy"', ) @@ -209,9 +207,9 @@ describe('getManifestOptions Schema Validation', () => { it('should fail if globDirectory is missing', async () => { const options = {} satisfies Partial - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( - 'getManifest() options validation failed: \n- The option "globDirectory" is required.', + `getManifest() options validation failed: \n- ${errors['invalid-glob-directory']}`, ) }) @@ -220,7 +218,7 @@ describe('getManifestOptions Schema Validation', () => { globDirectory: './', manifestTransforms: ['not-a-function'], } satisfies Omit & { manifestTransforms: string[] } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) @@ -231,7 +229,7 @@ describe('getManifestOptions Schema Validation', () => { globDirectory: './', manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), } satisfies Omit & { manifestTransforms: (manifest: any) => void } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', ) @@ -242,7 +240,7 @@ describe('getManifestOptions Schema Validation', () => { globDirectory: './', anotherUnknown: 'noop', } satisfies GetManifestOptions & { anotherUnknown?: string } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncGetManifestOptionsSchema, options as any, 'getManifest')).rejects.toThrow( 'getManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', ) @@ -257,7 +255,7 @@ describe('injectManifestOptions Schema Validation', () => { globDirectory: './', } satisfies InjectManifestOptions // @ts-expect-error - schema Type Inference Validation. - expectTypeOf().toMatchObjectType() + expectTypeOf().toMatchObjectType() await expect(validateAsync(AsyncInjectManifestOptionsSchema, options, 'injectManifest')).resolves.not.toThrow() }) @@ -266,9 +264,9 @@ describe('injectManifestOptions Schema Validation', () => { swDest: 'sw-injected.js', globDirectory: './', } satisfies Partial - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( - 'injectManifest() options validation failed: \n- The option "swSrc" is required.', + `injectManifest() options validation failed: \n- ${errors['invalid-sw-src']}`, ) }) @@ -279,7 +277,7 @@ describe('injectManifestOptions Schema Validation', () => { globDirectory: './', manifestTransforms: ['not-a-function'], } satisfies Omit & { manifestTransforms: string[] } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- Each item in the "manifestTransforms" array must be a function (error at index 0).', ) @@ -292,7 +290,7 @@ describe('injectManifestOptions Schema Validation', () => { globDirectory: './', manifestTransforms: (manifest: any) => ({ manifest, warnings: [] }), } satisfies Omit & { manifestTransforms: (manifest: any) => void } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- The "manifestTransforms" option must be an array.', ) @@ -306,7 +304,7 @@ describe('injectManifestOptions Schema Validation', () => { } satisfies InjectManifestOptions // The shape is valid, but the content is not. // @ts-expect-error - schema Type Inference Validation. - expectTypeOf().toMatchObjectType() + expectTypeOf().toMatchObjectType() await expect(validateAsync(AsyncInjectManifestOptionsSchema, options, 'injectManifest')).rejects.toThrow( `injectManifest() options validation failed: \n- ${errors['invalid-sw-dest-js-ext']}`, ) @@ -319,7 +317,7 @@ describe('injectManifestOptions Schema Validation', () => { globDirectory: './', anotherUnknown: 'noop', } satisfies InjectManifestOptions & { anotherUnknown?: string } - expectTypeOf().not.toMatchObjectType() + expectTypeOf().not.toMatchObjectType() await expect(validateAsync(AsyncInjectManifestOptionsSchema, options as any, 'injectManifest')).rejects.toThrow( 'injectManifest() options validation failed: \n- The option "anotherUnknown" is unknown or has been deprecated.', )