@timekast/factory 1.21.0 → 1.22.0

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.
@@ -200,6 +200,26 @@ export const E2E_UI_SCRIPT_CMD = `tsx ${E2E_DIRECT_SCRIPT_FILE} --ui`;
200
200
  export const GENERATE_REFERENCE_NAME = 'generate:reference';
201
201
  export const GENERATE_REFERENCE_CMD = 'tsx scripts/tools/generate-inventory.ts && tsx scripts/tools/generate-codebase.ts && tsx scripts/tools/generate-hooks.ts && tsx scripts/tools/generate-schema.ts && tsx scripts/tools/generate-api.ts';
202
202
  export const GENERATE_REFERENCE_GATE_FILE = 'scripts/tools/generate-schema.ts';
203
+ /**
204
+ * The dev server's heap ceiling, carried inside the `dev:next` script the launcher shells
205
+ * (`scripts/tools/dev.mjs` → `pnpm dev:next`).
206
+ *
207
+ * WHY THE CLI OWNS IT. This value lives in `package.json`, which is frozen at bootstrap in a
208
+ * derivative (BR-FACTORY-006) and is NOT part of the tracked file set — so `factory update`
209
+ * swapping `.claude/` + `scripts/` can never reach it. The CLI is the only thing that can,
210
+ * exactly as it does for `agentKitVersion` and the retired e2e port. Without an entry here,
211
+ * every existing derivative would keep the old ceiling forever while new ones are born with
212
+ * the current one.
213
+ *
214
+ * FILE-GATED on the launcher, like `GENERATOR_SCRIPTS`: a `core`/non-Next derivative has no
215
+ * `dev.mjs` and never receives an alias for a dev server it does not run.
216
+ */
217
+ export const DEV_NEXT_SCRIPT_NAME = 'dev:next';
218
+ export const DEV_NEXT_GATE_FILE = 'scripts/tools/dev.mjs';
219
+ export const DEV_NEXT_HEAP_MB = 4096;
220
+ export const DEV_NEXT_SCRIPT_CMD = `NODE_OPTIONS='--max-old-space-size=${DEV_NEXT_HEAP_MB}' next dev --turbopack`;
221
+ /** The ceiling(s) the kit shipped before — the ONLY values healing is allowed to overwrite. */
222
+ export const DEV_NEXT_OBSOLETE_HEAPS_MB = [2048];
203
223
  /**
204
224
  * The `ports` block of a derivative's `package.json`, and the one key inside it the CLI
205
225
  * maintains.
@@ -244,4 +264,8 @@ export const OBSOLETE_SCRIPT_CMDS = {
244
264
  // dev can act on. A derivative that pointed either alias somewhere of its own keeps it.
245
265
  [E2E_DIRECT_SCRIPT_NAME]: ['playwright test'],
246
266
  [E2E_UI_SCRIPT_NAME]: ['playwright test --ui'],
267
+ // Every heap ceiling the kit shipped for the dev server before the current one. Listed as
268
+ // whole COMMANDS (not just the number) so healing stays exact-match: a derivative that
269
+ // appended a flag of its own, or picked a ceiling the kit never shipped, is left alone.
270
+ [DEV_NEXT_SCRIPT_NAME]: DEV_NEXT_OBSOLETE_HEAPS_MB.map((mb) => `NODE_OPTIONS='--max-old-space-size=${mb}' next dev --turbopack`),
247
271
  };
@@ -28,6 +28,28 @@ const NEON_API_TIMEOUT_MS = 30_000;
28
28
  export const NEON_MAIN_BRANCH = 'main';
29
29
  /** Name of the isolated, data-clean second branch this step creates. */
30
30
  export const NEON_DEVELOP_BRANCH = 'develop';
