@_linked/cli 1.18.0 → 1.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +119 -0
  3. package/lib/esm/app-release/app-assets-store.d.ts +23 -0
  4. package/lib/esm/app-release/app-assets-store.d.ts.map +1 -0
  5. package/lib/esm/app-release/app-assets-store.js +33 -0
  6. package/lib/esm/app-release/app-assets-store.js.map +1 -0
  7. package/lib/esm/app-release/create-release-manifest.d.ts +114 -0
  8. package/lib/esm/app-release/create-release-manifest.d.ts.map +1 -0
  9. package/lib/esm/app-release/create-release-manifest.js +238 -0
  10. package/lib/esm/app-release/create-release-manifest.js.map +1 -0
  11. package/lib/esm/app-release/paths.d.ts +5 -0
  12. package/lib/esm/app-release/paths.d.ts.map +1 -0
  13. package/lib/esm/app-release/paths.js +44 -0
  14. package/lib/esm/app-release/paths.js.map +1 -0
  15. package/lib/esm/app-release/publisher.d.ts +25 -0
  16. package/lib/esm/app-release/publisher.d.ts.map +1 -0
  17. package/lib/esm/app-release/publisher.js +238 -0
  18. package/lib/esm/app-release/publisher.js.map +1 -0
  19. package/lib/esm/app-release/resolve-build-target.d.ts +19 -0
  20. package/lib/esm/app-release/resolve-build-target.d.ts.map +1 -0
  21. package/lib/esm/app-release/resolve-build-target.js +30 -0
  22. package/lib/esm/app-release/resolve-build-target.js.map +1 -0
  23. package/lib/esm/app-release/static-assets.d.ts +13 -0
  24. package/lib/esm/app-release/static-assets.d.ts.map +1 -0
  25. package/lib/esm/app-release/static-assets.js +55 -0
  26. package/lib/esm/app-release/static-assets.js.map +1 -0
  27. package/lib/esm/app-release/types.d.ts +71 -0
  28. package/lib/esm/app-release/types.d.ts.map +1 -0
  29. package/lib/esm/app-release/types.js +2 -0
  30. package/lib/esm/app-release/types.js.map +1 -0
  31. package/lib/esm/cli.js +78 -16
  32. package/lib/esm/cli.js.map +1 -1
  33. package/lib/esm/commands/build-app.d.ts +55 -0
  34. package/lib/esm/commands/build-app.d.ts.map +1 -0
  35. package/lib/esm/commands/build-app.js +178 -0
  36. package/lib/esm/commands/build-app.js.map +1 -0
  37. package/lib/esm/commands/publish-app.d.ts +18 -0
  38. package/lib/esm/commands/publish-app.d.ts.map +1 -0
  39. package/lib/esm/commands/publish-app.js +124 -0
  40. package/lib/esm/commands/publish-app.js.map +1 -0
  41. package/lib/esm/commands/serve-app.d.ts +14 -0
  42. package/lib/esm/commands/serve-app.d.ts.map +1 -0
  43. package/lib/esm/commands/serve-app.js +49 -0
  44. package/lib/esm/commands/serve-app.js.map +1 -0
  45. package/lib/esm/index.d.ts +11 -0
  46. package/lib/esm/index.d.ts.map +1 -1
  47. package/lib/esm/index.js +9 -0
  48. package/lib/esm/index.js.map +1 -1
  49. package/lib/esm/lifecycle.d.ts +1 -1
  50. package/lib/esm/lifecycle.d.ts.map +1 -1
  51. package/lib/esm/lifecycle.js +17 -8
  52. package/lib/esm/lifecycle.js.map +1 -1
  53. package/lib/esm/vite-config.d.ts +12 -0
  54. package/lib/esm/vite-config.d.ts.map +1 -1
  55. package/lib/esm/vite-config.js +54 -39
  56. package/lib/esm/vite-config.js.map +1 -1
  57. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,109 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.19.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#102](https://github.com/linked-fw/cli/pull/102) [`f3f2ea9`](https://github.com/linked-fw/cli/commit/f3f2ea918a357b683b1f9005d533776471d1d7ab) Thanks [@flyon](https://github.com/flyon)! - Keep `react-router` and `@remix-run/router` in the `react-vendor` chunk.
