@noodleseed/one 0.109.0 → 0.110.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.
Files changed (74) hide show
  1. package/dist/commands/deploy-ops.d.ts +0 -1
  2. package/dist/commands/deploy-ops.d.ts.map +1 -1
  3. package/dist/commands/deploy-ops.js +6 -42
  4. package/dist/commands/deploy-ops.js.map +1 -1
  5. package/dist/commands/project-setup.d.ts.map +1 -1
  6. package/dist/commands/project-setup.js +0 -4
  7. package/dist/commands/project-setup.js.map +1 -1
  8. package/dist/first-run.d.ts.map +1 -1
  9. package/dist/first-run.js +0 -3
  10. package/dist/first-run.js.map +1 -1
  11. package/node_modules/@noodle-borg/control-plane/dist/contracts.d.ts +210 -0
  12. package/node_modules/@noodle-borg/control-plane/dist/contracts.d.ts.map +1 -0
  13. package/node_modules/@noodle-borg/control-plane/dist/contracts.js +2 -0
  14. package/node_modules/@noodle-borg/control-plane/dist/contracts.js.map +1 -0
  15. package/node_modules/@noodle-borg/control-plane/dist/index.d.ts +6 -0
  16. package/node_modules/@noodle-borg/control-plane/dist/index.d.ts.map +1 -0
  17. package/node_modules/@noodle-borg/control-plane/dist/index.js +4 -0
  18. package/node_modules/@noodle-borg/control-plane/dist/index.js.map +1 -0
  19. package/node_modules/@noodle-borg/control-plane/dist/postgres-contracts.d.ts +11 -0
  20. package/node_modules/@noodle-borg/control-plane/dist/postgres-contracts.d.ts.map +1 -0
  21. package/node_modules/@noodle-borg/control-plane/dist/postgres-contracts.js +2 -0
  22. package/node_modules/@noodle-borg/control-plane/dist/postgres-contracts.js.map +1 -0
  23. package/node_modules/@noodle-borg/{service/dist/store/postgres-control-plane.d.ts → control-plane/dist/postgres-organization-store.d.ts} +4 -3
  24. package/node_modules/@noodle-borg/control-plane/dist/postgres-organization-store.d.ts.map +1 -0
  25. package/node_modules/@noodle-borg/{service/dist/store/postgres-control-plane.js → control-plane/dist/postgres-organization-store.js} +4 -5
  26. package/node_modules/@noodle-borg/control-plane/dist/postgres-organization-store.js.map +1 -0
  27. package/node_modules/@noodle-borg/control-plane/dist/postgres-schema.d.ts +4 -0
  28. package/node_modules/@noodle-borg/control-plane/dist/postgres-schema.d.ts.map +1 -0
  29. package/node_modules/@noodle-borg/control-plane/dist/postgres-schema.js +88 -0
  30. package/node_modules/@noodle-borg/control-plane/dist/postgres-schema.js.map +1 -0
  31. package/node_modules/@noodle-borg/control-plane/dist/validation.d.ts +16 -0
  32. package/node_modules/@noodle-borg/control-plane/dist/validation.d.ts.map +1 -0
  33. package/node_modules/@noodle-borg/control-plane/dist/validation.js +87 -0
  34. package/node_modules/@noodle-borg/control-plane/dist/validation.js.map +1 -0
  35. package/node_modules/@noodle-borg/control-plane/package.json +30 -0
  36. package/node_modules/@noodle-borg/service/dist/platform-auth-operator.d.ts +10 -10
  37. package/node_modules/@noodle-borg/service/dist/store/postgres-deploy-records.d.ts +1 -1
  38. package/node_modules/@noodle-borg/service/dist/store/postgres-deploy-records.js +1 -1
  39. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.d.ts.map +1 -1
  40. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js +2 -84
  41. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js.map +1 -1
  42. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.d.ts.map +1 -1
  43. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.js +1 -1
  44. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.js.map +1 -1
  45. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts.map +1 -1
  46. package/node_modules/@noodle-borg/service/dist/store/postgres.js +3 -2
  47. package/node_modules/@noodle-borg/service/dist/store/postgres.js.map +1 -1
  48. package/node_modules/@noodle-borg/service/dist/store/validate.d.ts +2 -20
  49. package/node_modules/@noodle-borg/service/dist/store/validate.d.ts.map +1 -1
  50. package/node_modules/@noodle-borg/service/dist/store/validate.js +7 -85
  51. package/node_modules/@noodle-borg/service/dist/store/validate.js.map +1 -1
  52. package/node_modules/@noodle-borg/service/dist/store.d.ts +3 -208
  53. package/node_modules/@noodle-borg/service/dist/store.d.ts.map +1 -1
  54. package/node_modules/@noodle-borg/service/dist/store.js.map +1 -1
  55. package/node_modules/@noodle-borg/service/package.json +1 -0
  56. package/package.json +2 -1
  57. package/dist/banner.d.ts +0 -11
  58. package/dist/banner.d.ts.map +0 -1
  59. package/dist/banner.js +0 -183
  60. package/dist/banner.js.map +0 -1
  61. package/dist/commands/deploy-fire-progress.d.ts +0 -28
  62. package/dist/commands/deploy-fire-progress.d.ts.map +0 -1
  63. package/dist/commands/deploy-fire-progress.js +0 -64
  64. package/dist/commands/deploy-fire-progress.js.map +0 -1
  65. package/dist/fire.d.ts +0 -108
  66. package/dist/fire.d.ts.map +0 -1
  67. package/dist/fire.js +0 -511
  68. package/dist/fire.js.map +0 -1
  69. package/dist/flavor.d.ts +0 -19
  70. package/dist/flavor.d.ts.map +0 -1
  71. package/dist/flavor.js +0 -105
  72. package/dist/flavor.js.map +0 -1
  73. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts.map +0 -1
  74. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"deploy-fire-progress.js","sourceRoot":"","sources":["../../src/commands/deploy-fire-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,UAAU,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,kGAAkG;AAElG;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAKnC;IACC,OAAO,CACL,CAAC,IAAI,CAAC,IAAI;QACV,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,SAAS;QACzB,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,SAAS;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;QAC1B,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,MAAM;QACvC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;QACzB,IAAI,CAAC,GAAG,CAAC,cAAc,KAAK,SAAS,CACtC,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAkC;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,YAAY,GAAG;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAClD,OAAQ,KAAiB,CAAC,GAAG,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAkB,EAAE,IAA0B;IAChF,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,cAAc,GAAW,UAAU,CAAC,MAAM,CAAC;IAC/C,MAAM,OAAO,GAAG,KAAK,EACnB,KAAkC,EAClC,IAAkC,EACR,EAAE;QAC5B,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QACtD,MAAM,QAAQ,GACZ,CAAC,YAAY,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;QACnF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,YAAY,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACzC,IAAI,WAAW,EAAE,CAAC;YAChB,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;YACnC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,QAAQ,EAAE,CAAC;YACpB,cAAc,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,cAAc,CAAC,GAAG,GAAG,CAAC;YAC7D,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,OAAuB,CAAC;AACjC,CAAC"}