31
+ /**
32
+ * Autoscaling bounds every compute this CLI creates is born with.
33
+ *
34
+ * We declare them EXPLICITLY instead of inheriting Neon's account default, which
35
+ * is not stable over time: projects provisioned in earlier eras were born at a
36
+ * fixed `1-1 CU` (min == max), which bills a full CU for every hour the compute
37
+ * is merely awake — 4x the floor below — and today's default is `0.25-8`, whose
38
+ * ceiling is 32x the floor. Neither is a deliberate choice for a fresh derivative.
39
+ *
40
+ * The floor is what an idle-but-awake compute bills, so it is the single biggest
41
+ * lever on the fleet's Neon bill; 0.25 CU is Neon's minimum. The ceiling is pure
42
+ * headroom (it costs nothing until traffic actually needs it), so 2 CU gives a
43
+ * new project 8x of burst room while keeping a runaway query from silently
44
+ * scaling to 8 CU. A project that outgrows it raises the ceiling deliberately.
45
+ */
46
+ export const NEON_AUTOSCALING_MIN_CU = 0.25;
47
+ export const NEON_AUTOSCALING_MAX_CU = 2;
48
+ /** The autoscaling pair as the Neon REST API spells it — shared by both writes. */
49
+ const NEON_AUTOSCALING_SETTINGS = {
50
+ autoscaling_limit_min_cu: NEON_AUTOSCALING_MIN_CU,
51
+ autoscaling_limit_max_cu: NEON_AUTOSCALING_MAX_CU,
52
+ };
31
53
  /**
32
54
  * Neon returns 423 (Locked) while a freshly-created project runs its post-create
33
55
  * operations ("project already has running conflicting operations"). The develop
@@ -93,6 +115,10 @@ export async function createNeonProject(apiKey, projectName, orgId, deps = {}) {
93
115
  ...(orgId ? { org_id: orgId } : {}),
94
116
  // Name the auto-created primary branch `main` so production reads cleanly.
95
117
  branch: { name: NEON_MAIN_BRANCH },
118
+ // Bind the autoscaling bounds at the PROJECT level so the primary compute
119
+ // AND every branch created later (by us or by hand in the console) inherit
120
+ // them — otherwise a new branch silently re-adopts Neon's account default.
121
+ default_endpoint_settings: { ...NEON_AUTOSCALING_SETTINGS },
96
122
  },
97
123
  }),
98
124
  signal: AbortSignal.timeout(NEON_API_TIMEOUT_MS),
@@ -129,7 +155,10 @@ export async function createDevelopBranch(apiKey, projectId, parentBranchId, dep
129
155
  headers: neonHeaders(apiKey),
130
156
  body: JSON.stringify({
131
157
  branch: { name: NEON_DEVELOP_BRANCH, parent_id: parentBranchId },
132
- endpoints: [{ type: 'read_write' }],
158
+ // Stated explicitly rather than left to project inheritance: this branch is
159
+ // created in the same run as the project, and a bare `{type}` endpoint is
160
+ // exactly how the fleet ended up with computes nobody chose the size of.
161
+ endpoints: [{ type: 'read_write', ...NEON_AUTOSCALING_SETTINGS }],
133
162
  }),
134
163
  signal: AbortSignal.timeout(NEON_API_TIMEOUT_MS),
135
164
  });
@@ -8,7 +8,7 @@
8
8
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import { CLIError } from './cli-error.js';
11
- import { E2E_DIRECT_SCRIPT_CMD, E2E_DIRECT_SCRIPT_FILE, E2E_DIRECT_SCRIPT_NAME, E2E_UI_SCRIPT_CMD, E2E_UI_SCRIPT_NAME, FACTORY_SCRIPTS, GENERATE_REFERENCE_CMD, GENERATE_REFERENCE_GATE_FILE, GENERATE_REFERENCE_NAME, GENERATOR_SCRIPTS, INVITE_ADMIN_SCRIPT_CMD, INVITE_ADMIN_SCRIPT_FILE, INVITE_ADMIN_SCRIPT_NAME, OBSOLETE_SCRIPT_CMDS, PORTS_E2E_COMMENT, PORTS_E2E_COMMENT_KEY, PORTS_E2E_KEY, PORTS_KEY, PREFLIGHT_SCRIPT_CMD, PREFLIGHT_SCRIPT_FILE, PREFLIGHT_SCRIPT_NAME, PROFILES, SHIPPED_E2E_PORT, UPDATE_SCRIPT_NAME, } from './constants.js';
11
+ import { DEV_NEXT_GATE_FILE, DEV_NEXT_SCRIPT_CMD, DEV_NEXT_SCRIPT_NAME, E2E_DIRECT_SCRIPT_CMD, E2E_DIRECT_SCRIPT_FILE, E2E_DIRECT_SCRIPT_NAME, E2E_UI_SCRIPT_CMD, E2E_UI_SCRIPT_NAME, FACTORY_SCRIPTS, GENERATE_REFERENCE_CMD, GENERATE_REFERENCE_GATE_FILE, GENERATE_REFERENCE_NAME, GENERATOR_SCRIPTS, INVITE_ADMIN_SCRIPT_CMD, INVITE_ADMIN_SCRIPT_FILE, INVITE_ADMIN_SCRIPT_NAME, OBSOLETE_SCRIPT_CMDS, PORTS_E2E_COMMENT, PORTS_E2E_COMMENT_KEY, PORTS_E2E_KEY, PORTS_KEY, PREFLIGHT_SCRIPT_CMD, PREFLIGHT_SCRIPT_FILE, PREFLIGHT_SCRIPT_NAME, PROFILES, SHIPPED_E2E_PORT, UPDATE_SCRIPT_NAME, } from './constants.js';
12
12
  /**
13
13
  * Auto-detect the profile to install into an existing repo from its
14
14
  * `package.json`: a `factoryVersion` field means the repo was born from the
@@ -113,6 +113,8 @@ function ensureFactoryScripts(pkg, extraScripts = {}) {
113
113
  * - `test:e2e:direct` / `test:e2e:ui` — the two raw-Playwright debugging entry
114
114
  * points, routed through the wrapper that arms the compile-guard stub. Both
115
115
  * gate on that one wrapper file.
116
+ * - `dev:next` — the dev server the launcher shells, whose heap ceiling the kit
117
+ * owns. Gated on the launcher (`dev.mjs`), which is what invokes it.
116
118
  *
117
119
  * Insert-if-missing for all: a dev's own value for any of these is never
118
120
  * overwritten (see `ensureFactoryScripts`).
@@ -134,6 +136,8 @@ export function kitLocalScripts(rootDir) {
134
136
  scripts[E2E_DIRECT_SCRIPT_NAME] = E2E_DIRECT_SCRIPT_CMD;
135
137
  scripts[E2E_UI_SCRIPT_NAME] = E2E_UI_SCRIPT_CMD;
136
138
  }
139
+ if (has(DEV_NEXT_GATE_FILE))
140
+ scripts[DEV_NEXT_SCRIPT_NAME] = DEV_NEXT_SCRIPT_CMD;
137
141
  return scripts;
138
142
  }
139
143
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timekast/factory",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "Public, thin CLI to bootstrap and maintain TimeKast Factory derived projects.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",