@spexcode/spec-core 0.6.2 → 0.6.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.
Files changed (56) hide show
  1. package/dist/anchors.d.ts +94 -0
  2. package/dist/anchors.js +730 -0
  3. package/dist/git.d.ts +166 -0
  4. package/dist/git.js +2736 -0
  5. package/dist/graph.d.ts +34 -0
  6. package/dist/graph.js +237 -0
  7. package/dist/graphDelta.d.ts +45 -0
  8. package/dist/graphDelta.js +84 -0
  9. package/dist/harness-identity.d.ts +31 -0
  10. package/dist/harness-identity.js +20 -0
  11. package/dist/identity-presets.d.ts +152 -0
  12. package/dist/identity-presets.js +132 -0
  13. package/dist/index.d.ts +45 -0
  14. package/dist/index.js +19 -0
  15. package/dist/layout.d.ts +183 -0
  16. package/dist/layout.js +548 -0
  17. package/dist/process-identity.d.ts +37 -0
  18. package/dist/process-identity.js +214 -0
  19. package/dist/project-identity.d.ts +12 -0
  20. package/dist/project-identity.js +71 -0
  21. package/dist/project-store.d.ts +3 -0
  22. package/dist/project-store.js +14 -0
  23. package/dist/resilience.d.ts +2 -0
  24. package/dist/resilience.js +40 -0
  25. package/dist/review/index.d.ts +3 -0
  26. package/{src → dist}/review/index.js +3 -3
  27. package/dist/review/reviewFilters.d.ts +77 -0
  28. package/dist/review/reviewFilters.js +308 -0
  29. package/dist/review/reviewQuery.d.ts +66 -0
  30. package/dist/review/reviewQuery.js +180 -0
  31. package/dist/review/session.d.ts +4 -0
  32. package/dist/review/session.js +8 -0
  33. package/dist/reviewSnapshot.d.ts +15 -0
  34. package/dist/reviewSnapshot.js +12 -0
  35. package/dist/root-lru.d.ts +4 -0
  36. package/{src/root-lru.ts → dist/root-lru.js} +26 -30
  37. package/dist/specs.d.ts +117 -0
  38. package/dist/specs.js +489 -0
  39. package/package.json +19 -8
  40. package/src/anchors.ts +0 -728
  41. package/src/git.ts +0 -2556
  42. package/src/graph.ts +0 -251
  43. package/src/harness-identity.ts +0 -26
  44. package/src/identity-presets.d.ts +0 -13
  45. package/src/identity-presets.js +0 -138
  46. package/src/index.ts +0 -20
  47. package/src/layout.ts +0 -637
  48. package/src/process-identity.ts +0 -207
  49. package/src/project-identity.ts +0 -73
  50. package/src/project-store.ts +0 -17
  51. package/src/resilience.ts +0 -41
  52. package/src/review/reviewFilters.js +0 -324
  53. package/src/review/reviewQuery.js +0 -174
  54. package/src/review/session.js +0 -13
  55. package/src/reviewSnapshot.ts +0 -28
  56. package/src/specs.ts +0 -498