8
+
9
+ `react-router-dom` was grouped with React but `react-router`, which it
10
+ re-exports, was left in the catch-all `vendor` chunk. That made the two chunks
11
+ import each other — `vendor` also runs `React.createContext(...)` at module
12
+ scope — and Rollup resolved the cycle by running `vendor` first, so every
13
+ production build threw `Cannot read properties of undefined (reading
14
+ 'createContext')` before the app rendered a single frame.
15
+
16
+ The chunk routing is now exported as `chunkForModuleId` and covered by unit
17
+ tests, since getting it wrong produces a bundle that builds cleanly and only
18
+ fails in the browser.
19
+
20
+ ## 1.19.0
21
+
22
+ ### Minor Changes
23
+
24
+ - [#100](https://github.com/linked-fw/cli/pull/100) [`8ef0fd7`](https://github.com/linked-fw/cli/commit/8ef0fd7f810dfff26b95f5390565661b8c38c7ac) Thanks [@flyon](https://github.com/flyon)! - ## App release pipeline on the file-store API
25
+
26
+ A manifest-driven build/publish/serve flow for Linked apps, replacing the old recursive upload of
27
+ everything under `public/`.
28
+
29
+ ### New commands
30
+
31
+ ```bash
32
+ linked build-app --env <env> --target web # build + write public/bundles/linked-release.json
33
+ linked build-app --env <env> --publish # ...and upload it in the same run
34
+ linked build-app --env <env> --target capacitor # local, non-publishable manifest; never uploads
35
+ linked build-app --revision <sha> # identify the release outside a git checkout
36
+ linked build-app --allow-dirty # build uncommitted work as <sha>-dirty
37
+ linked publish-app --env <env> # dry run by default; pass --yes to upload
38
+ linked publish-app --manifest <path> # publish a manifest from another path
39
+ linked serve-app --env <env> # compiled runtime, no Vite/HMR
40
+ ```
41
+
42
+ `build-app` never uploads on its own — it writes a release manifest, and `publish-app` uploads
43
+ exactly the files that manifest lists.
44
+
45
+ ### Publishing destination
46
+
47
+ Uploads go through `LinkedFileStorage.getStore(FileStorePurposes.appAssets)`. An app that dedicates a
48
+ bundle store configures `setStore(FileStorePurposes.appAssets, store)`; an app with a single store
49
+ configures only `setDefaultStore(store)` and publishing reaches it through the purpose fallback, with
50
+ no extra configuration and no error.
51
+
52
+ ### Behaviour
53
+ - Object keys are `releases/<appVersion>-<revision>/<path>`, so releases no longer overwrite each
54
+ other. `publish.releasePrefix` in `linked.config.js` changes the base. A manifest may only upload
55
+ to its own prefix: each `objectKey` is recomputed from the prefix and source path before any
56
+ write, so an edited or foreign manifest cannot overwrite a different release, and source paths
57
+ that leave the app root (`../`, absolute, or through a symlink) are refused.
58
+ - The client bundle is built with `base = <accessURL>/<releasePrefix>/public/bundles/`, so the URLs
59
+ it emits for its own chunks and assets resolve under the release prefix on a plain static store
60
+ with no server rewrite. The value is recorded as `destination.baseURL`; the unused `publicRoot`
61
+ field is gone. The store is therefore resolved before Vite runs, and a release is built for one
62
+ store.
63
+ - The release revision comes from a clean `git rev-parse HEAD`. A dirty tree is refused (two builds
64
+ of the same uncommitted work would share a prefix and overwrite each other); `--allow-dirty`
65
+ builds as `<sha>-dirty`. `--revision <sha>`, `LINKED_RELEASE_REVISION` and `GITHUB_SHA` work
66
+ outside a Git checkout.
67
+ - Cache policy follows a file's origin, not its name: files in the Vite manifest are hashed by
68
+ construction and get `public, max-age=31536000, immutable`; declared static assets and the
69
+ release manifest get `public, max-age=60, must-revalidate`. The old filename heuristic gave
70
+ `og-image-1200x630.png` and `sw-v20260101.js` a year of immutable caching.
71
+ - `publish.staticAssets` globs are checked per match, not only per pattern, so brace expansion
72
+ (`public/{,../}secret/**`) and symlinks can no longer pull in files from outside `public/`.
73
+ - `APP_ENV` selects the Capacitor target only when it is literally `capacitor`. Any other value
74
+ (`production`, `staging`, …) builds web instead of silently producing an empty, non-publishable
75
+ manifest.
76
+ - Files are re-hashed locally against the manifest before upload. The post-upload check uses the
77
+ optional `IFileStore.statFile`; a store that lacks it, or reports no `sha256`, is warned about once
78
+ and skipped rather than failing. `etag` is never treated as a content hash.
79
+ - The manifest records the store's `accessURL` and publishing refuses a store that no longer matches.
80
+ - Objects are written with `preventDuplicates: false` and a store that reports a different location
81
+ than the key it was given fails the publish, rather than scattering renamed files.
82
+ - A failure partway through an upload names the file, the object key, the store and how many objects
83
+ were already uploaded, and says that re-running resumes the release.
84
+ - Standalone `publish-app` is a dry run unless `--yes`; only publishable `target: 'web'` manifests
85
+ upload; the manifest is read before a store is resolved, so a Capacitor manifest fails with a
86
+ manifest error rather than a storage error; nothing is ever deleted remotely; credentials are
87
+ redacted from publisher errors.
88
+ - `build-app`, `publish-app` and `serve-app` print a single error and exit 1 instead of surfacing an
89
+ unhandled rejection, and `--target`/`--publish`/`--revision`/`--allow-dirty` are rejected on the
90
+ legacy webpack path instead of being ignored.
91
+
92
+ ### New exports
93
+
94
+ From `@_linked/cli`: `buildViteApp`, `hasViteConfig`, `publishApp`, `serveCompiledApp`,
95
+ `validateCompiledAppArtifacts`, `createReleaseManifest`, `serializeReleaseManifest`,
96
+ `writeReleaseManifest`, `buildReleasePrefix`, `releaseObjectKey`, `getCacheControl`,
97
+ `planReleasePublish`, `publishRelease`, `loadReleaseManifest`, `resolveAppAssetsStore`,
98
+ `normalizeAccessURL`, `resolveBuildTarget`, `resolveDeclaredStaticAssets`, `resolveReleaseIdentity`,
99
+ `resolveSourceRevision`, `releaseBaseURL`, `VITE_OUTPUT_DIR`, the path helpers, and the release
100
+ types.
101
+
102
+ Apps must build with `build.outDir: 'public/bundles'` and `build.manifest: true` (both already set
103
+ by `createViteConfig`) and must not set `base` themselves; see the README.
104
+
105
+ Requires `@_linked/core` 2.20.1 for the purpose-named file stores and `IFileStore.statFile`.
106
+
3
107
  ## 1.18.0
4
108
 
5
109
  ### Minor Changes
package/README.md CHANGED
@@ -34,6 +34,8 @@ linked create-component <name> # add a React component file
34
34
  ```bash
35
35
  linked build # build the current package (tsc + checks)
36
36
  linked build-app # build frontend + backend for the current app
37
+ linked build-app --target web # web release: build and write public/bundles/linked-release.json
38
+ linked build-app --target capacitor # Capacitor/local native build: local manifest only, never published
37
39
  linked build-workspace # build all linked packages in the workspace in dependency order
38
40
  linked build-updated # incremental: only packages that changed since last build
39
41
  linked build-package <file> # walk up from a file path to find its package and rebuild
@@ -43,6 +45,123 @@ linked build-package <file> # walk up from a file path to find its package
43
45
  in a package without `"linkedPackage": true` (it used to exit 0 there). A build that finishes with warnings still
44
46
  exits 0.
45
47
 
48
+ ### App release / production runtime
49
+
50
+ ```bash
51
+ linked build-app # build and write the release manifest (never uploads)
52
+ linked build-app --publish # ...and upload it in the same run
53
+ linked build-app --revision <sha> # identify the release without asking git
54
+ linked build-app --allow-dirty # allow uncommitted changes; revision gets a "-dirty" suffix
55
+ linked publish-app # dry-run the release manifest upload (pass --yes to write)
56
+ linked publish-app --yes # upload the release described by the manifest
57
+ linked publish-app --manifest <p> # publish a manifest other than the default path
58
+ linked serve-app # run the compiled backend without Vite/HMR (production runtime)
59
+ ```
60
+
61
+ **Required Vite config.** The release flow reads `vite build` output directly, and the paths are
62
+ hardcoded: the client bundle must land in `public/bundles` and Vite must write its manifest.
63
+ `createViteConfig()` from `@_linked/cli/vite` already sets both; a hand-written `vite.config.ts`
64
+ needs them explicitly:
65
+
66
+ ```ts
67
+ export default defineConfig({
68
+ build: {
69
+ outDir: 'public/bundles', // release object keys are <prefix>/public/bundles/...
70
+ manifest: true, // build-app reads public/bundles/.vite/manifest.json
71
+ },
72
+ });
73
+ ```
74
+
75
+ Without `manifest: true` the build fails with a missing `public/bundles/.vite/manifest.json`; with a
76
+ different `outDir` nothing is found to publish. Do not set `base` yourself — `build-app` passes it
77
+ (see "How a release is served").
78
+
79
+ **Building is not publishing.** `build-app` compiles the app and writes
80
+ `public/bundles/linked-release.json`, a manifest listing every file of the release with its sha256,
81
+ size, content type and cache policy. `publish-app` uploads exactly the files in that manifest —
82
+ nothing else in `public/` is touched. Pass `--publish` to `build-app` only when you want the two
83
+ chained in one command. `publish-app` is a dry run unless `--yes` is given, and `--manifest <path>`
84
+ publishes a manifest from somewhere other than `public/bundles/linked-release.json` (the path is
85
+ relative to the app root and must stay inside it).
86
+
87
+ **Where it publishes.** Uploads go to the store returned by
88
+ `LinkedFileStorage.getStore(FileStorePurposes.appAssets)`. Configure a dedicated bundle store with
89
+ `LinkedFileStorage.setStore(FileStorePurposes.appAssets, store)`; an app that configures only
90
+ `setDefaultStore(store)` publishes to that store through the purpose fallback, which needs no extra
91
+ configuration.
92
+
93
+ **Every release gets its own prefix.** Object keys are
94
+ `releases/<appVersion>-<revision>/<path>`, so publishing a new release cannot overwrite the
95
+ previous one and a rollback means pointing at an older prefix. Set `publish.releasePrefix` in
96
+ `linked.config.js` to change the `releases` base. A manifest may only upload to its own prefix: every
97
+ `objectKey` is recomputed from the prefix and the source path before anything is written, so an
98
+ edited or foreign manifest cannot reach into another release.
99
+
100
+ **How a release is served.** Because every release has its own key prefix, the bundle's own URLs have
101
+ to carry that prefix — nothing rewrites them at request time. `build-app` therefore resolves the
102
+ store and the release prefix *before* running Vite and passes
103
+
104
+ ```
105
+ base = <store accessURL>/<releasePrefix>/public/bundles/
106
+ ```
107
+
108
+ so the chunk and asset URLs the bundle emits are absolute URLs into the release prefix. This works on
109
+ a plain static store or CDN with no rewrite rules, and two releases can be served side by side. The
110
+ value is recorded as `destination.baseURL` in the release manifest.
111
+
112
+ The HTML entry tags are not part of the bundle — `@_linked/server` renders them as
113
+ `${STATIC_ACCESS_URL}/public/bundles/<file>`. So serving a particular release is one deployment
114
+ variable, which `build-app` prints when it finishes:
115
+
116
+ ```
117
+ STATIC_ACCESS_URL=<store accessURL>/releases/<appVersion>-<revision>
118
+ ```
119
+
120
+ Set that on the app server (or roll back by setting it to an older release prefix) and the entry
121
+ tags and everything the bundle loads resolve to the same release. Note the consequence of baking the
122
+ base in: a release is built for one store, and pointing it at another means rebuilding.
123
+
124
+ **Which revision identifies a release.** By default the short `git rev-parse HEAD` of a **clean**
125
+ tree. A dirty tree is refused, because two builds of the same uncommitted work would share a release
126
+ ID and prefix and silently overwrite each other while removed files linger; pass `--allow-dirty` to
127
+ build anyway as `<sha>-dirty`. Outside a Git checkout (an exported tarball, some CI images) pass
128
+ `--revision <sha>`, or set `LINKED_RELEASE_REVISION` or `GITHUB_SHA`; those win over Git and skip
129
+ the clean-tree check.
130
+
131
+ **Verification.** Files are re-hashed from disk and compared against the manifest immediately before
132
+ upload, so a build that changed since the manifest was written is refused. After upload the store is
133
+ asked for `statFile`; that method is optional and may report no `sha256`, in which case the remote
134
+ check is skipped with a single warning rather than a failure. An `etag` is never used as a content
135
+ hash. The manifest also records the store's `accessURL`, and publishing refuses to run against a
136
+ store that now writes somewhere else. A failure partway through names the file, the object key, the
137
+ store and how many objects were already uploaded; the manifest is uploaded last, so an incomplete
138
+ release is never marked complete, and re-running `publish-app` resumes it.
139
+
140
+ **The store must keep keys verbatim.** A release object has to be stored under exactly the key it
141
+ was given, or the URLs baked into the bundle point at nothing, so publishing passes
142
+ `preventDuplicates: false` and fails with a clear message if the store reports a different location.
143
+ `@_linked/server`'s `LocalFileStore` lowercases the keys it is handed, so an app publishing to it
144
+ needs lowercase asset filenames (`rollupOptions.output.hashCharacters: 'hex'` in `vite.config`).
145
+
146
+ **Cache policy follows the origin, not the filename.** Files listed in the Vite manifest are
147
+ content-hashed by construction and get `public, max-age=31536000, immutable`. Everything else —
148
+ declared `publish.staticAssets` and the release manifest — gets
149
+ `public, max-age=60, must-revalidate`. (Guessing from the filename was tried and dropped:
150
+ `og-image-1200x630.png` and `sw-v20260101.js` read as hashed, and a year of immutable caching on
151
+ those cannot be undone without renaming the file.)
152
+
153
+ **Extra static assets.** `publish.staticAssets` in `linked.config.js` lists globs of files under
154
+ `public/` to ship besides the bundle. Both the pattern and every match must resolve inside
155
+ `public/`, so brace expansion and symlinks cannot pull in files from elsewhere in the repo.
156
+
157
+ **Capacitor.** `--target capacitor` (or `APP_ENV=capacitor`, matched literally) writes a local,
158
+ non-publishable manifest for inspection. Its assets ship inside the native app, so no file store is
159
+ resolved, no `base` is set and nothing is uploaded, even with `--publish`. Any other `APP_ENV` value
160
+ (`production`, `staging`, …) is ignored by the target choice and builds web.
161
+
162
+ Use `linked start` for development; use `linked serve-app` after `linked build-app` for
163
+ staging/production.
164
+
46
165
  ### Publishing / release
47
166
 
48
167
  ```bash
@@ -0,0 +1,23 @@
1
+ import type { IFileStore } from '@_linked/core/interfaces/IFileStore';
2
+ export interface ResolveAppAssetsStoreDependencies {
3
+ /**
4
+ * Loads the app's storage bootstrap file. Importing it is what registers the
5
+ * stores on `LinkedFileStorage`; the module's own exports are not inspected.
6
+ */
7
+ loadStorageConfig?: () => Promise<unknown>;
8
+ /** Injection point for tests. Defaults to `LinkedFileStorage.getStore`. */
9
+ getStore?: (purpose: string) => IFileStore;
10
+ }
11
+ /**
12
+ * Resolve the store that release artifacts are published to.
13
+ *
14
+ * Apps that dedicate a CDN bucket to bundles configure
15
+ * `LinkedFileStorage.setStore(FileStorePurposes.appAssets, …)`. Apps with a
16
+ * single store configure only a default store and `getStore` falls back to it
17
+ * — that is a supported setup, not an error, so there is no check for a
18
+ * dedicated store here.
19
+ */
20
+ export declare const resolveAppAssetsStore: (dependencies?: ResolveAppAssetsStoreDependencies) => Promise<IFileStore>;
21
+ /** `accessURL` values are compared ignoring a trailing slash. */
22
+ export declare const normalizeAccessURL: (accessURL: string) => string;
23
+ //# sourceMappingURL=app-assets-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-assets-store.d.ts","sourceRoot":"","sources":["../../../src/app-release/app-assets-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qCAAqC,CAAC;AAEpE,MAAM,WAAW,iCAAiC;IAChD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,CAAC;CAC5C;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,eAAc,iCAAsC,KACnD,OAAO,CAAC,UAAU,CAapB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,KAAG,MACT,CAAC"}
@@ -0,0 +1,33 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { FileStorePurposes, LinkedFileStorage, } from '@_linked/core/utils/LinkedFileStorage';
11
+ /**
12
+ * Resolve the store that release artifacts are published to.
13
+ *
14
+ * Apps that dedicate a CDN bucket to bundles configure
15
+ * `LinkedFileStorage.setStore(FileStorePurposes.appAssets, …)`. Apps with a
16
+ * single store configure only a default store and `getStore` falls back to it
17
+ * — that is a supported setup, not an error, so there is no check for a
18
+ * dedicated store here.
19
+ */
20
+ export const resolveAppAssetsStore = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (dependencies = {}) {
21
+ const loadStorageConfig = dependencies.loadStorageConfig ||
22
+ (() => __awaiter(void 0, void 0, void 0, function* () {
23
+ const { loadBackendStorageConfig } = yield import('../lifecycle.js');
24
+ return loadBackendStorageConfig();
25
+ }));
26
+ yield loadStorageConfig();
27
+ const getStore = dependencies.getStore ||
28
+ ((purpose) => LinkedFileStorage.getStore(purpose));
29
+ return getStore(FileStorePurposes.appAssets);
30
+ });
31
+ /** `accessURL` values are compared ignoring a trailing slash. */
32
+ export const normalizeAccessURL = (accessURL) => (accessURL || '').trim().replace(/\/+$/, '');
33
+ //# sourceMappingURL=app-assets-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-assets-store.js","sourceRoot":"","sources":["../../../src/app-release/app-assets-store.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,uCAAuC,CAAC;AAa/C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,YAEd,EAAE,mDADvB,eAAkD,EAAE;IAEpD,MAAM,iBAAiB,GACrB,YAAY,CAAC,iBAAiB;QAC9B,CAAC,GAAS,EAAE;YACV,MAAM,EAAC,wBAAwB,EAAC,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;YACnE,OAAO,wBAAwB,EAAE,CAAC;QACpC,CAAC,CAAA,CAAC,CAAC;IACL,MAAM,iBAAiB,EAAE,CAAC;IAE1B,MAAM,QAAQ,GACZ,YAAY,CAAC,QAAQ;QACrB,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,OAAO,QAAQ,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;AAC/C,CAAC,CAAA,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAU,EAAE,CAC9D,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,114 @@
1
+ import type { AppBuildTarget, LinkedAppReleaseManifest } from './types.js';
2
+ export interface CreateReleaseManifestOptions {
3
+ appRoot: string;
4
+ environmentNames: string[];
5
+ target: AppBuildTarget;
6
+ /**
7
+ * `accessURL` of the store this release will be published to. Omitted (or
8
+ * null) for a Capacitor build, which produces a local, non-publishable
9
+ * manifest and never touches a file store.
10
+ */
11
+ accessURL?: string | null;
12
+ staticAssets?: string[];
13
+ /** Base key releases are nested under. Defaults to `releases`. */
14
+ releasePrefix?: string;
15
+ builtAt?: string;
16
+ sourceRevision?: string;
17
+ /** Allow a revision derived from a working tree with uncommitted changes. */
18
+ allowDirty?: boolean;
19
+ /**
20
+ * Public URL every asset of this release is served from — the release
21
+ * prefix resolved against the store's `accessURL`, and the value the client
22
+ * bundle was built with as Vite's `base`. Empty for a Capacitor build.
23
+ */
24
+ baseURL?: string;
25
+ }
26
+ export declare const DEFAULT_RELEASE_PREFIX = "releases";
27
+ export declare const MANIFEST_FILE_NAME = "linked-release.json";
28
+ export declare const MANIFEST_SOURCE_PATH = "public/bundles/linked-release.json";
29
+ export declare const MANIFEST_CONTENT_TYPE = "application/json; charset=utf-8";
30
+ /**
31
+ * Cache-Control rule, decided by where a file came from rather than by what it
32
+ * is called.
33
+ *
34
+ * Files listed in the Vite manifest are content-hashed by construction — Vite
35
+ * emitted the name, so the bytes behind that key can never change and the
36
+ * object is immutable for a year. Everything else (declared static assets, the
37
+ * release manifest) is revalidated after a minute, so a re-published release is
38
+ * picked up quickly. Guessing "is this hashed?" from the filename was tried and
39
+ * removed: `og-image-1200x630.png`, `sw-v20260101.js` and `icon-FacebookRound.svg`
40
+ * all read as hashed, and a year of immutable caching on those cannot be undone
41
+ * without renaming the file.
42
+ */
43
+ export declare const IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
44
+ export declare const ENTRY_CACHE_CONTROL = "public, max-age=60, must-revalidate";
45
+ /** Where a release file came from; this is what decides its cache policy. */
46
+ export type ReleaseFileOrigin = 'vite-bundle' | 'static-asset';
47
+ export declare const getCacheControl: (origin: ReleaseFileOrigin) => string;
48
+ export interface ResolveSourceRevisionOptions {
49
+ appRoot: string;
50
+ /** `--revision`, or a caller-supplied value. Used verbatim when given. */
51
+ revision?: string;
52
+ /** `--allow-dirty`: build from a dirty tree, tagging the revision `-dirty`. */
53
+ allowDirty?: boolean;
54
+ env?: NodeJS.ProcessEnv;
55
+ }
56
+ /**
57
+ * Determine the revision that identifies this release.
58
+ *
59
+ * An explicit `--revision`, then `LINKED_RELEASE_REVISION`, then `GITHUB_SHA`
60
+ * win over Git, so a release can be built from an exported tarball or a CI
61
+ * checkout that is not a working repository. Falling back to `git rev-parse`
62
+ * requires a clean tree: two builds of the same uncommitted work would
63
+ * otherwise share a release ID and a prefix, silently overwriting each other
64
+ * while files removed in between linger under the prefix.
65
+ */
66
+ export declare const resolveSourceRevision: ({ appRoot, revision, allowDirty, env, }: ResolveSourceRevisionOptions) => string;
67
+ /**
68
+ * Build the key prefix for one release. Every object of a release lives under
69
+ * it, so publishing a new release cannot overwrite the previous one and a
70
+ * rollback is a matter of pointing at the older prefix.
71
+ */
72
+ export declare const buildReleasePrefix: (releaseId: string, basePrefix?: string) => string;
73
+ export declare const releaseObjectKey: (releasePrefix: string, sourcePath: string) => string;
74
+ /** Directory `vite build` writes the client bundle to. See the README. */
75
+ export declare const VITE_OUTPUT_DIR = "public/bundles";
76
+ /**
77
+ * Public URL the client bundle of a release is served from. Object keys are
78
+ * `<releasePrefix>/public/bundles/…` under the store's `accessURL`, and this is
79
+ * the value the build passes to Vite as `base`, so the root-absolute URLs the
80
+ * bundle emits for its own chunks and assets resolve under the release prefix
81
+ * without any server rewrite.
82
+ */
83
+ export declare const releaseBaseURL: (accessURL: string, releasePrefix: string) => string;
84
+ /**
85
+ * The release's root URL: `accessURL` plus the release prefix, without the
86
+ * bundle directory. `@_linked/server` builds the HTML entry tags as
87
+ * `${STATIC_ACCESS_URL}/public/bundles/<file>`, so pointing `STATIC_ACCESS_URL`
88
+ * at this value is what makes a running app serve one particular release —
89
+ * a value to deploy, not server code to write or a rewrite rule to configure.
90
+ */
91
+ export declare const releaseStaticAccessURL: (accessURL: string, releasePrefix: string) => string;
92
+ export interface ReleaseIdentity {
93
+ appName: string;
94
+ appVersion: string;
95
+ sourceRevision: string;
96
+ releaseId: string;
97
+ releasePrefix: string;
98
+ }
99
+ /**
100
+ * Work out the release ID and prefix before anything is built. The client
101
+ * bundle has to be built with the release prefix as its `base`, so the prefix
102
+ * cannot wait until the manifest is written.
103
+ */
104
+ export declare const resolveReleaseIdentity: (options: {
105
+ appRoot: string;
106
+ releasePrefix?: string;
107
+ sourceRevision?: string;
108
+ allowDirty?: boolean;
109
+ env?: NodeJS.ProcessEnv;
110
+ }) => ReleaseIdentity;
111
+ export declare const createReleaseManifest: (options: CreateReleaseManifestOptions) => LinkedAppReleaseManifest;
112
+ export declare const serializeReleaseManifest: (manifest: LinkedAppReleaseManifest) => string;
113
+ export declare const writeReleaseManifest: (appRoot: string, manifest: LinkedAppReleaseManifest) => string;
114
+ //# sourceMappingURL=create-release-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-release-manifest.d.ts","sourceRoot":"","sources":["../../../src/app-release/create-release-manifest.ts"],"names":[],"mappings":"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"}