1616//
1717// injected = this framework tree's packages/spec (what must be bundled)
1818// vendored = the @objectstack/spec objectui's own lockfile installed
19- // (what gets bundled when the injection is missing or broken)
19+ // (what gets bundled when the injection is missing or broken);
20+ // on a pin carrying `.objectstack-sha`, objectui's own build of
21+ // that commit — see readPinnedReference for why only that one
2022//
2123// ## The test is two-sided, because one side is not enough
2224//
7375// --assets <built console dist/assets>
7476//
7577// Exit: 0 = injection proven (or no skew to prove) · 1 = injection failed
76- // 2 = inconclusive / cannot run
78+ // 2 = inconclusive / cannot run (including a reference that is this tree)
7779
80+ import fs from 'node:fs' ;
7881import path from 'node:path' ;
7982
8083import {
@@ -107,6 +110,50 @@ function parseArgs(argv) {
107110
108111const args = parseArgs ( process . argv ) ;
109112
113+ /**
114+ * The objectstack commit --vendored was built at, when objectui pins one; null otherwise.
115+ *
116+ * A pin carrying `.objectstack-sha` installs @objectstack/* by commit, and
117+ * build-console.sh stands THIS tree in for that commit, so the spec objectui's
118+ * install holds is this tree's own, packed. As --vendored it equals --injected:
119+ * both pools are empty, the no-skew branch below passes, and the stamp records no
120+ * witness for ANY bundle. What a broken pack-or-inject ships there is objectui's
121+ * own build of the pinned commit, so that build is the only reference taken on
122+ * such a pin. It is known by the stamp objectui's scripts/objectstack-pin.mjs
123+ * writes at the root of the cache it packed into (`<cache>/.stamp.json` beside
124+ * `<cache>/spec`): `source: 'pin'`, `sha` equal to the pin. A stand-in, a copy
125+ * with no such stamp (the install's node_modules/@objectstack/spec) or a build of
126+ * another commit is refused as cannot-judge, never read as "no skew".
127+ */
128+ function readPinnedReference ( objectuiDir , vendoredDir ) {
129+ const pinFile = path . join ( objectuiDir , '.objectstack-sha' ) ;
130+ if ( ! fs . existsSync ( pinFile ) ) return null ;
131+ const pin = fs . readFileSync ( pinFile , 'utf8' ) . trim ( ) ;
132+ const stampFile = path . join ( path . dirname ( vendoredDir ) , '.stamp.json' ) ;
133+ let stamp = null ;
134+ try {
135+ stamp = JSON . parse ( fs . readFileSync ( stampFile , 'utf8' ) ) ;
136+ } catch {
137+ // No readable stamp is refused below, with the rest.
138+ }
139+ if ( stamp ?. source !== 'pin' || stamp ?. sha !== pin ) {
140+ const found = stamp ? `source ${ JSON . stringify ( stamp . source ) } at ${ String ( stamp . sha ) . slice ( 0 , 12 ) } ` : 'no readable stamp' ;
141+ fail (
142+ `objectui pins objectstack ${ pin . slice ( 0 , 12 ) } (.objectstack-sha), so --vendored must be objectui's own\n` +
143+ ` build of that commit, and \`${ stampFile } \` says ${ found } . On this pin objectui's install is\n` +
144+ " this tree (the OBJECTSTACK_ROOT stand-in), and judged against itself this check would pass any\n" +
145+ " bundle. build-console.sh keeps that build before standing this tree in; pass its spec." ,
146+ ) ;
147+ }
148+ return pin ;
149+ }
150+
151+ const vendoredPin = readPinnedReference ( path . resolve ( args . objectui ) , path . resolve ( args . vendored ) ) ;
152+ const vendoredName = vendoredPin
153+ ? `@objectstack/spec of objectui's own build of objectstack ${ vendoredPin . slice ( 0 , 12 ) } `
154+ : 'PUBLISHED @objectstack/spec' ;
155+ const vendoredOnly = vendoredPin ? 'pin-only' : 'published-only' ;
156+
110157const assetsDir = path . resolve ( args . assets ) ;
111158// The dist root is the assets dir's parent: build-console.sh passes
112159// `<dist>/assets`, so this is where the .objectui-sha stamp already lives.
@@ -162,6 +209,8 @@ function stamp(skew) {
162209 {
163210 name : '@objectstack/spec' ,
164211 injectedFrom : path . resolve ( args . injected ) ,
212+ vendoredFrom : path . resolve ( args . vendored ) ,
213+ vendoredPin,
165214 skew,
166215 freshWitness,
167216 staleDetector,
@@ -175,8 +224,11 @@ function stamp(skew) {
175224 }
176225}
177226
227+ // With a pinned reference this is the honest case, never the vacuous one: an
228+ // independently built spec of the pinned commit declares the same descriptions as
229+ // this tree's, so a bundle carrying either carries the same text.
178230if ( freshCounts . pool === 0 && staleCounts . pool === 0 ) {
179- console . log ( ' ✓ Injected and vendored @objectstack/spec declare the same descriptions' ) ;
231+ console . log ( ` ✓ Injected and vendored @objectstack/spec declare the same descriptions${ vendoredPin ? ` (vendored: ${ vendoredName } )` : '' } ` ) ;
180232 console . log ( ' — no observable skew, so nothing for this check to assert.' ) ;
181233 stamp ( false ) ;
182234 process . exit ( 0 ) ;
@@ -193,7 +245,7 @@ if (staleCounts.pool > 0 && staleDetector === null) {
193245 console . error ( " it could look for is in the bundle, and objectui's own source at the pin carries" ) ;
194246 console . error ( ' each one too, so none can tell the published spec from objectui\'s own text.' ) ;
195247 console . error ( ' The injection is UNVERIFIED by this check.' ) ;
196- console . error ( ` published-only descriptions: ${ staleCounts . pool } , all of them carried by objectui's source` ) ;
248+ console . error ( ` ${ vendoredOnly } descriptions: ${ staleCounts . pool } , all of them carried by objectui's source` ) ;
197249 console . error ( ` first of them: "${ staleHostCarried [ 0 ] } "` ) ;
198250 process . exit ( 2 ) ;
199251}
@@ -204,19 +256,19 @@ if (freshPresent !== true && stalePresent !== true) {
204256 console . error ( '✗ Neither spec appears in the built console — no @objectstack/spec' ) ;
205257 console . error ( ' content matched. The injection is UNVERIFIED by this check.' ) ;
206258 console . error ( ` injected-only descriptions in the bundle: ${ freshCounts . inBundle } of ${ freshCounts . pool } ` ) ;
207- console . error ( ` published-only descriptions in the bundle: ${ staleCounts . inBundle } of ${ staleCounts . pool } ` ) ;
259+ console . error ( ` ${ vendoredOnly } descriptions in the bundle: ${ staleCounts . inBundle } of ${ staleCounts . pool } ` ) ;
208260 for ( const line of [ ...freshNote ( ' ' ) , ...staleNote ( ' ' ) ] ) console . error ( line ) ;
209261 process . exit ( 2 ) ;
210262}
211263
212264if ( stalePresent === true ) {
213- console . error ( " ✗ Built console still carries the PUBLISHED @objectstack/spec." ) ;
214- console . error ( ' The console resolved spec from objectui\ 's lockfile, so any authorable' ) ;
215- console . error ( ' key this framework declared after the last spec publish is unreachable' ) ;
265+ console . error ( ` ✗ Built console still carries the ${ vendoredName } .` ) ;
266+ console . error ( ` The console resolved spec from objectui's ${ vendoredPin ? 'own pin' : ' lockfile' } , so any authorable` ) ;
267+ console . error ( ` key this framework declared after ${ vendoredPin ? 'that commit' : ' the last spec publish' } is unreachable` ) ;
216268 console . error ( ' in the Studio designer — the defect objectstack#8134 exists to end.' ) ;
217269 console . error ( '' ) ;
218270 console . error ( ` Text found in the bundle that ONLY the vendored spec has (${ staleCounts . inBundle } of` ) ;
219- console . error ( ` ${ staleCounts . pool } published-only descriptions), the first of them:` ) ;
271+ console . error ( ` ${ staleCounts . pool } ${ vendoredOnly } descriptions), the first of them:` ) ;
220272 console . error ( ` "${ staleDetector } "` ) ;
221273 if ( staleCounts . hostCarried > 0 ) {
222274 console . error ( '' ) ;
@@ -241,16 +293,16 @@ if (freshPresent !== true) {
241293 process . exit ( 2 ) ;
242294}
243295
244- console . log ( " ✓ Console bundle carries THIS tree's @objectstack/spec, and only it." ) ;
296+ console . log ( ` ✓ Console bundle carries THIS tree's @objectstack/spec, and only it${ vendoredPin ? ` (vendored: ${ vendoredName } )` : '' } .` ) ;
245297console . log ( ` present (injected only): "${ freshWitness } "` ) ;
246298console . log ( ` — ${ freshCounts . inBundle } of ${ freshCounts . pool } injected-only descriptions are in the bundle` ) ;
247299for ( const line of freshNote ( ' ' ) ) console . log ( line ) ;
248300if ( staleDetector ) {
249301 console . log ( ` absent (vendored only): "${ staleDetector } "` ) ;
250302 if ( staleCounts . hostCarried === 0 ) {
251- console . log ( ` — and all ${ staleCounts . pool } published-only descriptions are absent` ) ;
303+ console . log ( ` — and all ${ staleCounts . pool } ${ vendoredOnly } descriptions are absent` ) ;
252304 } else {
253- console . log ( ` — and ${ staleCounts . pool - staleCounts . hostCarried } of ${ staleCounts . pool } published-only descriptions are absent;` ) ;
305+ console . log ( ` — and ${ staleCounts . pool - staleCounts . hostCarried } of ${ staleCounts . pool } ${ vendoredOnly } descriptions are absent;` ) ;
254306 for ( const line of staleNote ( ' ' ) ) console . log ( line ) ;
255307 }
256308}
0 commit comments