You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This adds a first-class build output manifest for static app caching and pairs it with site service-worker support.
The goal is to let a domstack app build a fully static client, then have its own service worker consume a normalized, revisioned list of emitted files. Domstack provides the build facts; the application still owns service-worker registration, update UX, route filtering, offline behavior, and cache policy.
What Changed
Add domstack-output-manifest.json, written by one-shot builds by default.
Return results.outputManifest from programmatic builds.
Track outputs from esbuild, pages, templates, static copies, and configured copy dirs through a shared report.outputs record collection.
Reconcile those records once at the end of the build into public manifest entries with URLs, revisions, byte sizes, kinds, and optional source/page metadata.
Add a committed JSON Schema for the manifest with a versioned unpkg $schema URL.
Derive/export the public manifest types from the schema with json-schema-to-ts.
Add manifest.settings.{js,mjs,cjs,ts,mts,cts} for build-output-manifest filtering policy.
Add first-class site service-worker entries: one service-worker.{js,mjs,cjs,ts,mts,cts} anywhere under src builds to stable root /service-worker.js.
Add domstack-owned browser defines for service workers and client bundles.
Keep watch mode simple: service workers still rebundle in watch mode, but output manifests are not written or returned in watch mode.
Replace .npmignore with a package.json#files allowlist.
How The Manifest Works
Each build step records files as it emits them instead of trying to reconstruct the build from loosely shaped downstream reports. At the end of a one-shot build, domstack reconciles those records:
Normalize each output path relative to dest.
Assert recorded files are inside the output directory.
Convert output names into public URLs.
Hash file contents with SHA-256 for revision.
Record bytes, kind, and optional source/page metadata.
De-dupe by output path using kind priority.
Apply configured exclude patterns and includeOutput(entry) hooks.
Generate a stable manifest version from cache-relevant fields: url, revision, kind, and page-level precache / offline vars.
Write the public JSON manifest with $schema, version, generatedAt, and entries.
The manifest intentionally does not expose local filesystem paths. It is a public serialized contract for deploy tools, service workers, and application code.
results.outputManifest is still returned when writing the JSON file is disabled with outputManifest: false. Watch mode does not return an output manifest.
This follows the existing Domstack settings-file pattern used by esbuild.settings.* and markdown-it.settings.*. The default export can be an object, a sync function, or an async function returning an object.
Use this when the app wants its build output manifest to reflect application policy, for example keeping admin pages, blog content, source maps, feeds, or other deployment-only files out of a PWA precache list.
outputManifest.exclude from CLI/programmatic config and manifest.settings.*exclude values are combined.
Exclude patterns use ignore-style matching against both entry.url and entry.outputRelname.
Excludes run before includeOutput(entry).
includeOutput(entry) receives the public manifest entry shape, not internal filesystem details.
Filtering affects both the written domstack-output-manifest.json and results.outputManifest.
Filtering also affects manifest.version, because versioning is based on the final cache-relevant entries.
Watch mode intentionally ignores this file because watch mode does not write or return an output manifest.
Useful entry fields for policy decisions:
entry.url// public URL, such as "/docs/" or "/chunks/app-ABC.js"entry.outputRelname// dest-relative output name, such as "docs/index.html"entry.kind// page, script, style, chunk, copy, service-worker, etc.entry.sourceRelname// source-relative path when availableentry.page?.vars// page-level precache/offline vars when present
Domstack records page-level precache and offline vars onto page entries when present, but it does not interpret those flags itself. They are metadata for service workers, deployment tools, or includeOutput(entry) policies.
For example, a page can opt out of an app's offline policy:
exportconstvars={precache: false,}
Then the app's includeOutput(entry) hook can decide whether that page entry should remain in the output manifest.
The public BuildOutputManifest, BuildOutputEntry, BuildOutputEntryPageMeta, and BuildOutputKind types are derived from those schemas and exported through the package type surface.
Service Worker Support
Domstack now reserves one site service-worker source filename:
The source may live anywhere under src, but only one is allowed. Multiple matches fail with DOM_STACK_ERROR_DUPLICATE_SERVICE_WORKER.
The service worker is bundled through esbuild like the rest of the browser-side code, but it is emitted with a stable root output path:
/service-worker.js
That stable root URL matters because browsers check service-worker updates at the script URL, and root placement gives the worker root scope without requiring a Service-Worker-Allowed header.
The built service worker is included in:
the merged esbuild metafile
the output manifest as kind: 'service-worker'
Browser Defines
Domstack injects these build facts into browser-side bundles:
Define
Value
process.env.DOMSTACK_OUTPUT_MANIFEST_URL
Public URL of the written manifest, usually /domstack-output-manifest.json
process.env.DOMSTACK_OUTPUT_MANIFEST_ENABLED
"true" for one-shot builds that write the manifest, "false" when disabled or in watch mode
process.env.DOMSTACK_SERVICE_WORKER_URL
Public service-worker URL, usually /service-worker.js, or "" when absent
process.env.DOMSTACK_SERVICE_WORKER_SCOPE
Registration scope, usually /, or "" when absent
A service worker can use the manifest URL at install time:
Domstack does not inject registration into the default layout. Registration timing, update prompts, local-development opt-outs, poisoned-cache recovery, and offline route behavior are application policy.
Compatibility Notes
copy is now the output kind for files copied by the generic copy step.
The manifest entry shape includes the Workbox-compatible url / revision pair, but this PR does not add a Workbox-specific generated artifact. There is a TODO to consider a derived Workbox manifest only after a concrete use case validates the API.
Watch mode intentionally does not write domstack-output-manifest.json or return results.outputManifest; it does still build and rebundle the site service worker.
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/@typescript-eslint/eslint-plugin@8.61.0. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
Warn
Obfuscated code: npm js-yaml is 90.0% likely obfuscated
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/js-yaml@4.2.0. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
Warn
Obfuscated code: npm preact is 90.0% likely obfuscated
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/preact@10.29.2. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds a first-class build output manifest for static app caching and pairs it with site service-worker support.
The goal is to let a domstack app build a fully static client, then have its own service worker consume a normalized, revisioned list of emitted files. Domstack provides the build facts; the application still owns service-worker registration, update UX, route filtering, offline behavior, and cache policy.
What Changed
domstack-output-manifest.json, written by one-shot builds by default.results.outputManifestfrom programmatic builds.report.outputsrecord collection.$schemaURL.json-schema-to-ts.manifest.settings.{js,mjs,cjs,ts,mts,cts}for build-output-manifest filtering policy.service-worker.{js,mjs,cjs,ts,mts,cts}anywhere undersrcbuilds to stable root/service-worker.js..npmignorewith apackage.json#filesallowlist.How The Manifest Works
Each build step records files as it emits them instead of trying to reconstruct the build from loosely shaped downstream reports. At the end of a one-shot build, domstack reconciles those records:
dest.revision.bytes,kind, and optional source/page metadata.excludepatterns andincludeOutput(entry)hooks.versionfrom cache-relevant fields:url,revision,kind, and page-levelprecache/offlinevars.$schema,version,generatedAt, andentries.The manifest intentionally does not expose local filesystem paths. It is a public serialized contract for deploy tools, service workers, and application code.
Manifest Shape
The written file looks like:
{ "$schema": "https://unpkg.com/@domstack/static@x.y.z/lib/build-output-manifest/schema.json", "version": "sha256-of-cache-relevant-output-metadata", "generatedAt": "2026-06-16T00:00:00.000Z", "entries": [ { "outputRelname": "index.html", "kind": "page", "url": "/", "revision": "sha256-file-content", "bytes": 1234 } ] }Entry
kindvalues are:New Public APIs
Build Results
Programmatic builds now return:
results.outputManifestis still returned when writing the JSON file is disabled withoutputManifest: false. Watch mode does not return an output manifest.CLI Options
Programmatic Options
Supported shape:
manifest.settings.*Apps can configure the generated manifest from a dedicated settings file anywhere under
src:This follows the existing Domstack settings-file pattern used by
esbuild.settings.*andmarkdown-it.settings.*. The default export can be an object, a sync function, or an async function returning an object.Use this when the app wants its build output manifest to reflect application policy, for example keeping admin pages, blog content, source maps, feeds, or other deployment-only files out of a PWA precache list.
Dynamic settings are supported too:
Supported shape:
How it is applied:
outputManifest.excludefrom CLI/programmatic config andmanifest.settings.*excludevalues are combined.entry.urlandentry.outputRelname.includeOutput(entry).includeOutput(entry)receives the public manifest entry shape, not internal filesystem details.domstack-output-manifest.jsonandresults.outputManifest.manifest.version, because versioning is based on the final cache-relevant entries.Useful entry fields for policy decisions:
Domstack records page-level
precacheandofflinevars onto page entries when present, but it does not interpret those flags itself. They are metadata for service workers, deployment tools, orincludeOutput(entry)policies.For example, a page can opt out of an app's offline policy:
Then the app's
includeOutput(entry)hook can decide whether that page entry should remain in the output manifest.Schema And Types
Domstack now exports:
The public
BuildOutputManifest,BuildOutputEntry,BuildOutputEntryPageMeta, andBuildOutputKindtypes are derived from those schemas and exported through the package type surface.Service Worker Support
Domstack now reserves one site service-worker source filename:
The source may live anywhere under
src, but only one is allowed. Multiple matches fail withDOM_STACK_ERROR_DUPLICATE_SERVICE_WORKER.The service worker is bundled through esbuild like the rest of the browser-side code, but it is emitted with a stable root output path:
That stable root URL matters because browsers check service-worker updates at the script URL, and root placement gives the worker root scope without requiring a
Service-Worker-Allowedheader.The built service worker is included in:
kind: 'service-worker'Browser Defines
Domstack injects these build facts into browser-side bundles:
process.env.DOMSTACK_OUTPUT_MANIFEST_URL/domstack-output-manifest.jsonprocess.env.DOMSTACK_OUTPUT_MANIFEST_ENABLED"true"for one-shot builds that write the manifest,"false"when disabled or in watch modeprocess.env.DOMSTACK_SERVICE_WORKER_URL/service-worker.js, or""when absentprocess.env.DOMSTACK_SERVICE_WORKER_SCOPE/, or""when absentA service worker can use the manifest URL at install time:
Application client code can register the worker from
global.client.js:Domstack does not inject registration into the default layout. Registration timing, update prompts, local-development opt-outs, poisoned-cache recovery, and offline route behavior are application policy.
Compatibility Notes
copyis now the output kind for files copied by the generic copy step.url/revisionpair, but this PR does not add a Workbox-specific generated artifact. There is a TODO to consider a derived Workbox manifest only after a concrete use case validates the API.domstack-output-manifest.jsonor returnresults.outputManifest; it does still build and rebundle the site service worker.Testing
node --test lib/identify-pages.test.js test-cases/general-features/index.test.jsnpm run test:tscnpm run test:neostandardnpm run build:declarationnpm run build:schemagit diff --check