Skip to content

Commit 6903620

Browse files
committed
fix(cli): the react-pages reader in console.ts imports Node built-ins only
`utils/console.ts` is behind the published `./console` subpath, which the packed-tarball pin runs with no other package installed, so the import of `@objectstack/types` failed there with ERR_MODULE_NOT_FOUND. The legacy `OS_PAGE_REACT` fallback is now spelled in the module, and its deprecation warning goes through the plugin logger once. The reader and its constants are no longer exported, so the module's export census is unchanged. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X4d6XVfcfxL17bMpptytcc
1 parent e6df92d commit 6903620

2 files changed

Lines changed: 43 additions & 14 deletions

File tree

‎packages/cli/src/utils/console.content-security-policy.test.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ describe('\'unsafe-eval\' follows the react-pages tier, which is off unless the
213213
expect(html).not.toContain('__OBJECTUI_CAPABILITIES_DISABLED__');
214214
});
215215

216-
it('reads the legacy OS_PAGE_REACT in both directions while the new name is unset', async () => {
216+
it('reads the legacy OS_PAGE_REACT in both directions while the new name is unset, and names the new one', async () => {
217217
process.env.OS_PAGE_REACT = 'on';
218-
expect((await serve()).scriptSrc).toContain("'unsafe-eval'");
218+
const warnings: string[] = [];
219+
expect((await serve(warnings)).scriptSrc).toContain("'unsafe-eval'");
220+
expect(warnings.filter((w) => w.includes('`OS_PAGE_REACT` is deprecated; rename it to `OS_PAGE_REACT_ENABLED`'))).toHaveLength(1);
219221
process.env.OS_PAGE_REACT = 'off';
220222
expect((await serve()).scriptSrc).not.toContain("'unsafe-eval'");
221223
});

‎packages/cli/src/utils/console.ts‎

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import fs from 'fs';
4848
import { createHash, randomBytes } from 'crypto';
4949
import { createRequire } from 'module';
5050
import { pathToFileURL, fileURLToPath } from 'url';
51-
import { readEnvWithDeprecation } from '@objectstack/types';
5251

5352
// ─── Constants ──────────────────────────────────────────────────────
5453

@@ -796,28 +795,44 @@ function consoleContentSecurityPolicy(input: {
796795
}
797796

798797
/** The variable a deployment sets to turn the `kind:'react'` page tier on. */
799-
export const REACT_PAGES_ENV = 'OS_PAGE_REACT_ENABLED';
800-
/** Its earlier name, read when the new one is unset (`readEnvWithDeprecation`). */
798+
const REACT_PAGES_ENV = 'OS_PAGE_REACT_ENABLED';
799+
/** Its earlier name, read when the new one is unset. */
801800
const REACT_PAGES_LEGACY_ENV = 'OS_PAGE_REACT';
802801
const REACT_PAGES_ON = new Set(['true', '1', 'on', 'yes', 'enabled']);
803802
const REACT_PAGES_OFF = new Set(['', 'false', '0', 'off', 'no', 'disabled']);
804803

804+
interface ReactPagesTierReports {
805+
/** The value is neither an on nor an off spelling. */
806+
unrecognized?: (name: string, value: string) => void;
807+
/** Only the legacy name is set. */
808+
legacyName?: (legacy: string, preferred: string) => void;
809+
}
810+
805811
/**
806812
* Whether this deployment serves the `kind:'react'` page tier (ADR-0081, as
807813
* amended: off by default). That tier runs the page author's code in every
808814
* viewer's Console with no sandbox, so it is on only when the operator says so
809815
* with `OS_PAGE_REACT_ENABLED=true` (or the legacy `OS_PAGE_REACT=on`). A value
810-
* that is neither an on nor an off spelling leaves the tier off and is reported
811-
* through `onUnrecognized`, so a typo never reads as a silent refusal.
816+
* that is neither an on nor an off spelling leaves the tier off and is reported,
817+
* so a typo never reads as a silent refusal.
818+
*
819+
* The legacy fallback is spelled here rather than through
820+
* `readEnvWithDeprecation` (`@objectstack/types`): this module is behind the
821+
* published `./console` subpath, which imports Node built-ins only
822+
* (`test/published-subpath-console.pin.test.ts` runs it from the packed
823+
* tarball with no other package installed).
812824
*/
813-
export function readReactPagesTier(onUnrecognized?: (name: string, value: string) => void): boolean {
814-
const raw = readEnvWithDeprecation(REACT_PAGES_ENV, REACT_PAGES_LEGACY_ENV);
825+
function readReactPagesTier(reports: ReactPagesTierReports = {}): boolean {
826+
let name = REACT_PAGES_ENV;
827+
let raw = process.env[REACT_PAGES_ENV];
828+
if (raw === undefined && process.env[REACT_PAGES_LEGACY_ENV] !== undefined) {
829+
name = REACT_PAGES_LEGACY_ENV;
830+
raw = process.env[REACT_PAGES_LEGACY_ENV];
831+
reports.legacyName?.(REACT_PAGES_LEGACY_ENV, REACT_PAGES_ENV);
832+
}
815833
const value = String(raw ?? '').trim().toLowerCase();
816834
if (REACT_PAGES_ON.has(value)) return true;
817-
if (!REACT_PAGES_OFF.has(value)) {
818-
const name = process.env[REACT_PAGES_ENV] !== undefined ? REACT_PAGES_ENV : REACT_PAGES_LEGACY_ENV;
819-
onUnrecognized?.(name, String(raw));
820-
}
835+
if (!REACT_PAGES_OFF.has(value)) reports.unrecognized?.(name, String(raw));
821836
return false;
822837
}
823838

@@ -916,6 +931,15 @@ export function createConsoleStaticPlugin(distPath: string, options?: { isDev?:
916931
+ 'unset it to keep them off. Reported once.',
917932
);
918933
};
934+
let reportedLegacyReactPagesName = false;
935+
const reportLegacyReactPagesName = (legacy: string, preferred: string) => {
936+
if (reportedLegacyReactPagesName) return;
937+
reportedLegacyReactPagesName = true;
938+
ctx.logger?.warn?.(
939+
`Console static: env var \`${legacy}\` is deprecated; rename it to \`${preferred}\`. `
940+
+ 'The legacy name still works for now but will be removed in a future major release.',
941+
);
942+
};
919943

920944
// A runtime-config read that fails leaves the shell's head as the build
921945
// shipped it, which is a smaller page, not a broken one: said once, at
@@ -960,7 +984,10 @@ export function createConsoleStaticPlugin(distPath: string, options?: { isDev?:
960984
const nonce = randomBytes(16).toString('base64');
961985
// One read per document, so the injected capability global and the
962986
// policy's `'unsafe-eval'` always agree.
963-
const reactPages = readReactPagesTier(reportUnrecognizedReactPages);
987+
const reactPages = readReactPagesTier({
988+
unrecognized: reportUnrecognizedReactPages,
989+
legacyName: reportLegacyReactPagesName,
990+
});
964991
const html = readIndexHtml(reactPages, nonce);
965992
const shell = await readShellConfig(app, c, reportShellConfigReadFailure);
966993
const policy = consoleContentSecurityPolicy({

0 commit comments

Comments
 (0)