@@ -0,0 +1,132 @@
1
+ // One browser-safe identity registry shared by backend validation and every dashboard projection.
2
+ // Geometry is data so the React renderer and favicon serializer cannot drift into separate drawings.
3
+ //
4
+ // @@@ shape keys travel through two renderers - a shape's own attributes override the group's
5
+ // fill/stroke, which is how a multi-colour mark fits a single-fg registry. Both consumers pass the keys
6
+ // through verbatim: the string serializer spreads them as attributes, React spreads them as JSX props.
7
+ // So a key must be valid in BOTH, which rules out hyphenated names (React wants strokeWidth) - hence a
8
+ // painted seam outline rather than a stroked line, since per-shape stroke-width cannot be expressed.
9
+ export const DEFAULT_PROJECT_ICON = 'spexcode';
10
+ export const DEFAULT_GATEWAY_ICON = 'gateway';
11
+ export const IDENTITY_PRESETS = Object.freeze([
12
+ {
13
+ // The brand mark: two brackets holding one file, cleft corner to corner and regenerated on the
14
+ // upper-right of the cleft. Painted rather than stroked — see the paint note below. Geometry is
15
+ // docs/brand/mark-dark.svg scaled by 24/512; the two surface gradients flatten to their midpoints
16
+ // because a 24-unit chip has no room for a ramp and the format carries no defs.
17
+ id: 'spexcode', label: 'SpexCode', bg: '#12161C', fg: '#EFE8D8',
18
+ shapes: [
19
+ { tag: 'path', d: 'M 4.500,4.500 L 9.750,4.500 L 9.750,6.656 L 6.656,6.656 L 6.656,17.344 L 9.750,17.344 L 9.750,19.500 L 4.500,19.500 Z', fill: '#EFE8D8', stroke: 'none' },
20
+ { tag: 'path', d: 'M 19.500,4.500 L 14.250,4.500 L 14.250,6.656 L 17.344,6.656 L 17.344,17.344 L 14.250,17.344 L 14.250,19.500 L 19.500,19.500 Z', fill: '#EFE8D8', stroke: 'none' },
21
+ { tag: 'path', d: 'M 15.281,15.281 L 11.731,13.057 L 12.269,10.943 L 8.719,8.719 L 8.719,15.281 Z', fill: '#327D95', stroke: 'none' },
22
+ { tag: 'path', d: 'M 8.719,8.719 L 12.269,10.943 L 11.731,13.057 L 15.281,15.281 L 15.281,12.000 L 12.000,8.719 Z', fill: '#82C3D6', stroke: 'none' },
23
+ { tag: 'path', d: 'M 8.569,8.957 L 11.946,11.073 L 11.408,13.186 L 15.132,15.520 L 15.431,15.043 L 12.054,12.927 L 12.592,10.814 L 8.868,8.480 Z', fill: '#E4F7FD', stroke: 'none' },
24
+ ],
25
+ },
26
+ {
27
+ id: 'gateway', label: 'Gateway', bg: '#155e75', fg: '#ecfeff',
28
+ shapes: [
29
+ { tag: 'path', d: 'm12 3 8 4.2-8 4.2-8-4.2Z' },
30
+ { tag: 'path', d: 'm4 11.2 8 4.2 8-4.2' },
31
+ { tag: 'path', d: 'm4 15.2 8 4.2 8-4.2' },
32
+ ],
33
+ },
34
+ {
35
+ id: 'mdi:rocket-launch', label: 'Rocket', bg: '#9f1239', fg: '#fff1f2',
36
+ shapes: [
37
+ { tag: 'path', d: 'M14.5 5.2c2.2-2.2 4.8-2 5.3-1.8.2.5.4 3.1-1.8 5.3l-5.5 5.5-4.2-4.2Z' },
38
+ { tag: 'path', d: 'm11.2 6.8-4.1.6-2.5 2.5 4.1.7' },
39
+ { tag: 'path', d: 'm16.4 12-1 4.7-2.5 2.5-.7-4.1' },
40
+ { tag: 'circle', cx: 16.1, cy: 7.1, r: 1.2 },
41
+ { tag: 'path', d: 'M7.6 14.6c-2.2.4-3.4 1.6-3.6 3.8 2.2-.2 3.4-1.4 3.8-3.6' },
42
+ ],
43
+ },
44
+ {
45
+ id: 'compass', label: 'Compass', bg: '#1d4ed8', fg: '#eff6ff',
46
+ shapes: [
47
+ { tag: 'circle', cx: 12, cy: 12, r: 8.5 },
48
+ { tag: 'path', d: 'm15.2 8.8-1.8 4.6-4.6 1.8 1.8-4.6Z' },
49
+ ],
50
+ },
51
+ {
52
+ id: 'terminal', label: 'Terminal', bg: '#3f3f46', fg: '#fafafa',
53
+ shapes: [
54
+ { tag: 'rect', x: 3.5, y: 4.5, width: 17, height: 15, rx: 2 },
55
+ { tag: 'path', d: 'm7 9 3 3-3 3' },
56
+ { tag: 'path', d: 'M12.5 15H17' },
57
+ ],
58
+ },
59
+ {
60
+ id: 'package', label: 'Package', bg: '#6d28d9', fg: '#f5f3ff',
61
+ shapes: [
62
+ { tag: 'path', d: 'm12 3 8 4.5v9L12 21l-8-4.5v-9Z' },
63
+ { tag: 'path', d: 'm4.3 7.7 7.7 4.4 7.7-4.4' },
64
+ { tag: 'path', d: 'M12 12.1V21' },
65
+ ],
66
+ },
67
+ {
68
+ id: 'database', label: 'Database', bg: '#a16207', fg: '#fefce8',
69
+ shapes: [
70
+ { tag: 'ellipse', cx: 12, cy: 6, rx: 7.5, ry: 3 },
71
+ { tag: 'path', d: 'M4.5 6v6c0 1.7 3.4 3 7.5 3s7.5-1.3 7.5-3V6' },
72
+ { tag: 'path', d: 'M4.5 12v6c0 1.7 3.4 3 7.5 3s7.5-1.3 7.5-3v-6' },
73
+ ],
74
+ },
75
+ {
76
+ id: 'spark', label: 'Spark', bg: '#c2410c', fg: '#fff7ed',
77
+ shapes: [
78
+ { tag: 'path', d: 'm12 3 1.5 5.2L19 10l-5.5 1.8L12 17l-1.5-5.2L5 10l5.5-1.8Z' },
79
+ { tag: 'path', d: 'm18.5 15 .7 2.2 2.3.8-2.3.8-.7 2.2-.7-2.2-2.3-.8 2.3-.8Z' },
80
+ ],
81
+ },
82
+ ]);
83
+ const BY_ID = new Map(IDENTITY_PRESETS.map((preset) => [preset.id, preset]));
84
+ const ICONIFY_ID = /^[a-z0-9-]+[:/][a-z0-9-]+$/i;
85
+ const ALIASES = new Map([
86
+ ['rocket', 'mdi:rocket-launch'],
87
+ ['mdi/rocket-launch', 'mdi:rocket-launch'],
88
+ ['layers', 'gateway'],
89
+ ['default', 'spexcode'],
90
+ ]);
91
+ export const IDENTITY_PRESET_IDS = Object.freeze(IDENTITY_PRESETS.map((preset) => preset.id));
92
+ export function resolvedIdentityIcon(value, fallback = DEFAULT_PROJECT_ICON) {
93
+ const raw = typeof value === 'string' ? value.trim() : '';
94
+ const id = ALIASES.get(raw) || raw;
95
+ return id || fallback;
96
+ }
97
+ export function identityPreset(value) {
98
+ const raw = typeof value === 'string' ? value.trim() : '';
99
+ return BY_ID.get(ALIASES.get(raw) || raw) || null;
100
+ }
101
+ export function isIconifyIcon(value) {
102
+ return ICONIFY_ID.test(typeof value === 'string' ? value.trim() : '');
103
+ }
104
+ export function requireIdentityChoice(value) {
105
+ const raw = typeof value === 'string' ? value.trim() : '';
106
+ const id = ALIASES.get(raw) || raw;
107
+ if (BY_ID.has(id))
108
+ return id;
109
+ if (isIconifyIcon(id))
110
+ return id.replace('/', ':');
111
+ throw new Error(`unknown identity icon '${raw}' (choose a preset or Iconify prefix:name)`);
112
+ }
113
+ function attrs(shape) {
114
+ return Object.entries(shape).filter(([key]) => key !== 'tag')
115
+ .map(([key, value]) => `${key === 'className' ? 'class' : key}="${String(value).replaceAll('&', '&').replaceAll('"', '"')}"`).join(' ');
116
+ }
117
+ export function identitySvg(value, fallback = DEFAULT_PROJECT_ICON) {
118
+ const preset = identityPreset(value) || identityPreset(fallback);
119
+ const geometry = preset.shapes.map((shape) => `<${shape.tag} ${attrs(shape)}/>`).join('');
120
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="1" y="1" width="22" height="22" rx="5" fill="${preset.bg}"/><g fill="none" stroke="${preset.fg}" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">${geometry}</g></svg>`;
121
+ }
122
+ export function identityFaviconHref(value, fallback = DEFAULT_PROJECT_ICON) {
123
+ const resolved = resolvedIdentityIcon(value, fallback);
124
+ if (identityPreset(resolved))
125
+ return `data:image/svg+xml,${encodeURIComponent(identitySvg(resolved, fallback))}`;
126
+ if (/^https?:\/\//.test(resolved))
127
+ return resolved;
128
+ if (isIconifyIcon(resolved))
129
+ return `https://api.iconify.design/${resolved.replace(':', '/')}.svg`;
130
+ const glyph = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text x="50" y=".86em" font-size="82" text-anchor="middle">${resolved.replaceAll('&', '&amp;').replaceAll('<', '&lt;')}</text></svg>`;
131
+ return `data:image/svg+xml,${encodeURIComponent(glyph)}`;
132
+ }
@@ -0,0 +1,45 @@
1
+ export declare function readSpecs(root: string): Promise<{
2
+ id: string;
3
+ parent: string | null;
4
+ path: string;
5
+ title: string;
6
+ status: import("./specs.js").DerivedStatus;
7
+ fmStatus: string | null;
8
+ session: string | null;
9
+ hue: number;
10
+ desc: string;
11
+ code: string[];
12
+ codeEntries: import("./anchors.js").RelationEntry[];
13
+ codeScoped: import("./anchors.js").RelationEntry[];
14
+ related: string[];
15
+ relatedEntries: import("./anchors.js").RelationEntry[];
16
+ relatedScoped: import("./anchors.js").RelationEntry[];
17
+ relationProblems: string[];
18
+ version: number;
19
+ reason: string;
20
+ lastEdited: string | null;
21
+ drift: number;
22
+ driftFiles: {
23
+ file: string;
24
+ behind: number;
25
+ }[];
26
+ relatedDriftFiles: {
27
+ file: string;
28
+ behind: number;
29
+ }[];
30
+ body: string;
31
+ parts: import("./specs.js").SpecParts | null;
32
+ }[]>;
33
+ export * from './anchors.js';
34
+ export * from './git.js';
35
+ export * from './graph.js';
36
+ export * from './graphDelta.js';
37
+ export * from './harness-identity.js';
38
+ export * from './layout.js';
39
+ export * from './process-identity.js';
40
+ export * from './project-identity.js';
41
+ export * from './project-store.js';
42
+ export * from './resilience.js';
43
+ export * from './reviewSnapshot.js';
44
+ export * from './root-lru.js';
45
+ export * from './specs.js';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import { sourceIndexesFull } from './git.js';
2
+ import { loadSpecs } from './specs.js';
3
+ export async function readSpecs(root) {
4
+ const [history, drift] = await sourceIndexesFull(root);
5
+ return loadSpecs(root, { history, drift });
6
+ }
7
+ export * from './anchors.js';
8
+ export * from './git.js';
9
+ export * from './graph.js';
10
+ export * from './graphDelta.js';
11
+ export * from './harness-identity.js';
12
+ export * from './layout.js';
13
+ export * from './process-identity.js';
14
+ export * from './project-identity.js';
15
+ export * from './project-store.js';
16
+ export * from './resilience.js';
17
+ export * from './reviewSnapshot.js';
18
+ export * from './root-lru.js';
19
+ export * from './specs.js';
@@ -0,0 +1,183 @@
1
+ import { type NodeOp } from './git.js';
2
+ import { type HarnessId } from './harness-identity.js';
3
+ export type Config = {
4
+ main?: string;
5
+ mainBranch?: string;
6
+ branchPrefix?: string;
7
+ preset?: string;
8
+ render?: string;
9
+ private?: boolean;
10
+ harnesses?: (string | {
11
+ plugin?: string;
12
+ })[];
13
+ dashboard?: {
14
+ apiUrl?: string;
15
+ title?: string;
16
+ icon?: string;
17
+ showHeadlessLaunchers?: boolean;
18
+ };
19
+ uploads?: {
20
+ maxBytes?: number;
21
+ chunkBytes?: number;
22
+ concurrency?: number;
23
+ requestTimeoutMs?: number;
24
+ retryLimit?: number;
25
+ retryDelayMs?: number;
26
+ incompleteTtlMs?: number;
27
+ cleanupIntervalMs?: number;
28
+ minFreeBytes?: number;
29
+ evidenceMaxBytes?: number;
30
+ };
31
+ sessions?: {
32
+ maxActive?: number;
33
+ launchers?: {
34
+ [name: string]: {
35
+ harness?: HarnessId;
36
+ cmd: string;
37
+ };
38
+ };
39
+ defaultLauncher?: string;
40
+ };
41
+ resources?: {
42
+ sessionRssMiB?: number;
43
+ backendRssMiB?: number;
44
+ idleCpuPercent?: number;
45
+ sampleMs?: number;
46
+ reportIntervalMs?: number;
47
+ };
48
+ serve?: {
49
+ public?: {
50
+ enabled?: boolean;
51
+ http?: boolean;
52
+ tls?: {
53
+ cert?: string;
54
+ key?: string;
55
+ };
56
+ };
57
+ };
58
+ issues?: {
59
+ enabled?: boolean;
60
+ };
61
+ forge?: {
62
+ host?: string;
63
+ };
64
+ };
65
+ type Convention = Required<Omit<Config, 'dashboard' | 'uploads' | 'sessions' | 'resources' | 'serve' | 'harnesses' | 'preset' | 'issues' | 'forge' | 'private' | 'render'>>;
66
+ export type Worktree = {
67
+ path: string;
68
+ branch: string | null;
69
+ node: string | null;
70
+ session: string | null;
71
+ status: string | null;
72
+ isMain: boolean;
73
+ liveness?: 'offline' | 'unknown';
74
+ ops: NodeOp[];
75
+ };
76
+ export type Layout = {
77
+ main: string;
78
+ convention: Convention;
79
+ worktrees: Worktree[];
80
+ };
81
+ export declare function readJsonConfig(p: string): any;
82
+ export declare function readConfig(root: string): Config;
83
+ export type UploadPolicy = Required<NonNullable<Config['uploads']>>;
84
+ export declare const templateConfigPath: string;
85
+ export declare function uploadPolicyDefaults(): UploadPolicy;
86
+ export declare function readUploadPolicy(root: string): UploadPolicy;
87
+ export declare function gitCommonDir(): string;
88
+ export declare function mainBranch(): string;
89
+ export declare function mainCheckout(proj?: string): string;
90
+ export declare function mainRoot(proj?: string): string;
91
+ export declare function runtimeRoot(proj?: string): string;
92
+ export declare function treeSlotDir(wt: string): string;
93
+ export declare function sessionsRoot(): string;
94
+ export declare function sessionStoreDir(id: string): string;
95
+ export declare function sessionRecordPath(id: string): string;
96
+ export declare function sessionArtifactPath(id: string, name: string): string;
97
+ export type RawRecord = {
98
+ session_id: string;
99
+ governed: boolean;
100
+ worktree_path: string;
101
+ branch: string | null;
102
+ node: string | null;
103
+ title: string | null;
104
+ name: string | null;
105
+ parent?: string | null;
106
+ status: string;
107
+ proposal: string | null;
108
+ merges: number;
109
+ note: string | null;
110
+ sortkey: number | null;
111
+ createdAt: number;
112
+ harness?: string;
113
+ harness_session_id?: string;
114
+ stopped?: boolean;
115
+ archived?: boolean;
116
+ cold_proof?: string;
117
+ adapter_recovery?: string;
118
+ launcher?: string;
119
+ launch_cmd?: string;
120
+ create_request_id?: string;
121
+ create_payload_hash?: string;
122
+ zcode_child_session_ids?: string[];
123
+ base?: string;
124
+ launch_readiness_pending?: '' | RawLaunchReadinessPending;
125
+ };
126
+ export declare const SESSION_LIFECYCLES: readonly ["active", "idle", "awaiting", "parked", "error", "asking", "queued"];
127
+ export declare const SESSION_PROPOSALS: readonly ["merge", "nothing", "close"];
128
+ export type SessionLifecycle = typeof SESSION_LIFECYCLES[number];
129
+ export type SessionProposal = typeof SESSION_PROPOSALS[number];
130
+ export declare const isSessionLifecycle: (value: unknown) => value is SessionLifecycle;
131
+ export declare const isSessionProposal: (value: unknown) => value is SessionProposal;
132
+ export type RawLaunchReadinessOriginal = {
133
+ status: string;
134
+ proposal: string | null;
135
+ note: string | null;
136
+ stopped: boolean;
137
+ archived: boolean;
138
+ cold_proof: string | null;
139
+ adapter_recovery: string | null;
140
+ };
141
+ export type RawLaunchReadinessPending = {
142
+ version: 1;
143
+ startedAt: number;
144
+ original: RawLaunchReadinessOriginal;
145
+ };
146
+ export declare function rawLaunchReadinessOriginal(raw: RawRecord): RawLaunchReadinessOriginal | null;
147
+ export declare function envSessionId(): string | null;
148
+ export type RecordEntry = {
149
+ kind: 'ok';
150
+ raw: RawRecord;
151
+ } | {
152
+ kind: 'absent';
153
+ } | {
154
+ kind: 'corrupt';
155
+ path: string;
156
+ error: string;
157
+ };
158
+ export type PublicRecordEntry = {
159
+ kind: 'ok';
160
+ raw: RawRecord;
161
+ liveness: 'offline' | null;
162
+ } | {
163
+ kind: 'absent';
164
+ } | {
165
+ kind: 'corrupt';
166
+ sessionId: string;
167
+ governed: boolean | null;
168
+ path: string;
169
+ error: string;
170
+ liveness: 'unknown';
171
+ };
172
+ export declare function readRecordEntry(id: string): RecordEntry;
173
+ export declare function projectPublicRecordEntry(id: string, entry: RecordEntry): PublicRecordEntry;
174
+ export declare function readPublicRecordEntry(id: string): PublicRecordEntry;
175
+ export declare function readRawRecord(id: string): RawRecord | null;
176
+ export declare function readAliasedRawRecord(id: string): RawRecord | null;
177
+ export declare function readAliasedRecordEntry(id: string): RecordEntry;
178
+ export declare function listSessionIds(): string[];
179
+ export declare function sessionBranchIndex(): Map<string, string>;
180
+ export declare function resolveLayout(options?: {
181
+ activeSessionIds?: readonly string[];
182
+ }): Promise<Layout>;
183
+ export {};