@bitmagic/asset-core 0.1.2 → 0.1.3
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.
- package/dist/publish-source.d.ts +260 -0
- package/dist/publish-source.d.ts.map +1 -0
- package/dist/publish-source.js +258 -0
- package/dist/publish-source.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What "the source of a published game" means, shared by the two sides that must agree on it.
|
|
3
|
+
*
|
|
4
|
+
* The CLI walks a creator's project to produce a fingerprint and a source archive; api-server
|
|
5
|
+
* re-derives the fingerprint from that archive's manifest to check the two describe the same
|
|
6
|
+
* tree. If the exclusion lists, the path normalisation or the fold ever differ between them, a
|
|
7
|
+
* perfectly honest publish reports a mismatch. So they live here, once.
|
|
8
|
+
*
|
|
9
|
+
* Import path matters, twice over:
|
|
10
|
+
*
|
|
11
|
+
* - Published as the `@bitmagic/asset-core/publish-source` subpath, following `./favicon`'s
|
|
12
|
+
* precedent, so consumers do not drag in the root barrel's zod/three/forger client. This is
|
|
13
|
+
* not cosmetic: `api-server/src/cli/cli-routes.ts` documents that eagerly importing the
|
|
14
|
+
* three.js graph into api-server's tests OOM-killed the CI worker. Nothing here may import
|
|
15
|
+
* from `./index.js`.
|
|
16
|
+
* - No zip library, no `fs`. Zip *format* handling stays in each consumer (the CLI writes an
|
|
17
|
+
* archive, api-server streams one apart) and only the pure logic is shared, which keeps this
|
|
18
|
+
* package dependency-clean and keeps the npm blast radius of a change small.
|
|
19
|
+
*
|
|
20
|
+
* The CLI ships to npm with this package pinned, while api-server runs from `main`, so the two
|
|
21
|
+
* routinely run DIFFERENT copies of this file. That is why the fingerprint is versioned — see
|
|
22
|
+
* `SOURCE_INDEX_ALGO`.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* The fingerprint algorithm's identity, sent by the CLI and dispatched on by the server.
|
|
26
|
+
*
|
|
27
|
+
* Without it, adding one entry to `EXCLUDED_FILES` — which has happened three times, each for a
|
|
28
|
+
* good reason documented below — would make every publish from an already-installed CLI report a
|
|
29
|
+
* fingerprint mismatch, because that CLI is still hashing the old file set. The server keeps every
|
|
30
|
+
* algorithm it has ever shipped and picks by this value; an unknown or absent one is reported as
|
|
31
|
+
* "cannot check", never as "mismatch". Bump it in lockstep with any change to the two sets below
|
|
32
|
+
* or to `fingerprintFromIndex`.
|
|
33
|
+
*/
|
|
34
|
+
export declare const SOURCE_INDEX_ALGO = "v1";
|
|
35
|
+
/**
|
|
36
|
+
* Directories whose contents never change what the bundle contains: our own artifacts
|
|
37
|
+
* (`.bitmagic/`, `dist/`), the dependency tree (`node_modules/` — pinned by the lockfile the
|
|
38
|
+
* scaffold writes), vite's cache, and git's object store. Including any of them would make the
|
|
39
|
+
* fingerprint change on something other than an edit, which would make the staleness gate reject
|
|
40
|
+
* a publish of an unchanged project.
|
|
41
|
+
*
|
|
42
|
+
* `.git` is the one that bit in practice. Hashing it means an EMPTY `git commit` — no source
|
|
43
|
+
* change at all — flips the fingerprint, because the commit writes new objects, a new ref value
|
|
44
|
+
* and a new reflog entry. That turns the ordinary `bitmagic verify` → `git commit` →
|
|
45
|
+
* `bitmagic publish` sequence into exit 3 ("The project has changed since it was last verified"),
|
|
46
|
+
* and `bitmagic upgrade` actively pushes creators into git by refusing a dirty tree. The
|
|
47
|
+
* scaffolded AGENTS.md's "do not edit any tracked file between verifying and publishing" cannot
|
|
48
|
+
* prevent it either: committing is not editing. It also made every publish rebuild, since the
|
|
49
|
+
* build manifest's own recorded fingerprint could never still match by the time publish ran.
|
|
50
|
+
*/
|
|
51
|
+
export declare const EXCLUDED_DIRS: ReadonlySet<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Files that can never change what the bundle contains, excluded at basename granularity.
|
|
54
|
+
*
|
|
55
|
+
* `.DS_Store`: on macOS, Finder writes it merely for opening the project folder, and no build tool
|
|
56
|
+
* ever reads it — so it is another way to flip the fingerprint without editing anything.
|
|
57
|
+
*
|
|
58
|
+
* `mechanics-plan.md`: the agent is told to rewrite it after every slice — moving shipped items
|
|
59
|
+
* out of the backlog and re-ranking the rest is the whole point of the file. Left in, the ordinary
|
|
60
|
+
* `bitmagic verify` → update the backlog → `bitmagic publish` sequence exits 3 over prose, which
|
|
61
|
+
* teaches agents to reach for `--force`, the one flag that also waves through a genuinely stale
|
|
62
|
+
* verify.
|
|
63
|
+
*
|
|
64
|
+
* `GAME-DESIGN.md`: same "no build tool reads it" test — nothing imports it,
|
|
65
|
+
* `vite.publish.config.js` cannot put prose in the bundle, and it exists to be REWRITTEN as the
|
|
66
|
+
* game evolves (it is the input `bitmagic cover` reads).
|
|
67
|
+
*
|
|
68
|
+
* The list is deliberately not a general ignore mechanism, because a false "fresh" ships an
|
|
69
|
+
* unverified bundle while a false "stale" only costs one `bitmagic verify`.
|
|
70
|
+
*
|
|
71
|
+
* NOTE the asymmetry with `SECRET_FILE_PATTERNS` below: these files are excluded from the
|
|
72
|
+
* FINGERPRINT but still travel in the source archive, because they are exactly what a human
|
|
73
|
+
* reading the archive wants. Secrets are the other way round.
|
|
74
|
+
*/
|
|
75
|
+
export declare const EXCLUDED_FILES: ReadonlySet<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Excluded from the fingerprint, but still carried in the source archive.
|
|
78
|
+
*
|
|
79
|
+
* The two sets answer different questions. The fingerprint asks "would changing this change the
|
|
80
|
+
* bundle?" — for a design document, no, which is why editing one must not invalidate a verify. The
|
|
81
|
+
* archive asks "would a human opening this want it?" — for a design document, emphatically yes: it
|
|
82
|
+
* is the single most useful file for understanding what the game was trying to be.
|
|
83
|
+
*
|
|
84
|
+
* `.DS_Store` is in neither, so it is deliberately absent here: nobody wants it and it is not
|
|
85
|
+
* evidence of anything.
|
|
86
|
+
*/
|
|
87
|
+
export declare const ARCHIVED_UNFINGERPRINTED_FILES: ReadonlySet<string>;
|
|
88
|
+
/**
|
|
89
|
+
* Files that must never travel inside the source archive, however the fingerprint treats them.
|
|
90
|
+
*
|
|
91
|
+
* The archive lands in the portal bucket, and that bucket carries a BUCKET-level
|
|
92
|
+
* `allUsers:objectViewer` grant (see `.github/workflows/README.md`) — every object in it is
|
|
93
|
+
* world-readable. A creator's project is their working directory, and working directories collect
|
|
94
|
+
* credentials. Shipping one to a public URL because nobody thought about it is how an incident
|
|
95
|
+
* starts.
|
|
96
|
+
*
|
|
97
|
+
* These files are still hashed and still listed in the manifest (path + hash, no content), so the
|
|
98
|
+
* server can reconcile the fingerprint without ever receiving the bytes. That is the whole reason
|
|
99
|
+
* the manifest is separate from the payload.
|
|
100
|
+
*
|
|
101
|
+
* Deliberately a deny-list of shapes rather than an allow-list of known-good paths: an allow-list
|
|
102
|
+
* would silently drop a creator's own source file the first time someone used an unusual layout,
|
|
103
|
+
* and dropping game code from the archive is a quieter failure than including a stray dotfile.
|
|
104
|
+
* The trade is accepted knowingly — this list will never be complete, which is why the archive's
|
|
105
|
+
* storage key is also unguessable rather than relying on this alone.
|
|
106
|
+
*/
|
|
107
|
+
export declare const SECRET_FILE_PATTERNS: readonly RegExp[];
|
|
108
|
+
/** Would including this path in the archive payload risk publishing a credential? */
|
|
109
|
+
export declare function isSecretPath(posixPath: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* A relative path as the fingerprint hashes it: always forward-slashed.
|
|
112
|
+
*
|
|
113
|
+
* `sep` is a parameter rather than a direct `path.sep` read so this is testable on any
|
|
114
|
+
* platform — the Windows behaviour that makes normalization necessary cannot otherwise be
|
|
115
|
+
* exercised on the POSIX runners CI uses, which is exactly how a vacuous test gets written.
|
|
116
|
+
*/
|
|
117
|
+
export declare function toPosixPath(relativePath: string, sep?: string): string;
|
|
118
|
+
/** One file as both sides see it: the project-relative posix path and the hash of its bytes. */
|
|
119
|
+
export interface SourceIndexEntry {
|
|
120
|
+
path: string;
|
|
121
|
+
sha256: string;
|
|
122
|
+
bytes: number;
|
|
123
|
+
/**
|
|
124
|
+
* For vendored engine files only: the same content hashed with line endings normalised, used
|
|
125
|
+
* exclusively to compare against the official release. See `engineComparisonHash` for why the
|
|
126
|
+
* raw `sha256` above cannot serve — in short, a Windows checkout rewrites LF to CRLF in the
|
|
127
|
+
* committed `engine/` tree and would otherwise report every engine file as modified.
|
|
128
|
+
*
|
|
129
|
+
* Computed here, by the side that actually has the bytes: the archive carries hashes rather than
|
|
130
|
+
* engine content, so the server can never normalise after the fact.
|
|
131
|
+
*/
|
|
132
|
+
engineSha256?: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Every fingerprinted file in a project, plus the fingerprint they roll up to.
|
|
136
|
+
*
|
|
137
|
+
* Produced once per publish by the CLI (which owns the filesystem walk) and reconstructed by
|
|
138
|
+
* api-server from the archive's manifest. `files` is sorted by path — `fingerprintFromIndex`
|
|
139
|
+
* depends on it and sorts defensively rather than trusting the caller.
|
|
140
|
+
*/
|
|
141
|
+
export interface SourceIndex {
|
|
142
|
+
algo: string;
|
|
143
|
+
files: SourceIndexEntry[];
|
|
144
|
+
fingerprint: string;
|
|
145
|
+
/**
|
|
146
|
+
* Files the archive carries but the fingerprint ignores — see
|
|
147
|
+
* `ARCHIVED_UNFINGERPRINTED_FILES`. Kept apart from `files` so they can never reach
|
|
148
|
+
* `fingerprintFromIndex`, which is the whole reason they were excluded in the first place.
|
|
149
|
+
*/
|
|
150
|
+
archivedOnly: SourceIndexEntry[];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The fingerprint: a hash of everything that determines the published bundle.
|
|
154
|
+
*
|
|
155
|
+
* This is what lets `publish` decide whether a `verify` is too stale to trust, by CONTENT
|
|
156
|
+
* rather than by clock: a timestamp policy would accept an edit made one second after
|
|
157
|
+
* verifying and reject an untouched project that merely sat overnight.
|
|
158
|
+
*
|
|
159
|
+
* The path is hashed alongside the content so that moving a file — which changes what the
|
|
160
|
+
* bundle imports — changes the fingerprint even though no byte of any file did.
|
|
161
|
+
*
|
|
162
|
+
* Kept deliberately coarse (every tracked file, not a module graph): a false "stale" costs one
|
|
163
|
+
* `bitmagic verify`, while a false "fresh" ships an unverified bundle.
|
|
164
|
+
*
|
|
165
|
+
* The exact byte sequence folded here is load-bearing across two independently-released
|
|
166
|
+
* packages. Do not "clean up" the trailing newline or the separator without bumping
|
|
167
|
+
* `SOURCE_INDEX_ALGO`.
|
|
168
|
+
*/
|
|
169
|
+
export declare function fingerprintFromIndex(files: readonly SourceIndexEntry[]): string;
|
|
170
|
+
/** Where the archive's manifest lives inside the zip. */
|
|
171
|
+
export declare const SOURCE_MANIFEST_NAME = "bitmagic-source.json";
|
|
172
|
+
/**
|
|
173
|
+
* The archive's manifest: enough for the server to re-derive the fingerprint and to check the
|
|
174
|
+
* vendored engine, without the archive having to carry either the engine or any secret.
|
|
175
|
+
*
|
|
176
|
+
* `omitted` lists the paths that are in `files` but deliberately absent from the payload, so a
|
|
177
|
+
* reader (human or server) can tell "not shipped, on purpose" from "the archive is truncated".
|
|
178
|
+
*
|
|
179
|
+
* Deliberately NO `publishVersion`. The archive has to be built and hashed BEFORE `publish/begin`
|
|
180
|
+
* — that hash is what `begin` bakes into the meta block — and the version is not allocated until
|
|
181
|
+
* `begin` returns. A field that cannot be known when the file is written cannot be in the file.
|
|
182
|
+
* The version is recorded where it is actually available: in the archive's storage key and in the
|
|
183
|
+
* meta block's own `bm:publish-version`.
|
|
184
|
+
*/
|
|
185
|
+
export interface SourceManifest {
|
|
186
|
+
algo: string;
|
|
187
|
+
gameId: string;
|
|
188
|
+
engineVersion: string;
|
|
189
|
+
genre: string;
|
|
190
|
+
exportedAt: string;
|
|
191
|
+
fingerprint: string;
|
|
192
|
+
files: Record<string, string>;
|
|
193
|
+
omitted: string[];
|
|
194
|
+
/**
|
|
195
|
+
* Line-ending-normalised hashes for the vendored engine paths only, keyed the same way as
|
|
196
|
+
* `files`. Separate from `files` because the two answer different questions: `files` must stay
|
|
197
|
+
* byte-exact for the fingerprint, while this exists to survive a platform's checkout rules.
|
|
198
|
+
*/
|
|
199
|
+
engineHashes: Record<string, string>;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Project-relative prefix of the vendored engine. `bitmagic init`/`upgrade` unpack the release
|
|
203
|
+
* tarball's `game/src/<root>` into `engine/<root>`, so every vendored file lives under this.
|
|
204
|
+
*/
|
|
205
|
+
export declare const VENDORED_ENGINE_PREFIX = "engine/";
|
|
206
|
+
/**
|
|
207
|
+
* Map a path as it appears in the engine release tarball to where it lands in a scaffolded
|
|
208
|
+
* project, or null when the tarball entry is not vendored into projects at all.
|
|
209
|
+
*
|
|
210
|
+
* The tarball stores three shapes at different depths (see `cli/src/scaffold/engine-download.ts`):
|
|
211
|
+
* `game/src/<root>/…` → `engine/<root>/…`, `game/agent-docs/…` → `engine/agent-docs/…`, and
|
|
212
|
+
* `game/sw-cache-buster.js` → `sw-cache-buster.js` at the project root. `templates/` ships in the
|
|
213
|
+
* tarball for the scaffolder to read and is never vendored, so it maps to null.
|
|
214
|
+
*
|
|
215
|
+
* Lives here rather than in api-server because it is a statement about what the CLI did to the
|
|
216
|
+
* tarball, and the CLI is the other consumer that has to agree.
|
|
217
|
+
*/
|
|
218
|
+
export declare function tarballPathToProjectPath(tarballPath: string): string | null;
|
|
219
|
+
/**
|
|
220
|
+
* Bytes every genuine published bundle carries, used to refuse a publish that is not an engine
|
|
221
|
+
* build at all.
|
|
222
|
+
*
|
|
223
|
+
* These survive the toolchain verbatim, which is the entire reason they were chosen over anything
|
|
224
|
+
* cleverer. The scaffold's `index.html` emits `<div id="game-container">` and a
|
|
225
|
+
* `<script type="importmap">` whose body Vite only RELOCATES (its import-map hook lifts the tag
|
|
226
|
+
* out and reinserts the matched text unchanged before the first module script) — so both land, byte
|
|
227
|
+
* for byte, a few KB into the single-file bundle, well inside the head range `complete` already
|
|
228
|
+
* reads for the meta block. Verified against a real `vite build` with `vite-plugin-singlefile`,
|
|
229
|
+
* not inferred.
|
|
230
|
+
*
|
|
231
|
+
* Why a SUBSET of the alias entries rather than all of them. A creator's installed CLI is an older
|
|
232
|
+
* npm release than the running server, so the server must only require markers that both agree on.
|
|
233
|
+
* The CDN entries are disqualified outright — they embed the pinned three.js version, which moves
|
|
234
|
+
* whenever the CLI bumps it. Of the local aliases these three are the ones that have to exist for
|
|
235
|
+
* a project to resolve its entry module at all, so requiring more would buy nothing and would turn
|
|
236
|
+
* any future alias change into a spurious refusal for every not-yet-upgraded project.
|
|
237
|
+
*
|
|
238
|
+
* What this is worth: it stops bitmagic.ai being used to host arbitrary HTML by someone who has an
|
|
239
|
+
* account and a game id. It is trivially copy-pasteable by anyone who looks, and is not claimed to
|
|
240
|
+
* be more than that.
|
|
241
|
+
*/
|
|
242
|
+
export declare const ENGINE_BUILD_MARKERS: readonly string[];
|
|
243
|
+
/** Does this bundle's head carry every marker a genuine engine build has? */
|
|
244
|
+
export declare function hasEngineBuildMarkers(head: string): boolean;
|
|
245
|
+
/**
|
|
246
|
+
* Hash used ONLY when comparing a vendored engine file against the official release.
|
|
247
|
+
*
|
|
248
|
+
* Line endings are normalised first, which the fingerprint deliberately never does. The reason is
|
|
249
|
+
* Windows: `renderGitignore()` does not ignore `engine/`, so the vendored tree is committed, and
|
|
250
|
+
* AGENTS.md pushes creators into git (`bitmagic upgrade` refuses a dirty tree). A Windows checkout
|
|
251
|
+
* with the default `core.autocrlf=true` rewrites LF to CRLF in every `.ts` file it restores — so
|
|
252
|
+
* without this, every Windows creator would see 100% of their engine files reported as modified,
|
|
253
|
+
* forever, on every publish. That is a false alarm about the one check most likely to be believed.
|
|
254
|
+
*
|
|
255
|
+
* Confined to this comparison on purpose. Normalising inside the fingerprint would instead make
|
|
256
|
+
* the fingerprint disagree with itself across platforms in the staleness gate, where a false
|
|
257
|
+
* "fresh" is the dangerous direction.
|
|
258
|
+
*/
|
|
259
|
+
export declare function engineComparisonHash(content: Uint8Array): string;
|
|
260
|
+
//# sourceMappingURL=publish-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-source.d.ts","sourceRoot":"","sources":["../src/publish-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAM5C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAI7C,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,EAAE,WAAW,CAAC,MAAM,CAG7D,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EASjD,CAAC;AAEF,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,GAAE,MAAY,GAAG,MAAM,CAE3E;AAED,gGAAgG;AAChG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,gBAAgB,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAO/E;AAED,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW3E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAIjD,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAMhE"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What "the source of a published game" means, shared by the two sides that must agree on it.
|
|
3
|
+
*
|
|
4
|
+
* The CLI walks a creator's project to produce a fingerprint and a source archive; api-server
|
|
5
|
+
* re-derives the fingerprint from that archive's manifest to check the two describe the same
|
|
6
|
+
* tree. If the exclusion lists, the path normalisation or the fold ever differ between them, a
|
|
7
|
+
* perfectly honest publish reports a mismatch. So they live here, once.
|
|
8
|
+
*
|
|
9
|
+
* Import path matters, twice over:
|
|
10
|
+
*
|
|
11
|
+
* - Published as the `@bitmagic/asset-core/publish-source` subpath, following `./favicon`'s
|
|
12
|
+
* precedent, so consumers do not drag in the root barrel's zod/three/forger client. This is
|
|
13
|
+
* not cosmetic: `api-server/src/cli/cli-routes.ts` documents that eagerly importing the
|
|
14
|
+
* three.js graph into api-server's tests OOM-killed the CI worker. Nothing here may import
|
|
15
|
+
* from `./index.js`.
|
|
16
|
+
* - No zip library, no `fs`. Zip *format* handling stays in each consumer (the CLI writes an
|
|
17
|
+
* archive, api-server streams one apart) and only the pure logic is shared, which keeps this
|
|
18
|
+
* package dependency-clean and keeps the npm blast radius of a change small.
|
|
19
|
+
*
|
|
20
|
+
* The CLI ships to npm with this package pinned, while api-server runs from `main`, so the two
|
|
21
|
+
* routinely run DIFFERENT copies of this file. That is why the fingerprint is versioned — see
|
|
22
|
+
* `SOURCE_INDEX_ALGO`.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from 'node:crypto';
|
|
25
|
+
// Imported rather than taken from the ambient global, matching `node:crypto` above: this file's
|
|
26
|
+
// eslint config enumerates its globals deliberately, and an explicit import says "node-only" as
|
|
27
|
+
// clearly as the crypto one does.
|
|
28
|
+
import { TextDecoder } from 'node:util';
|
|
29
|
+
/**
|
|
30
|
+
* The fingerprint algorithm's identity, sent by the CLI and dispatched on by the server.
|
|
31
|
+
*
|
|
32
|
+
* Without it, adding one entry to `EXCLUDED_FILES` — which has happened three times, each for a
|
|
33
|
+
* good reason documented below — would make every publish from an already-installed CLI report a
|
|
34
|
+
* fingerprint mismatch, because that CLI is still hashing the old file set. The server keeps every
|
|
35
|
+
* algorithm it has ever shipped and picks by this value; an unknown or absent one is reported as
|
|
36
|
+
* "cannot check", never as "mismatch". Bump it in lockstep with any change to the two sets below
|
|
37
|
+
* or to `fingerprintFromIndex`.
|
|
38
|
+
*/
|
|
39
|
+
export const SOURCE_INDEX_ALGO = 'v1';
|
|
40
|
+
/**
|
|
41
|
+
* Directories whose contents never change what the bundle contains: our own artifacts
|
|
42
|
+
* (`.bitmagic/`, `dist/`), the dependency tree (`node_modules/` — pinned by the lockfile the
|
|
43
|
+
* scaffold writes), vite's cache, and git's object store. Including any of them would make the
|
|
44
|
+
* fingerprint change on something other than an edit, which would make the staleness gate reject
|
|
45
|
+
* a publish of an unchanged project.
|
|
46
|
+
*
|
|
47
|
+
* `.git` is the one that bit in practice. Hashing it means an EMPTY `git commit` — no source
|
|
48
|
+
* change at all — flips the fingerprint, because the commit writes new objects, a new ref value
|
|
49
|
+
* and a new reflog entry. That turns the ordinary `bitmagic verify` → `git commit` →
|
|
50
|
+
* `bitmagic publish` sequence into exit 3 ("The project has changed since it was last verified"),
|
|
51
|
+
* and `bitmagic upgrade` actively pushes creators into git by refusing a dirty tree. The
|
|
52
|
+
* scaffolded AGENTS.md's "do not edit any tracked file between verifying and publishing" cannot
|
|
53
|
+
* prevent it either: committing is not editing. It also made every publish rebuild, since the
|
|
54
|
+
* build manifest's own recorded fingerprint could never still match by the time publish ran.
|
|
55
|
+
*/
|
|
56
|
+
export const EXCLUDED_DIRS = new Set([
|
|
57
|
+
'.bitmagic',
|
|
58
|
+
'node_modules',
|
|
59
|
+
'dist',
|
|
60
|
+
'.vite-cache',
|
|
61
|
+
'.git',
|
|
62
|
+
]);
|
|
63
|
+
/**
|
|
64
|
+
* Files that can never change what the bundle contains, excluded at basename granularity.
|
|
65
|
+
*
|
|
66
|
+
* `.DS_Store`: on macOS, Finder writes it merely for opening the project folder, and no build tool
|
|
67
|
+
* ever reads it — so it is another way to flip the fingerprint without editing anything.
|
|
68
|
+
*
|
|
69
|
+
* `mechanics-plan.md`: the agent is told to rewrite it after every slice — moving shipped items
|
|
70
|
+
* out of the backlog and re-ranking the rest is the whole point of the file. Left in, the ordinary
|
|
71
|
+
* `bitmagic verify` → update the backlog → `bitmagic publish` sequence exits 3 over prose, which
|
|
72
|
+
* teaches agents to reach for `--force`, the one flag that also waves through a genuinely stale
|
|
73
|
+
* verify.
|
|
74
|
+
*
|
|
75
|
+
* `GAME-DESIGN.md`: same "no build tool reads it" test — nothing imports it,
|
|
76
|
+
* `vite.publish.config.js` cannot put prose in the bundle, and it exists to be REWRITTEN as the
|
|
77
|
+
* game evolves (it is the input `bitmagic cover` reads).
|
|
78
|
+
*
|
|
79
|
+
* The list is deliberately not a general ignore mechanism, because a false "fresh" ships an
|
|
80
|
+
* unverified bundle while a false "stale" only costs one `bitmagic verify`.
|
|
81
|
+
*
|
|
82
|
+
* NOTE the asymmetry with `SECRET_FILE_PATTERNS` below: these files are excluded from the
|
|
83
|
+
* FINGERPRINT but still travel in the source archive, because they are exactly what a human
|
|
84
|
+
* reading the archive wants. Secrets are the other way round.
|
|
85
|
+
*/
|
|
86
|
+
export const EXCLUDED_FILES = new Set([
|
|
87
|
+
'.DS_Store',
|
|
88
|
+
'GAME-DESIGN.md',
|
|
89
|
+
'mechanics-plan.md',
|
|
90
|
+
]);
|
|
91
|
+
/**
|
|
92
|
+
* Excluded from the fingerprint, but still carried in the source archive.
|
|
93
|
+
*
|
|
94
|
+
* The two sets answer different questions. The fingerprint asks "would changing this change the
|
|
95
|
+
* bundle?" — for a design document, no, which is why editing one must not invalidate a verify. The
|
|
96
|
+
* archive asks "would a human opening this want it?" — for a design document, emphatically yes: it
|
|
97
|
+
* is the single most useful file for understanding what the game was trying to be.
|
|
98
|
+
*
|
|
99
|
+
* `.DS_Store` is in neither, so it is deliberately absent here: nobody wants it and it is not
|
|
100
|
+
* evidence of anything.
|
|
101
|
+
*/
|
|
102
|
+
export const ARCHIVED_UNFINGERPRINTED_FILES = new Set([
|
|
103
|
+
'GAME-DESIGN.md',
|
|
104
|
+
'mechanics-plan.md',
|
|
105
|
+
]);
|
|
106
|
+
/**
|
|
107
|
+
* Files that must never travel inside the source archive, however the fingerprint treats them.
|
|
108
|
+
*
|
|
109
|
+
* The archive lands in the portal bucket, and that bucket carries a BUCKET-level
|
|
110
|
+
* `allUsers:objectViewer` grant (see `.github/workflows/README.md`) — every object in it is
|
|
111
|
+
* world-readable. A creator's project is their working directory, and working directories collect
|
|
112
|
+
* credentials. Shipping one to a public URL because nobody thought about it is how an incident
|
|
113
|
+
* starts.
|
|
114
|
+
*
|
|
115
|
+
* These files are still hashed and still listed in the manifest (path + hash, no content), so the
|
|
116
|
+
* server can reconcile the fingerprint without ever receiving the bytes. That is the whole reason
|
|
117
|
+
* the manifest is separate from the payload.
|
|
118
|
+
*
|
|
119
|
+
* Deliberately a deny-list of shapes rather than an allow-list of known-good paths: an allow-list
|
|
120
|
+
* would silently drop a creator's own source file the first time someone used an unusual layout,
|
|
121
|
+
* and dropping game code from the archive is a quieter failure than including a stray dotfile.
|
|
122
|
+
* The trade is accepted knowingly — this list will never be complete, which is why the archive's
|
|
123
|
+
* storage key is also unguessable rather than relying on this alone.
|
|
124
|
+
*/
|
|
125
|
+
export const SECRET_FILE_PATTERNS = [
|
|
126
|
+
/(^|\/)\.env($|\.)/,
|
|
127
|
+
/\.pem$/,
|
|
128
|
+
/\.key$/,
|
|
129
|
+
/(^|\/)id_rsa/,
|
|
130
|
+
/(^|\/)\.npmrc$/,
|
|
131
|
+
/(^|\/)\.git-credentials$/,
|
|
132
|
+
/service-account[^/]*\.json$/,
|
|
133
|
+
/(^|\/)\.claude\/settings\.local\.json$/,
|
|
134
|
+
];
|
|
135
|
+
/** Would including this path in the archive payload risk publishing a credential? */
|
|
136
|
+
export function isSecretPath(posixPath) {
|
|
137
|
+
return SECRET_FILE_PATTERNS.some((pattern) => pattern.test(posixPath));
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A relative path as the fingerprint hashes it: always forward-slashed.
|
|
141
|
+
*
|
|
142
|
+
* `sep` is a parameter rather than a direct `path.sep` read so this is testable on any
|
|
143
|
+
* platform — the Windows behaviour that makes normalization necessary cannot otherwise be
|
|
144
|
+
* exercised on the POSIX runners CI uses, which is exactly how a vacuous test gets written.
|
|
145
|
+
*/
|
|
146
|
+
export function toPosixPath(relativePath, sep = '/') {
|
|
147
|
+
return relativePath.split(sep).join('/');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The fingerprint: a hash of everything that determines the published bundle.
|
|
151
|
+
*
|
|
152
|
+
* This is what lets `publish` decide whether a `verify` is too stale to trust, by CONTENT
|
|
153
|
+
* rather than by clock: a timestamp policy would accept an edit made one second after
|
|
154
|
+
* verifying and reject an untouched project that merely sat overnight.
|
|
155
|
+
*
|
|
156
|
+
* The path is hashed alongside the content so that moving a file — which changes what the
|
|
157
|
+
* bundle imports — changes the fingerprint even though no byte of any file did.
|
|
158
|
+
*
|
|
159
|
+
* Kept deliberately coarse (every tracked file, not a module graph): a false "stale" costs one
|
|
160
|
+
* `bitmagic verify`, while a false "fresh" ships an unverified bundle.
|
|
161
|
+
*
|
|
162
|
+
* The exact byte sequence folded here is load-bearing across two independently-released
|
|
163
|
+
* packages. Do not "clean up" the trailing newline or the separator without bumping
|
|
164
|
+
* `SOURCE_INDEX_ALGO`.
|
|
165
|
+
*/
|
|
166
|
+
export function fingerprintFromIndex(files) {
|
|
167
|
+
const sorted = [...files].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
168
|
+
const hash = createHash('sha256');
|
|
169
|
+
for (const entry of sorted) {
|
|
170
|
+
hash.update(`${entry.path}:${entry.sha256}\n`);
|
|
171
|
+
}
|
|
172
|
+
return hash.digest('hex');
|
|
173
|
+
}
|
|
174
|
+
/** Where the archive's manifest lives inside the zip. */
|
|
175
|
+
export const SOURCE_MANIFEST_NAME = 'bitmagic-source.json';
|
|
176
|
+
/**
|
|
177
|
+
* Project-relative prefix of the vendored engine. `bitmagic init`/`upgrade` unpack the release
|
|
178
|
+
* tarball's `game/src/<root>` into `engine/<root>`, so every vendored file lives under this.
|
|
179
|
+
*/
|
|
180
|
+
export const VENDORED_ENGINE_PREFIX = 'engine/';
|
|
181
|
+
/**
|
|
182
|
+
* Map a path as it appears in the engine release tarball to where it lands in a scaffolded
|
|
183
|
+
* project, or null when the tarball entry is not vendored into projects at all.
|
|
184
|
+
*
|
|
185
|
+
* The tarball stores three shapes at different depths (see `cli/src/scaffold/engine-download.ts`):
|
|
186
|
+
* `game/src/<root>/…` → `engine/<root>/…`, `game/agent-docs/…` → `engine/agent-docs/…`, and
|
|
187
|
+
* `game/sw-cache-buster.js` → `sw-cache-buster.js` at the project root. `templates/` ships in the
|
|
188
|
+
* tarball for the scaffolder to read and is never vendored, so it maps to null.
|
|
189
|
+
*
|
|
190
|
+
* Lives here rather than in api-server because it is a statement about what the CLI did to the
|
|
191
|
+
* tarball, and the CLI is the other consumer that has to agree.
|
|
192
|
+
*/
|
|
193
|
+
export function tarballPathToProjectPath(tarballPath) {
|
|
194
|
+
if (tarballPath.startsWith('game/src/')) {
|
|
195
|
+
return `${VENDORED_ENGINE_PREFIX}${tarballPath.slice('game/src/'.length)}`;
|
|
196
|
+
}
|
|
197
|
+
if (tarballPath.startsWith('game/agent-docs/')) {
|
|
198
|
+
return `${VENDORED_ENGINE_PREFIX}agent-docs/${tarballPath.slice('game/agent-docs/'.length)}`;
|
|
199
|
+
}
|
|
200
|
+
if (tarballPath === 'game/sw-cache-buster.js') {
|
|
201
|
+
return 'sw-cache-buster.js';
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Bytes every genuine published bundle carries, used to refuse a publish that is not an engine
|
|
207
|
+
* build at all.
|
|
208
|
+
*
|
|
209
|
+
* These survive the toolchain verbatim, which is the entire reason they were chosen over anything
|
|
210
|
+
* cleverer. The scaffold's `index.html` emits `<div id="game-container">` and a
|
|
211
|
+
* `<script type="importmap">` whose body Vite only RELOCATES (its import-map hook lifts the tag
|
|
212
|
+
* out and reinserts the matched text unchanged before the first module script) — so both land, byte
|
|
213
|
+
* for byte, a few KB into the single-file bundle, well inside the head range `complete` already
|
|
214
|
+
* reads for the meta block. Verified against a real `vite build` with `vite-plugin-singlefile`,
|
|
215
|
+
* not inferred.
|
|
216
|
+
*
|
|
217
|
+
* Why a SUBSET of the alias entries rather than all of them. A creator's installed CLI is an older
|
|
218
|
+
* npm release than the running server, so the server must only require markers that both agree on.
|
|
219
|
+
* The CDN entries are disqualified outright — they embed the pinned three.js version, which moves
|
|
220
|
+
* whenever the CLI bumps it. Of the local aliases these three are the ones that have to exist for
|
|
221
|
+
* a project to resolve its entry module at all, so requiring more would buy nothing and would turn
|
|
222
|
+
* any future alias change into a spurious refusal for every not-yet-upgraded project.
|
|
223
|
+
*
|
|
224
|
+
* What this is worth: it stops bitmagic.ai being used to host arbitrary HTML by someone who has an
|
|
225
|
+
* account and a game id. It is trivially copy-pasteable by anyone who looks, and is not claimed to
|
|
226
|
+
* be more than that.
|
|
227
|
+
*/
|
|
228
|
+
export const ENGINE_BUILD_MARKERS = [
|
|
229
|
+
'id="game-container"',
|
|
230
|
+
'"engine/": "/dist/engine/engine/"',
|
|
231
|
+
'"types/": "/dist/engine/types/"',
|
|
232
|
+
];
|
|
233
|
+
/** Does this bundle's head carry every marker a genuine engine build has? */
|
|
234
|
+
export function hasEngineBuildMarkers(head) {
|
|
235
|
+
return ENGINE_BUILD_MARKERS.every((marker) => head.includes(marker));
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Hash used ONLY when comparing a vendored engine file against the official release.
|
|
239
|
+
*
|
|
240
|
+
* Line endings are normalised first, which the fingerprint deliberately never does. The reason is
|
|
241
|
+
* Windows: `renderGitignore()` does not ignore `engine/`, so the vendored tree is committed, and
|
|
242
|
+
* AGENTS.md pushes creators into git (`bitmagic upgrade` refuses a dirty tree). A Windows checkout
|
|
243
|
+
* with the default `core.autocrlf=true` rewrites LF to CRLF in every `.ts` file it restores — so
|
|
244
|
+
* without this, every Windows creator would see 100% of their engine files reported as modified,
|
|
245
|
+
* forever, on every publish. That is a false alarm about the one check most likely to be believed.
|
|
246
|
+
*
|
|
247
|
+
* Confined to this comparison on purpose. Normalising inside the fingerprint would instead make
|
|
248
|
+
* the fingerprint disagree with itself across platforms in the staleness gate, where a false
|
|
249
|
+
* "fresh" is the dangerous direction.
|
|
250
|
+
*/
|
|
251
|
+
export function engineComparisonHash(content) {
|
|
252
|
+
// TextDecoder rather than Buffer: this module is imported by an npm-published CLI as well as by
|
|
253
|
+
// the server, and keeping Node's Buffer out of it means the file has no ambient-global
|
|
254
|
+
// dependency beyond `node:crypto`, which it imports explicitly.
|
|
255
|
+
const text = new TextDecoder('utf-8').decode(content);
|
|
256
|
+
return createHash('sha256').update(text.replace(/\r\n/g, '\n'), 'utf8').digest('hex');
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=publish-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-source.js","sourceRoot":"","sources":["../src/publish-source.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitmagic/asset-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"types": "./dist/favicon.d.ts",
|
|
18
18
|
"default": "./dist/favicon.js"
|
|
19
19
|
},
|
|
20
|
+
"./publish-source": {
|
|
21
|
+
"types": "./dist/publish-source.d.ts",
|
|
22
|
+
"default": "./dist/publish-source.js"
|
|
23
|
+
},
|
|
20
24
|
"./animation/*": {
|
|
21
25
|
"default": "./src/animation/*"
|
|
22
26
|
},
|