package/dist/fire.d.ts DELETED
@@ -1,108 +0,0 @@
1
- import { type ColorMode, type GlyphMode, type RGB } from './gradient.js';
2
- export type FireMode = 'warm' | 'green' | 'rose';
3
- /** Heat (0..1) through the mode's gradient stops, evenly spaced and linearly interpolated. */
4
- export declare function fireColor(h: number, mode: FireMode): RGB;
5
- /** Heat (0..1) quantized to one of 5 density glyphs (Unicode blocks, or an ASCII fallback). */
6
- export declare function glyphFor(h: number, glyph: GlyphMode): string;
7
- export type HeatGrid = readonly (readonly number[])[];
8
- export declare function createGrid(rows: number, cols: number): HeatGrid;
9
- /**
10
- * Advance the grid one tick: heat flows up by exactly one row per call (row `y`'s new value comes
11
- * from the *previous* frame's row `y+1`, or `sources` for the bottom-most row), with a jittered
12
- * lateral drift and a jittered decay — both index/tick-hashed, so `advanceHeat` is a pure function
13
- * of its inputs (same grid + sources + tick always produces the same next grid).
14
- */
15
- export declare function advanceHeat(grid: HeatGrid, sources: readonly number[], tick: number): HeatGrid;
16
- export type Stage = 'compile' | 'assets' | 'upload' | 'activate' | 'live';
17
- /** x-fraction (0..1) along the band each pipeline stage's milestone tick sits at. */
18
- export declare const MILESTONES: Readonly<Record<Stage, number>>;
19
- export declare const MILESTONE_ORDER: readonly Stage[];
20
- /**
21
- * The stage currently in flight (used to pick the flavor line): the window between two milestones
22
- * belongs to the *later* one — e.g. `[0.16, 0.30)` is "checking assets", not "still compiling" —
23
- * so each threshold below is the *previous* milestone, not the stage's own.
24
- */
25
- export declare function stageForProgress(progress: number): Stage;
26
- /** The column a milestone x-fraction lands on for a given band width. */
27
- export declare function milestoneCol(position: number, cols: number): number;
28
- /** A milestone-crossing flare: a short-lived additive heat boost centered on `col`, aging each tick. */
29
- export interface FlareState {
30
- readonly col: number;
31
- readonly ageTicks: number;
32
- }
33
- /** ~1.1s of decay at `FRAME_MS` — exported so tests and the renderer share one duration. */
34
- export declare const FLARE_DURATION_TICKS: number;
35
- export type FirePhase = 'progress' | 'success' | 'failure';
36
- export interface FireZoneState {
37
- readonly cols: number;
38
- readonly tick: number;
39
- /**
40
- * Ticks since the current end phase (`success`/`failure`) began — NOT the global tick. The failure
41
- * spread is computed from this, so a deploy that dies late still bleeds rose outward gradually
42
- * instead of instantly owning the band. Defaults to 0 when omitted (spread not yet started).
43
- */
44
- readonly phaseTick?: number;
45
- readonly frontCol: number;
46
- readonly phase: FirePhase;
47
- /** Column the deploy died at; only meaningful (and required) once `phase === 'failure'`. */
48
- readonly deathCol?: number;
49
- readonly flare?: FlareState;
50
- }
51
- /** Heat sources injected on the virtual row below the band, one per column, for this tick's zone state. */
52
- export declare function sourcesFor(state: FireZoneState): number[];
53
- export interface FireFrameInput {
54
- readonly grid: HeatGrid;
55
- readonly cols: number;
56
- readonly progress: number;
57
- readonly mode: FireMode;
58
- readonly color: ColorMode;
59
- readonly glyph: GlyphMode;
60
- /** Pre-rendered ✔-checklist lines drawn above the band (the approved layout), or omit. */
61
- readonly headerLines?: readonly string[];
62
- /** Pre-rendered flavor line, or `undefined` to omit it entirely. */
63
- readonly flavorLine?: string;
64
- /** Pre-rendered "press enter to close" hint, or `undefined` while not yet holding. */
65
- readonly dismissHint?: string;
66
- }
67
- /**
68
- * The labeled stage track from the approved frames: `compile —— assets —— upload ——— activate —— live`,
69
- * label starts anchored to their milestone columns, dashes filling the gaps. Passed stages read amber,
70
- * the road ahead dim.
71
- */
72
- export declare function renderStageLabelRow(progress: number, cols: number, color: ColorMode, glyph: GlyphMode): string;
73
- /** Build one frame's lines: checklist header, grid rows, milestone ticks + labels, flavor/dismiss. */
74
- export declare function renderFireFrame(input: FireFrameInput): readonly string[];
75
- export interface DeployFireOptions {
76
- readonly stream: NodeJS.WriteStream;
77
- /** Enables Enter/Ctrl-C dismissal on success; failure and non-TTY runs settle immediately. */
78
- readonly stdin?: (NodeJS.ReadStream & {
79
- isRaw?: boolean;
80
- }) | undefined;
81
- readonly frameMs?: number;
82
- readonly rows?: number;
83
- readonly cols?: number;
84
- /** Entrypoint shown on the checklist's compile line (e.g. `server.ts`). */
85
- readonly entrypointLabel?: string;
86
- }
87
- export interface DeployFireController {
88
- /** Advance progress along the pipeline (0..1); crossing a milestone pushes a decaying flare. */
89
- setProgress(progress: number): void;
90
- /** Green end-state + simmer + hold; `lines` join the ✔ checklist (e.g. Activated / Live: url). */
91
- succeed(opts?: {
92
- readonly lines?: readonly string[];
93
- }): Promise<void>;
94
- /** Final rose frame; `line` becomes the ✗ checklist entry before recovery output prints. */
95
- fail(opts?: {
96
- readonly line?: string;
97
- }): Promise<void>;
98
- /** Stop immediately with no end-state and no hold (e.g. pausing for an interactive secret prompt). */
99
- stop(): void;
100
- }
101
- /**
102
- * Drive the fire band on `stream`. Assumes the caller has already decided this run is eligible
103
- * (interactive, colored, not `--json`, not `NOODLE_NO_FIRE`) — this function independently derives
104
- * color from `stream` too, and renders nothing at all when that resolves to `'none'`, so it is safe
105
- * to call directly in tests without re-deriving the outer gate.
106
- */
107
- export declare function runDeployFire(opts: DeployFireOptions): DeployFireController;
108
- //# sourceMappingURL=fire.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fire.d.ts","sourceRoot":"","sources":["../src/fire.ts"],"names":[],"mappings":"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"}
package/dist/fire.js DELETED
@@ -1,511 +0,0 @@
1
- /**
2
- * The `noodle deploy` fire animation: a cell-grid heat band (classic demoscene fire propagation)
3
- * whose x-axis maps deploy progress. Split in two halves, both in this one file per the approved
4
- * design: a pure engine (heat propagation, zone sourcing, palette, glyph quantization — fully
5
- * unit-testable without a TTY) and a renderer (`runDeployFire`) that repaints it in place on a
6
- * real stream, reusing the HIDE_CURSOR/cursorUp/CLEAR_EOL conventions `watch.ts`/`status.ts`
7
- * already established.
8
- *
9
- * Semantics: position = pipeline. `MILESTONES` places compile/assets/upload/activate/live at fixed
10
- * x-fractions, drawn as tick marks on a track row under the band (bright once passed, dim ahead).
11
- * Settled territory burns as a calm ember bed; the 7-column working front is a blazing crest;
12
- * territory ahead stays dark except for faint sparks within 2 columns of the front. Crossing a
13
- * milestone pushes a short decaying flare. An indeterminate wait (e.g. service-side activation)
14
- * holds the front in place and burns there — it never fake-advances.
15
- *
16
- * Success converts the whole bed to a simmering green fire and, on a full interactive TTY
17
- * (`stdin.isTTY` as well as the color stream), holds until the user dismisses it (Enter or Ctrl-C).
18
- * Failure paints a final rose frame and returns immediately so the caller's recovery diagnostics are
19
- * never gated behind animation dismissal. Headless/non-TTY runs always settle immediately.
20
- *
21
- * Determinism: every stochastic-looking element (flicker, drift, decay, sparks) is derived from a
22
- * cheap integer hash of `(x, y, tick)` — never `Math.random` — so the same tick always paints the
23
- * same grid and the whole suite can run under fake timers with no real waits.
24
- */
25
- import { emitKeypressEvents } from 'node:readline';
26
- import { dimText } from './commands/resource-shared.js';
27
- import { createFlavorSeed, renderFlavorLine } from './flavor.js';
28
- import { AMBER, CLEAR_EOL, cursorUp, detectColorMode, detectGlyphMode, HIDE_CURSOR, mix, ORANGE, paint, ROSE, SHOW_CURSOR, } from './gradient.js';
29
- const CR = String.fromCharCode(13);
30
- const NL = String.fromCharCode(10);
31
- /** Repaint cadence — matches `status.ts`'s spinner `FRAME_MS` so the two animations feel like one system. */
32
- const FRAME_MS = 80;
33
- const DEFAULT_ROWS = 7;
34
- const DEFAULT_COLS = 80;
35
- const MIN_COLS = 20;
36
- const GREEN = [34, 197, 94];
37
- const WARM_STOPS = [[26, 10, 10], ROSE, ORANGE, AMBER, [255, 244, 210]];
38
- const GREEN_STOPS = [[6, 24, 14], GREEN, [214, 255, 224]];
39
- const ROSE_STOPS = [[30, 8, 14], ROSE, [255, 214, 224]];
40
- function stopsFor(mode) {
41
- if (mode === 'green')
42
- return GREEN_STOPS;
43
- if (mode === 'rose')
44
- return ROSE_STOPS;
45
- return WARM_STOPS;
46
- }
47
- /** Heat (0..1) through the mode's gradient stops, evenly spaced and linearly interpolated. */
48
- export function fireColor(h, mode) {
49
- const stops = stopsFor(mode);
50
- const clamped = clamp01(h);
51
- const segment = 1 / (stops.length - 1);
52
- const index = Math.min(stops.length - 2, Math.floor(clamped / segment));
53
- const t = (clamped - index * segment) / segment;
54
- return mix(stops[index], stops[index + 1], t);
55
- }
56
- const GLYPHS_UNICODE = [' ', '░', '▒', '▓', '█'];
57
- const GLYPHS_ASCII = [' ', '.', ':', '*', '#'];
58
- /** Heat (0..1) quantized to one of 5 density glyphs (Unicode blocks, or an ASCII fallback). */
59
- export function glyphFor(h, glyph) {
60
- const idx = Math.min(4, Math.floor(clamp01(h) * 5));
61
- return (glyph === 'ascii' ? GLYPHS_ASCII : GLYPHS_UNICODE)[idx];
62
- }
63
- function clamp01(v) {
64
- return v < 0 ? 0 : v > 1 ? 1 : v;
65
- }
66
- function clampInt(v, lo, hi) {
67
- return v < lo ? lo : v > hi ? hi : v;
68
- }
69
- // --- deterministic noise -------------------------------------------------------------------------
70
- /** Cheap deterministic 2D+time hash to [0,1) — never `Math.random`, so a tick always repaints identically. */
71
- function hash01(x, y, tick) {
72
- let h = (x * 374761393 + y * 668265263 + tick * 974634) | 0;
73
- h = Math.imul(h ^ (h >>> 13), 1274126177);
74
- h ^= h >>> 16;
75
- return (h >>> 0) / 4294967295;
76
- }
77
- export function createGrid(rows, cols) {
78
- return Array.from({ length: rows }, () => new Array(cols).fill(0));
79
- }
80
- const MIN_DECAY = 0.08;
81
- const DECAY_JITTER = 0.07;
82
- /**
83
- * Advance the grid one tick: heat flows up by exactly one row per call (row `y`'s new value comes
84
- * from the *previous* frame's row `y+1`, or `sources` for the bottom-most row), with a jittered
85
- * lateral drift and a jittered decay — both index/tick-hashed, so `advanceHeat` is a pure function
86
- * of its inputs (same grid + sources + tick always produces the same next grid).
87
- */
88
- export function advanceHeat(grid, sources, tick) {
89
- const rows = grid.length;
90
- const cols = sources.length;
91
- const next = new Array(rows);
92
- for (let y = 0; y < rows; y++) {
93
- const below = y === rows - 1 ? sources : grid[y + 1];
94
- const row = new Array(cols);
95
- for (let x = 0; x < cols; x++) {
96
- const drift = Math.floor(hash01(x, y, tick) * 3) - 1; // -1, 0, or 1
97
- const sx = clampInt(x + drift, 0, cols - 1);
98
- const decay = MIN_DECAY + hash01(x, y + 101, tick) * DECAY_JITTER;
99
- row[x] = clamp01(below[sx] - decay);
100
- }
101
- next[y] = row;
102
- }
103
- return next;
104
- }
105
- /** x-fraction (0..1) along the band each pipeline stage's milestone tick sits at. */
106
- export const MILESTONES = {
107
- compile: 0.16,
108
- assets: 0.3,
109
- upload: 0.68,
110
- activate: 0.88,
111
- live: 1,
112
- };
113
- export const MILESTONE_ORDER = [
114
- 'compile',
115
- 'assets',
116
- 'upload',
117
- 'activate',
118
- 'live',
119
- ];
120
- /**
121
- * The stage currently in flight (used to pick the flavor line): the window between two milestones
122
- * belongs to the *later* one — e.g. `[0.16, 0.30)` is "checking assets", not "still compiling" —
123
- * so each threshold below is the *previous* milestone, not the stage's own.
124
- */
125
- export function stageForProgress(progress) {
126
- if (progress >= MILESTONES.live)
127
- return 'live';
128
- if (progress >= MILESTONES.upload)
129
- return 'activate'; // [0.68, 1.0): sending + awaiting activation
130
- if (progress >= MILESTONES.assets)
131
- return 'upload';
132
- if (progress >= MILESTONES.compile)
133
- return 'assets';
134
- return 'compile';
135
- }
136
- /** The column a milestone x-fraction lands on for a given band width. */
137
- export function milestoneCol(position, cols) {
138
- return Math.round(clamp01(position) * Math.max(1, cols - 1));
139
- }
140
- /** ~1.1s of decay at `FRAME_MS` — exported so tests and the renderer share one duration. */
141
- export const FLARE_DURATION_TICKS = Math.round(1100 / FRAME_MS);
142
- const FLARE_RADIUS_COLS = 4;
143
- const FLARE_PEAK = 0.35;
144
- function flareBoost(x, flare) {
145
- if (flare === undefined || flare.ageTicks > FLARE_DURATION_TICKS)
146
- return 0;
147
- const distance = Math.abs(x - flare.col);
148
- if (distance > FLARE_RADIUS_COLS)
149
- return 0;
150
- const spatialFalloff = 1 - distance / FLARE_RADIUS_COLS;
151
- const timeFalloff = 1 - flare.ageTicks / FLARE_DURATION_TICKS;
152
- return FLARE_PEAK * spatialFalloff * timeFalloff;
153
- }
154
- const CREST_WIDTH = 7; // the working front: the 7 most-recently-covered columns
155
- const SPARK_AHEAD_COLS = 2;
156
- const EMBER_BASE = 0.3;
157
- const EMBER_AMPLITUDE = 0.06;
158
- const CREST_BASE = 0.78;
159
- const CREST_FLICKER = 0.22;
160
- const SIMMER_BASE = 0.42; // success simmer
161
- const FAILURE_SIMMER_BASE = 0.4;
162
- const SIMMER_AMPLITUDE = 0.06;
163
- // Slowly-evolving taller "structures" over the low bed — the approved frames show block clusters
164
- // rising from a 1-2 row ember baseline, not a uniform wall. Cluster identity comes from a coarse
165
- // column bucket and a slow tick bucket, so shapes hold for ~5s then drift.
166
- const CLUSTER_SPAN_COLS = 6;
167
- const CLUSTER_TICKS = 64;
168
- const CLUSTER_BOOST = 0.34;
169
- function clusterBoost(x, tick, salt) {
170
- const shape = hash01(Math.floor(x / CLUSTER_SPAN_COLS), salt, Math.floor(tick / CLUSTER_TICKS));
171
- return shape > 0.55 ? CLUSTER_BOOST * ((shape - 0.55) / 0.45) : 0;
172
- }
173
- const FAILURE_CREST_BASE = 0.8;
174
- const FAILURE_CREST_FLICKER = 0.15;
175
- const FAILURE_DIM = 0.25;
176
- const FAILURE_SPREAD_COLS_PER_TICK = 1.5;
177
- /** Heat sources injected on the virtual row below the band, one per column, for this tick's zone state. */
178
- export function sourcesFor(state) {
179
- const { cols, tick, frontCol, phase } = state;
180
- const out = new Array(cols);
181
- if (phase === 'success') {
182
- for (let x = 0; x < cols; x++) {
183
- out[x] = clamp01(SIMMER_BASE +
184
- SIMMER_AMPLITUDE * Math.sin(tick * 0.04 + x * 0.2) +
185
- clusterBoost(x, tick, 13));
186
- }
187
- return out;
188
- }
189
- if (phase === 'failure') {
190
- const deathCol = state.deathCol ?? frontCol;
191
- const spreadRadius = (state.phaseTick ?? 0) * FAILURE_SPREAD_COLS_PER_TICK;
192
- const fullyOwned = spreadRadius >= cols;
193
- for (let x = 0; x < cols; x++) {
194
- if (fullyOwned) {
195
- out[x] = clamp01(FAILURE_SIMMER_BASE +
196
- SIMMER_AMPLITUDE * Math.sin(tick * 0.04 + x * 0.2) +
197
- clusterBoost(x, tick, 17));
198
- continue;
199
- }
200
- const distance = Math.abs(x - deathCol);
201
- out[x] =
202
- distance <= spreadRadius
203
- ? clamp01(FAILURE_CREST_BASE + FAILURE_CREST_FLICKER * hash01(x, 5, tick))
204
- : FAILURE_DIM;
205
- }
206
- return out;
207
- }
208
- for (let x = 0; x < cols; x++) {
209
- let base;
210
- if (x <= frontCol && frontCol - x < CREST_WIDTH) {
211
- base = CREST_BASE + CREST_FLICKER * hash01(x, 1, tick);
212
- }
213
- else if (x < frontCol) {
214
- base =
215
- EMBER_BASE + EMBER_AMPLITUDE * Math.sin(tick * 0.05 + x * 0.3) + clusterBoost(x, tick, 11);
216
- }
217
- else if (x - frontCol <= SPARK_AHEAD_COLS) {
218
- base = hash01(x, 2, tick) > 0.82 ? 0.3 * hash01(x, 3, tick) : 0;
219
- }
220
- else {
221
- base = 0;
222
- }
223
- out[x] = clamp01(base + flareBoost(x, state.flare));
224
- }
225
- return out;
226
- }
227
- function renderGridLine(row, mode, color, glyph) {
228
- let out = '';
229
- for (const heat of row) {
230
- const ch = glyphFor(heat, glyph);
231
- out += color === 'none' ? ch : paint(fireColor(heat, mode), ch, color);
232
- }
233
- return out;
234
- }
235
- const DIM_GRAY = [115, 115, 115];
236
- function renderMilestoneRow(progress, cols, color, glyph) {
237
- const tick = glyph === 'ascii' ? '+' : '┴';
238
- const cells = new Array(cols).fill(' ');
239
- for (const stage of MILESTONE_ORDER) {
240
- const position = MILESTONES[stage];
241
- const col = milestoneCol(position, cols);
242
- const passed = progress >= position;
243
- cells[col] = color === 'none' ? tick : paint(passed ? AMBER : DIM_GRAY, tick, color);
244
- }
245
- return cells.join('');
246
- }
247
- /**
248
- * The labeled stage track from the approved frames: `compile —— assets —— upload ——— activate —— live`,
249
- * label starts anchored to their milestone columns, dashes filling the gaps. Passed stages read amber,
250
- * the road ahead dim.
251
- */
252
- export function renderStageLabelRow(progress, cols, color, glyph) {
253
- const dash = glyph === 'ascii' ? '-' : '─';
254
- const cells = Array.from({ length: cols }, () => ({
255
- ch: dash,
256
- passed: undefined,
257
- }));
258
- for (const stage of MILESTONE_ORDER) {
259
- const position = MILESTONES[stage];
260
- // Anchor each label so it ENDS near its milestone column (the final 'live' label must fit).
261
- const col = milestoneCol(position, cols);
262
- const start = clampInt(stage === 'live' ? col - stage.length + 1 : Math.max(0, col - Math.floor(stage.length / 2)), 0, Math.max(0, cols - stage.length));
263
- const passed = progress >= position;
264
- // One space of breathing room around each label.
265
- if (start > 0)
266
- cells[start - 1] = { ch: ' ', passed: undefined };
267
- for (let i = 0; i < stage.length && start + i < cols; i++) {
268
- cells[start + i] = { ch: stage[i], passed };
269
- }
270
- if (start + stage.length < cols)
271
- cells[start + stage.length] = { ch: ' ', passed: undefined };
272
- }
273
- if (color === 'none')
274
- return cells.map((cell) => cell.ch).join('');
275
- return cells
276
- .map((cell) => cell.passed === undefined
277
- ? paint(DIM_GRAY, cell.ch, color)
278
- : paint(cell.passed ? AMBER : DIM_GRAY, cell.ch, color))
279
- .join('');
280
- }
281
- /** Build one frame's lines: checklist header, grid rows, milestone ticks + labels, flavor/dismiss. */
282
- export function renderFireFrame(input) {
283
- const lines = [];
284
- if (input.headerLines !== undefined)
285
- lines.push(...input.headerLines, '');
286
- lines.push(...input.grid.map((row) => renderGridLine(row, input.mode, input.color, input.glyph)));
287
- lines.push(renderMilestoneRow(input.progress, input.cols, input.color, input.glyph));
288
- if (input.flavorLine !== undefined)
289
- lines.push(input.flavorLine);
290
- lines.push(renderStageLabelRow(input.progress, input.cols, input.color, input.glyph));
291
- if (input.dismissHint !== undefined)
292
- lines.push(input.dismissHint);
293
- return lines;
294
- }
295
- function inertController() {
296
- return {
297
- setProgress: () => { },
298
- succeed: async () => { },
299
- fail: async () => { },
300
- stop: () => { },
301
- };
302
- }
303
- /**
304
- * Drive the fire band on `stream`. Assumes the caller has already decided this run is eligible
305
- * (interactive, colored, not `--json`, not `NOODLE_NO_FIRE`) — this function independently derives
306
- * color from `stream` too, and renders nothing at all when that resolves to `'none'`, so it is safe
307
- * to call directly in tests without re-deriving the outer gate.
308
- */
309
- export function runDeployFire(opts) {
310
- const stream = opts.stream;
311
- const color = detectColorMode(stream);
312
- if (color === 'none')
313
- return inertController();
314
- const glyph = detectGlyphMode();
315
- const frameMs = opts.frameMs ?? FRAME_MS;
316
- const rows = opts.rows ?? DEFAULT_ROWS;
317
- const cols = opts.cols ?? (typeof stream.columns === 'number' ? stream.columns : DEFAULT_COLS);
318
- // A band narrower than MIN_COLS can't hold the milestone track, and rows longer than the real
319
- // terminal would soft-wrap — breaking repaintBand's move-up-by-line-count math into garbage.
320
- // Too narrow to draw honestly = don't draw at all.
321
- if (cols < MIN_COLS)
322
- return inertController();
323
- const stdin = opts.stdin;
324
- const flavorSeed = createFlavorSeed();
325
- const entrypointLabel = opts.entrypointLabel ?? 'server.ts';
326
- let grid = createGrid(rows, cols);
327
- let tick = 0;
328
- let progress = 0;
329
- let phase = 'progress';
330
- let phaseStartTick = 0; // tick at which the current end phase (success/failure) began
331
- let deathCol;
332
- let flare;
333
- let holding = false;
334
- let previousLineCount = 0;
335
- let stopped = false;
336
- let successLines = [];
337
- let failLine;
338
- const flaredStages = new Set();
339
- // The ✔ checklist above the band (approved layout): milestone crossings append lines; the
340
- // indeterminate activate wait shows as a pending line until the end state replaces it.
341
- const glyphs = detectGlyphMode() === 'ascii'
342
- ? { prompt: '>', ok: '+', pending: '~', bad: 'x' }
343
- : { prompt: '❯', ok: '✔', pending: '⠿', bad: '✗' };
344
- // `color` is already narrowed past the top inert guard — a 'none' stream never reaches here.
345
- const tint = (rgb, text) => paint(rgb, text, color);
346
- // Clamp every checklist line to the band width: a long error message or endpoint URL that
347
- // soft-wraps would break the cursor-up repaint math (CodeRabbit, fire-fix review).
348
- const clampLine = (text) => [...text].length > cols - 2 ? `${[...text].slice(0, cols - 3).join('')}…` : text;
349
- const headerLines = () => {
350
- const lines = [`${tint(ORANGE, glyphs.prompt)} noodle deploy`];
351
- const done = (text) => lines.push(`${tint(GREEN, glyphs.ok)} ${clampLine(text)}`);
352
- if (progress >= MILESTONES.compile)
353
- done(`Compiled ${entrypointLabel}`);
354
- if (progress >= MILESTONES.assets)
355
- done('Assets verified');
356
- if (progress >= MILESTONES.upload)
357
- done('Uploaded');
358
- if (phase === 'success') {
359
- for (const line of successLines.length > 0 ? successLines : ['Activated (atomic)']) {
360
- done(line);
361
- }
362
- }
363
- else if (phase === 'failure') {
364
- lines.push(`${tint(ROSE, glyphs.bad)} ${clampLine(failLine ?? 'Deploy failed')}`);
365
- }
366
- else if (progress >= MILESTONES.upload) {
367
- lines.push(tint(DIM_GRAY, `${glyphs.pending} Activating on Noodle Seed Cloud…`));
368
- }
369
- return lines;
370
- };
371
- const currentFlavorStage = () => phase === 'failure' ? 'failed' : phase === 'success' ? 'live' : stageForProgress(progress);
372
- const paintFrame = () => {
373
- tick += 1;
374
- if (flare !== undefined) {
375
- const aged = { col: flare.col, ageTicks: flare.ageTicks + 1 };
376
- flare = aged.ageTicks > FLARE_DURATION_TICKS ? undefined : aged;
377
- }
378
- const frontCol = milestoneCol(Math.min(progress, 1), cols);
379
- const zone = {
380
- cols,
381
- tick,
382
- frontCol,
383
- phase,
384
- ...(phase !== 'progress' ? { phaseTick: tick - phaseStartTick } : {}),
385
- ...(deathCol !== undefined ? { deathCol } : {}),
386
- ...(flare !== undefined ? { flare } : {}),
387
- };
388
- grid = advanceHeat(grid, sourcesFor(zone), tick);
389
- const mode = phase === 'success' ? 'green' : phase === 'failure' ? 'rose' : 'warm';
390
- const lines = renderFireFrame({
391
- grid,
392
- cols,
393
- progress,
394
- mode,
395
- color,
396
- glyph,
397
- headerLines: headerLines(),
398
- flavorLine: renderFlavorLine(currentFlavorStage(), flavorSeed, color, cols),
399
- ...(holding ? { dismissHint: dimText('press enter to close', stream) } : {}),
400
- });
401
- previousLineCount = repaintBand(stream, previousLineCount, lines);
402
- };
403
- stream.write(HIDE_CURSOR);
404
- paintFrame();
405
- const timer = setInterval(paintFrame, frameMs);
406
- timer.unref?.();
407
- const teardownOutput = () => {
408
- if (stopped)
409
- return;
410
- stopped = true;
411
- clearInterval(timer);
412
- stream.write(SHOW_CURSOR);
413
- };
414
- // Set only while `holdUntilDismiss` is waiting on a keypress: lets `stop()` (or a SIGINT during a
415
- // hold) run the hold's full cleanup — detach listeners, restore raw mode, resolve the promise —
416
- // instead of leaving the terminal in raw mode with a forever-pending hold.
417
- let finishHold;
418
- const teardown = () => {
419
- finishHold?.();
420
- teardownOutput();
421
- };
422
- const setProgress = (next) => {
423
- if (stopped || phase !== 'progress')
424
- return;
425
- const clamped = clamp01(next);
426
- for (const stage of MILESTONE_ORDER) {
427
- if (clamped >= MILESTONES[stage] && !flaredStages.has(stage)) {
428
- flaredStages.add(stage);
429
- flare = { col: milestoneCol(MILESTONES[stage], cols), ageTicks: 0 };
430
- }
431
- }
432
- progress = clamped;
433
- };
434
- const holdUntilDismiss = () => {
435
- if (stopped)
436
- return Promise.resolve();
437
- if (stdin?.isTTY !== true) {
438
- teardown();
439
- return Promise.resolve();
440
- }
441
- holding = true;
442
- paintFrame();
443
- const input = stdin;
444
- return new Promise((resolve) => {
445
- const wasRaw = input.isRaw === true;
446
- emitKeypressEvents(input);
447
- input.setRawMode?.(true);
448
- let settled = false;
449
- const onKey = (_s, key = {}) => {
450
- if (key.name === 'return' ||
451
- key.name === 'enter' ||
452
- (key.ctrl === true && key.name === 'c')) {
453
- finish();
454
- }
455
- };
456
- const onSigint = () => finish();
457
- function finish() {
458
- if (settled)
459
- return;
460
- settled = true;
461
- finishHold = undefined;
462
- input.off('keypress', onKey);
463
- process.off('SIGINT', onSigint);
464
- input.setRawMode?.(wasRaw);
465
- teardownOutput();
466
- resolve();
467
- }
468
- finishHold = finish;
469
- input.on('keypress', onKey);
470
- process.once('SIGINT', onSigint);
471
- });
472
- };
473
- return {
474
- setProgress,
475
- succeed: async (opts = {}) => {
476
- if (stopped)
477
- return;
478
- if (opts.lines !== undefined)
479
- successLines = opts.lines;
480
- phase = 'success';
481
- phaseStartTick = tick;
482
- progress = 1;
483
- await holdUntilDismiss();
484
- },
485
- fail: async (opts = {}) => {
486
- if (stopped)
487
- return;
488
- if (opts.line !== undefined)
489
- failLine = opts.line;
490
- deathCol = milestoneCol(Math.min(progress, 1), cols);
491
- phase = 'failure';
492
- phaseStartTick = tick;
493
- paintFrame();
494
- teardownOutput();
495
- },
496
- stop: () => teardown(),
497
- };
498
- }
499
- /**
500
- * Repaint in place: move the cursor back up over the previous frame and redraw every line. Unlike
501
- * `watch.ts`'s `repaint`, this never needs to blank stale trailing lines — a fire frame only ever
502
- * grows (the dismiss hint appends one line once) and never shrinks.
503
- */
504
- function repaintBand(stream, previousLineCount, lines) {
505
- let out = previousLineCount > 0 ? cursorUp(previousLineCount) : '';
506
- for (const line of lines)
507
- out += `${CR}${line}${CLEAR_EOL}${NL}`;
508
- stream.write(out);
509
- return lines.length;
510
- }
511
- //# sourceMappingURL=fire.js.map