@_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
@@ -0,0 +1,238 @@
1
+ import crypto from 'crypto';
2
+ import { execFileSync } from 'child_process';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { joinObjectKey, normalizeReleasePath, resolveExistingPathWithinRoot, } from './paths.js';
6
+ import { resolveDeclaredStaticAssets } from './static-assets.js';
7
+ const MIME_TYPES = {
8
+ '.css': 'text/css; charset=utf-8',
9
+ '.gif': 'image/gif',
10
+ '.html': 'text/html; charset=utf-8',
11
+ '.ico': 'image/x-icon',
12
+ '.jpeg': 'image/jpeg',
13
+ '.jpg': 'image/jpeg',
14
+ '.js': 'text/javascript; charset=utf-8',
15
+ '.json': 'application/json; charset=utf-8',
16
+ '.map': 'application/json; charset=utf-8',
17
+ '.png': 'image/png',
18
+ '.svg': 'image/svg+xml',
19
+ '.webp': 'image/webp',
20
+ '.woff': 'font/woff',
21
+ '.woff2': 'font/woff2',
22
+ };
23
+ export const DEFAULT_RELEASE_PREFIX = 'releases';
24
+ export const MANIFEST_FILE_NAME = 'linked-release.json';
25
+ export const MANIFEST_SOURCE_PATH = `public/bundles/${MANIFEST_FILE_NAME}`;
26
+ export const MANIFEST_CONTENT_TYPE = 'application/json; charset=utf-8';
27
+ /**
28
+ * Cache-Control rule, decided by where a file came from rather than by what it
29
+ * is called.
30
+ *
31
+ * Files listed in the Vite manifest are content-hashed by construction — Vite
32
+ * emitted the name, so the bytes behind that key can never change and the
33
+ * object is immutable for a year. Everything else (declared static assets, the
34
+ * release manifest) is revalidated after a minute, so a re-published release is
35
+ * picked up quickly. Guessing "is this hashed?" from the filename was tried and
36
+ * removed: `og-image-1200x630.png`, `sw-v20260101.js` and `icon-FacebookRound.svg`
37
+ * all read as hashed, and a year of immutable caching on those cannot be undone
38
+ * without renaming the file.
39
+ */
40
+ export const IMMUTABLE_CACHE_CONTROL = 'public, max-age=31536000, immutable';
41
+ export const ENTRY_CACHE_CONTROL = 'public, max-age=60, must-revalidate';
42
+ const getContentType = (filePath) => MIME_TYPES[path.extname(filePath).toLowerCase()] || 'application/octet-stream';
43
+ export const getCacheControl = (origin) => origin === 'vite-bundle' ? IMMUTABLE_CACHE_CONTROL : ENTRY_CACHE_CONTROL;
44
+ /** A revision has to be safe to embed in an object key and a URL. */
45
+ const SAFE_REVISION = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
46
+ const runGit = (appRoot, args) => execFileSync('git', args, {
47
+ cwd: appRoot,
48
+ encoding: 'utf8',
49
+ stdio: ['ignore', 'pipe', 'ignore'],
50
+ });
51
+ /**
52
+ * Determine the revision that identifies this release.
53
+ *
54
+ * An explicit `--revision`, then `LINKED_RELEASE_REVISION`, then `GITHUB_SHA`
55
+ * win over Git, so a release can be built from an exported tarball or a CI
56
+ * checkout that is not a working repository. Falling back to `git rev-parse`
57
+ * requires a clean tree: two builds of the same uncommitted work would
58
+ * otherwise share a release ID and a prefix, silently overwriting each other
59
+ * while files removed in between linger under the prefix.
60
+ */
61
+ export const resolveSourceRevision = ({ appRoot, revision, allowDirty, env = process.env, }) => {
62
+ const explicit = (revision ||
63
+ env.LINKED_RELEASE_REVISION ||
64
+ env.GITHUB_SHA ||
65
+ '').trim();
66
+ if (explicit) {
67
+ if (!SAFE_REVISION.test(explicit)) {
68
+ throw new Error(`Invalid release revision "${explicit}". Use letters, digits, ".", "-" or "_".`);
69
+ }
70
+ return explicit;
71
+ }
72
+ let head;
73
+ try {
74
+ head = runGit(appRoot, ['rev-parse', '--short=12', 'HEAD']).trim();
75
+ }
76
+ catch (_a) {
77
+ throw new Error('Cannot determine the Git revision for this release. Build from a Git checkout, ' +
78
+ 'or pass --revision <sha> (or set LINKED_RELEASE_REVISION / GITHUB_SHA).');
79
+ }
80
+ let dirty = false;
81
+ try {
82
+ dirty = runGit(appRoot, ['status', '--porcelain']).trim().length > 0;
83
+ }
84
+ catch (_b) {
85
+ // `rev-parse` worked, so this is a repository; treat an unreadable status
86
+ // as clean rather than blocking the build on a Git quirk.
87
+ }
88
+ if (!dirty)
89
+ return head;
90
+ if (!allowDirty) {
91
+ throw new Error(`The working tree has uncommitted changes, so release ${head} would not identify ` +
92
+ 'what was built: a second build of the same edits reuses the prefix and overwrites it. ' +
93
+ 'Commit or stash the changes, pass --allow-dirty to publish as ' +
94
+ `"${head}-dirty", or pass --revision <sha>.`);
95
+ }
96
+ return `${head}-dirty`;
97
+ };
98
+ const collectViteOutputPaths = (manifest) => {
99
+ const files = new Set();
100
+ for (const [entryName, entry] of Object.entries(manifest)) {
101
+ if (!entry || typeof entry.file !== 'string') {
102
+ throw new Error(`Invalid Vite manifest entry: ${entryName}`);
103
+ }
104
+ files.add(joinObjectKey('public/bundles', entry.file));
105
+ for (const relativePath of [...(entry.css || []), ...(entry.assets || [])]) {
106
+ files.add(joinObjectKey('public/bundles', relativePath));
107
+ }
108
+ for (const importedEntry of [
109
+ ...(entry.imports || []),
110
+ ...(entry.dynamicImports || []),
111
+ ]) {
112
+ if (!manifest[importedEntry]) {
113
+ throw new Error(`Vite manifest entry ${entryName} references missing import ${importedEntry}`);
114
+ }
115
+ }
116
+ }
117
+ return [...files];
118
+ };
119
+ /**
120
+ * Build the key prefix for one release. Every object of a release lives under
121
+ * it, so publishing a new release cannot overwrite the previous one and a
122
+ * rollback is a matter of pointing at the older prefix.
123
+ */
124
+ export const buildReleasePrefix = (releaseId, basePrefix = DEFAULT_RELEASE_PREFIX) => joinObjectKey(basePrefix, releaseId);
125
+ export const releaseObjectKey = (releasePrefix, sourcePath) => joinObjectKey(releasePrefix, sourcePath);
126
+ /** Directory `vite build` writes the client bundle to. See the README. */
127
+ export const VITE_OUTPUT_DIR = 'public/bundles';
128
+ /**
129
+ * Public URL the client bundle of a release is served from. Object keys are
130
+ * `<releasePrefix>/public/bundles/…` under the store's `accessURL`, and this is
131
+ * the value the build passes to Vite as `base`, so the root-absolute URLs the
132
+ * bundle emits for its own chunks and assets resolve under the release prefix
133
+ * without any server rewrite.
134
+ */
135
+ export const releaseBaseURL = (accessURL, releasePrefix) => `${releaseStaticAccessURL(accessURL, releasePrefix)}/${VITE_OUTPUT_DIR}/`;
136
+ /**
137
+ * The release's root URL: `accessURL` plus the release prefix, without the
138
+ * bundle directory. `@_linked/server` builds the HTML entry tags as
139
+ * `${STATIC_ACCESS_URL}/public/bundles/<file>`, so pointing `STATIC_ACCESS_URL`
140
+ * at this value is what makes a running app serve one particular release —
141
+ * a value to deploy, not server code to write or a rewrite rule to configure.
142
+ */
143
+ export const releaseStaticAccessURL = (accessURL, releasePrefix) => `${(accessURL || '').trim().replace(/\/+$/, '')}/${releasePrefix}`;
144
+ /**
145
+ * Work out the release ID and prefix before anything is built. The client
146
+ * bundle has to be built with the release prefix as its `base`, so the prefix
147
+ * cannot wait until the manifest is written.
148
+ */
149
+ export const resolveReleaseIdentity = (options) => {
150
+ const packageJson = JSON.parse(fs.readFileSync(path.join(options.appRoot, 'package.json'), 'utf8'));
151
+ const sourceRevision = resolveSourceRevision({
152
+ appRoot: options.appRoot,
153
+ revision: options.sourceRevision,
154
+ allowDirty: options.allowDirty,
155
+ env: options.env,
156
+ });
157
+ const releaseId = `${packageJson.version}-${sourceRevision}`;
158
+ return {
159
+ appName: packageJson.name,
160
+ appVersion: packageJson.version,
161
+ sourceRevision,
162
+ releaseId,
163
+ releasePrefix: buildReleasePrefix(releaseId, options.releasePrefix),
164
+ };
165
+ };
166
+ const describeReleaseFile = (appRoot, releasePrefix, sourcePath, origin) => {
167
+ const normalizedSourcePath = normalizeReleasePath(sourcePath, 'source path');
168
+ const absolutePath = resolveExistingPathWithinRoot(appRoot, normalizedSourcePath);
169
+ const content = fs.readFileSync(absolutePath);
170
+ return {
171
+ sourcePath: normalizedSourcePath,
172
+ objectKey: releaseObjectKey(releasePrefix, normalizedSourcePath),
173
+ sha256: crypto.createHash('sha256').update(content).digest('hex'),
174
+ size: content.byteLength,
175
+ contentType: getContentType(normalizedSourcePath),
176
+ cacheControl: getCacheControl(origin),
177
+ };
178
+ };
179
+ export const createReleaseManifest = (options) => {
180
+ var _a;
181
+ const identity = resolveReleaseIdentity({
182
+ appRoot: options.appRoot,
183
+ releasePrefix: options.releasePrefix,
184
+ sourceRevision: options.sourceRevision,
185
+ allowDirty: options.allowDirty,
186
+ });
187
+ const { releasePrefix } = identity;
188
+ // A Capacitor build ships its assets inside the native app, so it produces a
189
+ // manifest for local inspection only and is never uploaded. That is why it
190
+ // needs no destination and is marked unpublishable.
191
+ const publishable = options.target === 'web';
192
+ if (publishable && options.accessURL === undefined) {
193
+ throw new Error('A web release manifest requires the accessURL of the store it will be published to');
194
+ }
195
+ // Origin decides the cache policy: Vite named these files after their own
196
+ // content, so they are immutable; declared static assets keep their name
197
+ // across releases and must be revalidated.
198
+ const origins = new Map();
199
+ if (options.target === 'web') {
200
+ const viteManifestPath = path.join(options.appRoot, `${VITE_OUTPUT_DIR}/.vite/manifest.json`);
201
+ const viteManifest = JSON.parse(fs.readFileSync(viteManifestPath, 'utf8'));
202
+ for (const sourcePath of collectViteOutputPaths(viteManifest)) {
203
+ origins.set(sourcePath, 'vite-bundle');
204
+ }
205
+ }
206
+ for (const sourcePath of resolveDeclaredStaticAssets(options.appRoot, options.staticAssets)) {
207
+ if (!origins.has(sourcePath))
208
+ origins.set(sourcePath, 'static-asset');
209
+ }
210
+ const files = [...origins.entries()]
211
+ .map(([sourcePath, origin]) => describeReleaseFile(options.appRoot, releasePrefix, sourcePath, origin))
212
+ .sort((a, b) => a.objectKey.localeCompare(b.objectKey));
213
+ return {
214
+ schemaVersion: 1,
215
+ appName: identity.appName,
216
+ appVersion: identity.appVersion,
217
+ releaseId: identity.releaseId,
218
+ target: options.target,
219
+ publishable,
220
+ builtAt: options.builtAt || new Date().toISOString(),
221
+ environmentNames: [...options.environmentNames],
222
+ destination: {
223
+ accessURL: options.accessURL || '',
224
+ releasePrefix,
225
+ baseURL: (_a = options.baseURL) !== null && _a !== void 0 ? _a : (options.accessURL
226
+ ? releaseBaseURL(options.accessURL, releasePrefix)
227
+ : ''),
228
+ },
229
+ files,
230
+ };
231
+ };
232
+ export const serializeReleaseManifest = (manifest) => `${JSON.stringify(manifest, null, 2)}\n`;
233
+ export const writeReleaseManifest = (appRoot, manifest) => {
234
+ const outputPath = path.join(appRoot, MANIFEST_SOURCE_PATH);
235
+ fs.writeFileSync(outputPath, serializeReleaseManifest(manifest));
236
+ return outputPath;
237
+ };
238
+ //# sourceMappingURL=create-release-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-release-manifest.js","sourceRoot":"","sources":["../../../src/app-release/create-release-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare const normalizeReleasePath: (value: string, label?: string) => string;
2
+ export declare const joinObjectKey: (...segments: string[]) => string;
3
+ export declare const resolveExistingPathWithinRoot: (appRoot: string, relativePath: string) => string;
4
+ export declare const joinStaticAssetUrl: (publicBaseUrl: string, assetPath: string) => string;
5
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/app-release/paths.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,QAAO,MAAuB,KAC7B,MAiBF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,GAAG,UAAU,MAAM,EAAE,KAAG,MAMrD,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,SAAS,MAAM,EACf,cAAc,MAAM,KACnB,MAeF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,eAAe,MAAM,EACrB,WAAW,MAAM,KAChB,MASF,CAAC"}
@@ -0,0 +1,44 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ const WINDOWS_ABSOLUTE_PATH = /^[a-zA-Z]:[\\/]/;
4
+ export const normalizeReleasePath = (value, label = 'release path') => {
5
+ const slashPath = value.replace(/\\/g, '/').trim();
6
+ if (!slashPath || slashPath === '.') {
7
+ throw new Error(`${label} must not be empty`);
8
+ }
9
+ if (path.posix.isAbsolute(slashPath) || WINDOWS_ABSOLUTE_PATH.test(value)) {
10
+ throw new Error(`${label} must be relative: ${value}`);
11
+ }
12
+ const normalized = path.posix.normalize(slashPath).replace(/^\.\//, '');
13
+ if (normalized === '..' || normalized.startsWith('../')) {
14
+ throw new Error(`${label} must stay inside the application root: ${value}`);
15
+ }
16
+ return normalized;
17
+ };
18
+ export const joinObjectKey = (...segments) => {
19
+ if (segments.length === 0) {
20
+ throw new Error('object key must not be empty');
21
+ }
22
+ return normalizeReleasePath(segments.join('/'), 'object key');
23
+ };
24
+ export const resolveExistingPathWithinRoot = (appRoot, relativePath) => {
25
+ const normalized = normalizeReleasePath(relativePath, 'source path');
26
+ const rootRealPath = fs.realpathSync(appRoot);
27
+ const candidateRealPath = fs.realpathSync(path.resolve(rootRealPath, normalized));
28
+ const relativeToRoot = path.relative(rootRealPath, candidateRealPath);
29
+ if (relativeToRoot === '..' ||
30
+ relativeToRoot.startsWith(`..${path.sep}`) ||
31
+ path.isAbsolute(relativeToRoot)) {
32
+ throw new Error(`source path escapes the application root: ${relativePath}`);
33
+ }
34
+ return candidateRealPath;
35
+ };
36
+ export const joinStaticAssetUrl = (publicBaseUrl, assetPath) => {
37
+ const normalizedAssetPath = normalizeReleasePath(assetPath, 'asset path');
38
+ const normalizedBaseUrl = publicBaseUrl.trim().replace(/\/+$/, '');
39
+ if (!normalizedBaseUrl) {
40
+ throw new Error('public base URL must not be empty');
41
+ }
42
+ return `${normalizedBaseUrl}/${normalizedAssetPath}`;
43
+ };
44
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/app-release/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAEhD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,KAAa,EACb,QAAgB,cAAc,EACtB,EAAE;IACV,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAEnD,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,sBAAsB,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACxE,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,2CAA2C,KAAK,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAG,QAAkB,EAAU,EAAE;IAC7D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC,CAAC;AAChE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAC3C,OAAe,EACf,YAAoB,EACZ,EAAE;IACV,MAAM,UAAU,GAAG,oBAAoB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,iBAAiB,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IAClF,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAEtE,IACE,cAAc,KAAK,IAAI;QACvB,cAAc,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1C,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAC/B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,6CAA6C,YAAY,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,OAAO,iBAAiB,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,aAAqB,EACrB,SAAiB,EACT,EAAE;IACV,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC1E,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEnE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,GAAG,iBAAiB,IAAI,mBAAmB,EAAE,CAAC;AACvD,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { IFileStore } from '@_linked/core/interfaces/IFileStore';
2
+ import type { LinkedAppReleaseManifest, PublishPlan, PublishResult } from './types.js';
3
+ export interface PlanReleasePublishOptions {
4
+ appRoot: string;
5
+ manifestPath?: string;
6
+ store: IFileStore;
7
+ }
8
+ export interface LoadedReleaseManifest {
9
+ absolutePath: string;
10
+ manifest: LinkedAppReleaseManifest;
11
+ }
12
+ export interface PublishReleaseOptions extends PlanReleasePublishOptions {
13
+ yes?: boolean;
14
+ onProgress?: (progress: PublishProgress) => void;
15
+ onWarning?: (message: string) => void;
16
+ }
17
+ export interface PublishProgress {
18
+ completed: number;
19
+ total: number;
20
+ objectKey: string;
21
+ }
22
+ export declare const loadReleaseManifest: (appRoot: string, manifestPath?: string) => LoadedReleaseManifest;
23
+ export declare const planReleasePublish: (options: PlanReleasePublishOptions) => PublishPlan;
24
+ export declare const publishRelease: (options: PublishReleaseOptions) => Promise<PublishResult>;
25
+ //# sourceMappingURL=publisher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../../src/app-release/publisher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qCAAqC,CAAC;AASpE,OAAO,KAAK,EAEV,wBAAwB,EACxB,WAAW,EACX,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,wBAAwB,CAAC;CACpC;AAED,MAAM,WAAW,qBAAsB,SAAQ,yBAAyB;IACtE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAkCD,eAAO,MAAM,mBAAmB,GAC9B,SAAS,MAAM,EACf,qBAAmC,KAClC,qBA6BF,CAAC;AA2CF,eAAO,MAAM,kBAAkB,GAC7B,SAAS,yBAAyB,KACjC,WAsBF,CAAC;AAkCF,eAAO,MAAM,cAAc,GACzB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,aAAa,CAgJvB,CAAC"}
@@ -0,0 +1,238 @@
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 crypto from 'crypto';
11
+ import fs from 'fs';
12
+ import { normalizeAccessURL } from './app-assets-store.js';
13
+ import { MANIFEST_CONTENT_TYPE, MANIFEST_SOURCE_PATH, ENTRY_CACHE_CONTROL, releaseObjectKey, } from './create-release-manifest.js';
14
+ import { normalizeReleasePath, resolveExistingPathWithinRoot } from './paths.js';
15
+ /**
16
+ * Every object of a release must land under that release's own prefix.
17
+ *
18
+ * The manifest is a plain file on disk: a hand-edited or foreign one can name
19
+ * any `objectKey` it likes, and nothing else catches it — the `accessURL` check
20
+ * only proves the store is the right store, and the stored-location check only
21
+ * proves the store kept the key it was handed. So the key is not merely checked
22
+ * for shape, it is recomputed: it has to be exactly what this release prefix and
23
+ * this source path produce, or a release could overwrite a *different* release.
24
+ */
25
+ const validateObjectKey = (releasePrefix, file) => {
26
+ var _a, _b;
27
+ const sourcePath = normalizeReleasePath(String((_a = file.sourcePath) !== null && _a !== void 0 ? _a : ''), 'source path');
28
+ const objectKey = normalizeReleasePath(String((_b = file.objectKey) !== null && _b !== void 0 ? _b : ''), 'object key');
29
+ const expected = releaseObjectKey(releasePrefix, sourcePath);
30
+ if (objectKey !== expected) {
31
+ throw new Error(`Release manifest object key is not under the release prefix: ` +
32
+ `"${file.objectKey}" (expected "${expected}"). Rebuild the release; ` +
33
+ 'a manifest may only upload to its own prefix.');
34
+ }
35
+ };
36
+ export const loadReleaseManifest = (appRoot, manifestPath = MANIFEST_SOURCE_PATH) => {
37
+ var _a;
38
+ const absolutePath = resolveExistingPathWithinRoot(appRoot, manifestPath);
39
+ const manifest = JSON.parse(fs.readFileSync(absolutePath, 'utf8'));
40
+ if (manifest.schemaVersion !== 1) {
41
+ throw new Error(`Unsupported release manifest schema: ${manifest.schemaVersion}`);
42
+ }
43
+ if (manifest.target !== 'web' || manifest.publishable !== true) {
44
+ throw new Error('Only publishable web release manifests may be uploaded');
45
+ }
46
+ if (!Array.isArray(manifest.files) || manifest.files.length === 0) {
47
+ throw new Error('Release manifest contains no files');
48
+ }
49
+ if (!((_a = manifest.destination) === null || _a === void 0 ? void 0 : _a.releasePrefix)) {
50
+ throw new Error('Release manifest has no release prefix');
51
+ }
52
+ const releasePrefix = normalizeReleasePath(manifest.destination.releasePrefix, 'release prefix');
53
+ for (const file of manifest.files) {
54
+ validateObjectKey(releasePrefix, file);
55
+ }
56
+ return { absolutePath, manifest };
57
+ };
58
+ /**
59
+ * The manifest records the `accessURL` of the store it was built for. If the
60
+ * configured store now writes somewhere else, the release is not the one that
61
+ * was built and publishing stops. Trailing slashes are not significant.
62
+ */
63
+ const validateDestination = (manifest, store) => {
64
+ const expected = normalizeAccessURL(manifest.destination.accessURL);
65
+ const actual = normalizeAccessURL(store.accessURL);
66
+ if (expected !== actual) {
67
+ throw new Error(`Release destination does not match the manifest (manifest was built for ${expected || '(empty)'}, the configured appAssets store writes to ${actual || '(empty)'})`);
68
+ }
69
+ };
70
+ const hashBuffer = (content) => crypto.createHash('sha256').update(content).digest('hex');
71
+ /**
72
+ * Re-hash the file on disk before uploading. This needs no support from the
73
+ * store and is what actually guarantees the bytes match the manifest.
74
+ */
75
+ const validateLocalFile = (appRoot, file) => {
76
+ // `resolveExistingPathWithinRoot` resolves symlinks, so a source path that
77
+ // points out of the app root through one is refused here too.
78
+ const absolutePath = resolveExistingPathWithinRoot(appRoot, file.sourcePath);
79
+ const content = fs.readFileSync(absolutePath);
80
+ if (content.byteLength !== file.size || hashBuffer(content) !== file.sha256) {
81
+ throw new Error(`Release artifact changed after build: ${file.sourcePath}`);
82
+ }
83
+ return absolutePath;
84
+ };
85
+ export const planReleasePublish = (options) => {
86
+ const { absolutePath, manifest } = loadReleaseManifest(options.appRoot, options.manifestPath);
87
+ validateDestination(manifest, options.store);
88
+ for (const file of manifest.files) {
89
+ validateLocalFile(options.appRoot, file);
90
+ }
91
+ return {
92
+ releaseId: manifest.releaseId,
93
+ manifestPath: absolutePath,
94
+ manifestObjectKey: releaseObjectKey(manifest.destination.releasePrefix, MANIFEST_SOURCE_PATH),
95
+ destination: manifest.destination,
96
+ files: manifest.files,
97
+ totalBytes: manifest.files.reduce((total, file) => total + file.size, 0),
98
+ };
99
+ };
100
+ /**
101
+ * Verify one uploaded object against the hash in the manifest.
102
+ *
103
+ * `statFile` is optional on `IFileStore`, and a store that has it may still
104
+ * report no `sha256` (S3 only returns one when the object carries a checksum).
105
+ * Neither case is a failure: verification is skipped, the key is reported back
106
+ * as unverified, and the caller warns once. An `etag` is deliberately not used
107
+ * as a substitute — it is not a content hash.
108
+ */
109
+ const verifyUpload = (store, objectKey, expected) => __awaiter(void 0, void 0, void 0, function* () {
110
+ if (typeof store.statFile !== 'function')
111
+ return 'unsupported';
112
+ const stat = yield store.statFile(objectKey);
113
+ if (!stat) {
114
+ throw new Error(`Uploaded artifact is missing from the store: ${objectKey}`);
115
+ }
116
+ if (stat.size !== expected.size) {
117
+ throw new Error(`Uploaded artifact size does not match the manifest: ${objectKey}`);
118
+ }
119
+ if (!stat.sha256)
120
+ return 'no-hash';
121
+ if (stat.sha256 !== expected.sha256) {
122
+ throw new Error(`Uploaded artifact verification failed: ${objectKey}`);
123
+ }
124
+ return 'verified';
125
+ });
126
+ export const publishRelease = (options) => __awaiter(void 0, void 0, void 0, function* () {
127
+ var _a, _b;
128
+ const plan = planReleasePublish(options);
129
+ if (!options.yes) {
130
+ return {
131
+ releaseId: plan.releaseId,
132
+ uploadedFiles: 0,
133
+ uploadedBytes: 0,
134
+ dryRun: true,
135
+ unverified: [],
136
+ };
137
+ }
138
+ const unverified = [];
139
+ let warnedAboutVerification = false;
140
+ const noteUnverified = (objectKey, reason) => {
141
+ var _a;
142
+ unverified.push(objectKey);
143
+ if (warnedAboutVerification)
144
+ return;
145
+ warnedAboutVerification = true;
146
+ (_a = options.onWarning) === null || _a === void 0 ? void 0 : _a.call(options, `Cannot verify uploads against a store hash (${reason}). Files are still ` +
147
+ 'checked against the manifest before upload; the remote copy is not re-checked.');
148
+ };
149
+ const upload = (objectKey, content, contentType, cacheControl, sha256) => __awaiter(void 0, void 0, void 0, function* () {
150
+ // `preventDuplicates: false` is explicit: a release key must be exactly the
151
+ // key in the manifest. `LocalFileStore` otherwise appends a random suffix,
152
+ // which would both break the URL and make verification meaningless.
153
+ const storedURL = yield options.store.saveFile(objectKey, content, {
154
+ mimeType: contentType,
155
+ cacheControl,
156
+ preventDuplicates: false,
157
+ });
158
+ // A release key has to survive the round trip verbatim, or the published
159
+ // URLs in the bundle point at nothing. Some stores sanitise or suffix the
160
+ // path they are given; say so plainly instead of failing later with a
161
+ // confusing "missing from the store".
162
+ if (typeof storedURL === 'string' && !storedURL.endsWith(objectKey)) {
163
+ throw new Error(`The file store did not store the release object under its own key. ` +
164
+ `Expected a location ending in "${objectKey}", got "${storedURL}". ` +
165
+ 'A release store must keep object keys verbatim.');
166
+ }
167
+ const outcome = yield verifyUpload(options.store, objectKey, {
168
+ size: content.byteLength,
169
+ sha256,
170
+ });
171
+ if (outcome === 'unsupported') {
172
+ noteUnverified(objectKey, 'this store does not implement statFile');
173
+ }
174
+ else if (outcome === 'no-hash') {
175
+ noteUnverified(objectKey, 'the store reports no sha256 for stored objects');
176
+ }
177
+ });
178
+ const totalUploads = plan.files.length + 1;
179
+ let completedUploads = 0;
180
+ /**
181
+ * A failure halfway through leaves a partial prefix behind, so the message
182
+ * has to say which object failed, where it was going and how much is already
183
+ * there — and that a re-run finishes the job, since keys are deterministic
184
+ * and every object is overwritten with identical bytes.
185
+ */
186
+ const describeFailure = (error, what, objectKey) => {
187
+ const cause = error instanceof Error ? error : new Error(String(error));
188
+ const failure = new Error(`Release upload failed while uploading ${what} to "${objectKey}" ` +
189
+ `(store ${options.store.accessURL || '(no accessURL)'}): ${cause.message}. ` +
190
+ `${completedUploads} of ${totalUploads} objects were already uploaded under ` +
191
+ `"${plan.destination.releasePrefix}"; the release manifest was not uploaded, so the ` +
192
+ 'release is incomplete. Re-running publish-app resumes it — object keys are ' +
193
+ 'deterministic and already-uploaded objects are rewritten with identical bytes.');
194
+ failure.cause = cause;
195
+ failure.stack = cause.stack
196
+ ? `${failure.message}\nCaused by: ${cause.stack}`
197
+ : failure.stack;
198
+ return failure;
199
+ };
200
+ for (const file of plan.files) {
201
+ const absolutePath = resolveExistingPathWithinRoot(options.appRoot, file.sourcePath);
202
+ try {
203
+ yield upload(file.objectKey, fs.readFileSync(absolutePath), file.contentType, file.cacheControl, file.sha256);
204
+ }
205
+ catch (error) {
206
+ throw describeFailure(error, file.sourcePath, file.objectKey);
207
+ }
208
+ completedUploads += 1;
209
+ (_a = options.onProgress) === null || _a === void 0 ? void 0 : _a.call(options, {
210
+ completed: completedUploads,
211
+ total: totalUploads,
212
+ objectKey: file.objectKey,
213
+ });
214
+ }
215
+ // The manifest goes last: its presence under the release prefix is what marks
216
+ // the release as complete.
217
+ const manifestBody = fs.readFileSync(plan.manifestPath);
218
+ try {
219
+ yield upload(plan.manifestObjectKey, manifestBody, MANIFEST_CONTENT_TYPE, ENTRY_CACHE_CONTROL, hashBuffer(manifestBody));
220
+ }
221
+ catch (error) {
222
+ throw describeFailure(error, 'the release manifest', plan.manifestObjectKey);
223
+ }
224
+ completedUploads += 1;
225
+ (_b = options.onProgress) === null || _b === void 0 ? void 0 : _b.call(options, {
226
+ completed: completedUploads,
227
+ total: totalUploads,
228
+ objectKey: plan.manifestObjectKey,
229
+ });
230
+ return {
231
+ releaseId: plan.releaseId,
232
+ uploadedFiles: totalUploads,
233
+ uploadedBytes: plan.totalBytes + manifestBody.byteLength,
234
+ dryRun: false,
235
+ unverified,
236
+ };
237
+ });
238
+ //# sourceMappingURL=publisher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publisher.js","sourceRoot":"","sources":["../../../src/app-release/publisher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { AppBuildTarget } from './types.js';
2
+ export interface ResolveBuildTargetOptions {
3
+ target?: AppBuildTarget;
4
+ appEnv?: string;
5
+ }
6
+ /**
7
+ * Resolve the app build target.
8
+ *
9
+ * `APP_ENV` selects the Capacitor target only when it literally says
10
+ * `capacitor`. It used to be read as a boolean — any non-empty value meant
11
+ * "native" — which quietly hijacked the far more common `APP_ENV=production`
12
+ * into a near-empty, non-publishable manifest, and then made `--target web`
13
+ * fail outright. Nothing else in this monorepo reads `APP_ENV`, so there is no
14
+ * boolean contract left to preserve; an unrelated value is ignored and the
15
+ * build defaults to web, exactly as it does with `APP_ENV` unset. Pass
16
+ * `--target` to be explicit.
17
+ */
18
+ export declare const resolveBuildTarget: ({ target, appEnv, }?: ResolveBuildTargetOptions) => AppBuildTarget;
19
+ //# sourceMappingURL=resolve-build-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-build-target.d.ts","sourceRoot":"","sources":["../../../src/app-release/resolve-build-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAAI,sBAGhC,yBAA8B,KAAG,cAsBnC,CAAC"}