@mmnto/totem 1.20.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.
Files changed (65) hide show
  1. package/dist/ast-classifier.d.ts +72 -4
  2. package/dist/ast-classifier.d.ts.map +1 -1
  3. package/dist/ast-classifier.js +155 -30
  4. package/dist/ast-classifier.js.map +1 -1
  5. package/dist/ast-classifier.test.js +80 -2
  6. package/dist/ast-classifier.test.js.map +1 -1
  7. package/dist/ast-grep-query.d.ts.map +1 -1
  8. package/dist/ast-grep-query.js +31 -11
  9. package/dist/ast-grep-query.js.map +1 -1
  10. package/dist/chunkers/chunker-registry.d.ts +77 -0
  11. package/dist/chunkers/chunker-registry.d.ts.map +1 -0
  12. package/dist/chunkers/chunker-registry.js +134 -0
  13. package/dist/chunkers/chunker-registry.js.map +1 -0
  14. package/dist/chunkers/chunker-registry.test.d.ts +14 -0
  15. package/dist/chunkers/chunker-registry.test.d.ts.map +1 -0
  16. package/dist/chunkers/chunker-registry.test.js +95 -0
  17. package/dist/chunkers/chunker-registry.test.js.map +1 -0
  18. package/dist/chunkers/chunker.d.ts +18 -3
  19. package/dist/chunkers/chunker.d.ts.map +1 -1
  20. package/dist/chunkers/chunker.js +20 -13
  21. package/dist/chunkers/chunker.js.map +1 -1
  22. package/dist/compiler.d.ts +1 -1
  23. package/dist/compiler.d.ts.map +1 -1
  24. package/dist/compiler.js +1 -1
  25. package/dist/compiler.js.map +1 -1
  26. package/dist/config-schema.d.ts +193 -10
  27. package/dist/config-schema.d.ts.map +1 -1
  28. package/dist/config-schema.js +87 -2
  29. package/dist/config-schema.js.map +1 -1
  30. package/dist/config-schema.test.js +122 -0
  31. package/dist/config-schema.test.js.map +1 -1
  32. package/dist/index.d.ts +9 -3
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +10 -2
  35. package/dist/index.js.map +1 -1
  36. package/dist/pack-discovery.d.ts +201 -0
  37. package/dist/pack-discovery.d.ts.map +1 -0
  38. package/dist/pack-discovery.js +298 -0
  39. package/dist/pack-discovery.js.map +1 -0
  40. package/dist/pack-discovery.test.d.ts +17 -0
  41. package/dist/pack-discovery.test.d.ts.map +1 -0
  42. package/dist/pack-discovery.test.js +343 -0
  43. package/dist/pack-discovery.test.js.map +1 -0
  44. package/dist/pack-manifest-writer.d.ts +88 -0
  45. package/dist/pack-manifest-writer.d.ts.map +1 -0
  46. package/dist/pack-manifest-writer.js +162 -0
  47. package/dist/pack-manifest-writer.js.map +1 -0
  48. package/dist/pack-manifest-writer.test.d.ts +15 -0
  49. package/dist/pack-manifest-writer.test.d.ts.map +1 -0
  50. package/dist/pack-manifest-writer.test.js +184 -0
  51. package/dist/pack-manifest-writer.test.js.map +1 -0
  52. package/dist/rule-engine.d.ts +11 -0
  53. package/dist/rule-engine.d.ts.map +1 -1
  54. package/dist/rule-engine.js +60 -6
  55. package/dist/rule-engine.js.map +1 -1
  56. package/dist/rule-engine.test.js +116 -0
  57. package/dist/rule-engine.test.js.map +1 -1
  58. package/dist/stage4-verifier.d.ts +94 -1
  59. package/dist/stage4-verifier.d.ts.map +1 -1
  60. package/dist/stage4-verifier.js +139 -71
  61. package/dist/stage4-verifier.js.map +1 -1
  62. package/dist/stage4-verifier.test.js +215 -4
  63. package/dist/stage4-verifier.test.js.map +1 -1
  64. package/dist/store/lance-schema.d.ts +3 -3
  65. package/package.json +3 -1
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Tests for the pack discovery substrate (mmnto-ai/totem#1768, ADR-097
3
+ * § 5 Q5 + § 10).
4
+ *
5
+ * Covers invariants 1, 2, 3, 4, 8 from `.totem/specs/pack-substrate-bundle.md`:
6
+ *
7
+ * - Missing manifest → empty packs, no throw.
8
+ * - Malformed manifest → fail loud.
9
+ * - peerDependencies engine version mismatch → structured fail loud.
10
+ * - Re-load after seal → throw.
11
+ * - Pack callback can register chunker + language; lookups return registered values.
12
+ *
13
+ * Uses the `inMemoryPacks` test escape hatch to drive the registration
14
+ * phase without writing fixture pack packages to `node_modules`.
15
+ */
16
+ import * as fs from 'node:fs';
17
+ import * as os from 'node:os';
18
+ import * as path from 'node:path';
19
+ import { afterEach, describe, expect, it } from 'vitest';
20
+ import { __resetLangRegistryForTests, extensionToLanguage, registeredExtensions, } from './ast-classifier.js';
21
+ import { __resetForTests as __resetChunkerRegistryForTests, lookup as lookupChunker, registeredNames as registeredChunkerStrategies, } from './chunkers/chunker-registry.js';
22
+ import { __resetForTests as __resetPackDiscoveryForTests, isEngineSealed, loadInstalledPacks, } from './pack-discovery.js';
23
+ class FakeChunker {
24
+ strategy = 'rust-ast';
25
+ chunk(_content, _filePath, _type) {
26
+ return [];
27
+ }
28
+ }
29
+ afterEach(() => {
30
+ // Order matters: pack-discovery's seal flag and registries are
31
+ // independent state containers — reset all three each test so seal
32
+ // state from one case doesn't leak into the next.
33
+ __resetPackDiscoveryForTests();
34
+ __resetChunkerRegistryForTests();
35
+ __resetLangRegistryForTests();
36
+ });
37
+ describe('loadInstalledPacks: missing manifest', () => {
38
+ it('returns empty packs and does not throw when installed-packs.json is absent', () => {
39
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pack-discovery-test-'));
40
+ try {
41
+ const packs = loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' });
42
+ expect(packs).toEqual([]);
43
+ expect(isEngineSealed()).toBe(true);
44
+ }
45
+ finally {
46
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
47
+ }
48
+ });
49
+ });
50
+ describe('loadInstalledPacks: malformed manifest', () => {
51
+ it('throws structured error on invalid JSON', () => {
52
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pack-discovery-test-'));
53
+ try {
54
+ const totemDir = path.join(tmpRoot, '.totem');
55
+ fs.mkdirSync(totemDir, { recursive: true });
56
+ fs.writeFileSync(path.join(totemDir, 'installed-packs.json'), '{ this is not json');
57
+ expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/not valid JSON/);
58
+ }
59
+ finally {
60
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
61
+ }
62
+ });
63
+ it('throws structured error on schema validation failure', () => {
64
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pack-discovery-test-'));
65
+ try {
66
+ const totemDir = path.join(tmpRoot, '.totem');
67
+ fs.mkdirSync(totemDir, { recursive: true });
68
+ fs.writeFileSync(path.join(totemDir, 'installed-packs.json'), JSON.stringify({ version: 99, packs: [] }));
69
+ expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/failed schema validation/);
70
+ }
71
+ finally {
72
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
73
+ }
74
+ });
75
+ it('throws on unknown sibling keys (strict schema)', () => {
76
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pack-discovery-test-'));
77
+ try {
78
+ const totemDir = path.join(tmpRoot, '.totem');
79
+ fs.mkdirSync(totemDir, { recursive: true });
80
+ fs.writeFileSync(path.join(totemDir, 'installed-packs.json'), JSON.stringify({ version: 1, packs: [], extra: 'oops' }));
81
+ expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/failed schema validation/);
82
+ }
83
+ finally {
84
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
85
+ }
86
+ });
87
+ it('rejects manifest with duplicate pack names at the schema boundary', () => {
88
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pack-discovery-test-'));
89
+ try {
90
+ const totemDir = path.join(tmpRoot, '.totem');
91
+ fs.mkdirSync(totemDir, { recursive: true });
92
+ fs.writeFileSync(path.join(totemDir, 'installed-packs.json'), JSON.stringify({
93
+ version: 1,
94
+ packs: [
95
+ {
96
+ name: '@totem/pack-rust',
97
+ resolvedPath: path.resolve('/abs/a'),
98
+ declaredEngineRange: '^1.19.0',
99
+ },
100
+ {
101
+ name: '@totem/pack-rust',
102
+ resolvedPath: path.resolve('/abs/b'),
103
+ declaredEngineRange: '^1.19.0',
104
+ },
105
+ ],
106
+ }));
107
+ expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/duplicate pack entry '@totem\/pack-rust'/);
108
+ }
109
+ finally {
110
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
111
+ }
112
+ });
113
+ it('rejects manifest entries with a relative resolvedPath at the schema boundary', () => {
114
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pack-discovery-test-'));
115
+ try {
116
+ const totemDir = path.join(tmpRoot, '.totem');
117
+ fs.mkdirSync(totemDir, { recursive: true });
118
+ fs.writeFileSync(path.join(totemDir, 'installed-packs.json'), JSON.stringify({
119
+ version: 1,
120
+ packs: [
121
+ {
122
+ name: '@totem/pack-relative',
123
+ resolvedPath: 'node_modules/@totem/pack-relative',
124
+ declaredEngineRange: '^1.19.0',
125
+ },
126
+ ],
127
+ }));
128
+ expect(() => loadInstalledPacks({ projectRoot: tmpRoot, totemDir: '.totem' })).toThrowError(/resolvedPath must be an absolute path/);
129
+ }
130
+ finally {
131
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
132
+ }
133
+ });
134
+ });
135
+ describe('loadInstalledPacks: peerDependencies engine version mismatch', () => {
136
+ it('throws structured error naming pack name + declared range + actual engine version', () => {
137
+ const fakeCallback = () => { };
138
+ const fakePack = {
139
+ name: '@totem/pack-fake',
140
+ resolvedPath: '/fake/path',
141
+ declaredEngineRange: '^2.0.0',
142
+ };
143
+ expect(() => loadInstalledPacks({
144
+ engineVersion: '1.21.0',
145
+ inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
146
+ })).toThrowError(/Pack '@totem\/pack-fake' requires @mmnto\/totem '\^2\.0\.0'.*running engine is 1\.21\.0/);
147
+ });
148
+ it('passes when engine version satisfies declared range', () => {
149
+ const fakeCallback = () => { };
150
+ const fakePack = {
151
+ name: '@totem/pack-fake',
152
+ resolvedPath: '/fake/path',
153
+ declaredEngineRange: '^1.19.0',
154
+ };
155
+ const packs = loadInstalledPacks({
156
+ engineVersion: '1.21.0',
157
+ inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
158
+ });
159
+ expect(packs).toHaveLength(1);
160
+ expect(packs[0]?.name).toBe('@totem/pack-fake');
161
+ });
162
+ it('throws when declared range is invalid semver', () => {
163
+ const fakeCallback = () => { };
164
+ const fakePack = {
165
+ name: '@totem/pack-fake',
166
+ resolvedPath: '/fake/path',
167
+ declaredEngineRange: 'not-a-semver-range',
168
+ };
169
+ expect(() => loadInstalledPacks({
170
+ engineVersion: '1.21.0',
171
+ inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
172
+ })).toThrowError(/not a valid semver range/);
173
+ });
174
+ });
175
+ describe('loadInstalledPacks: re-load after seal', () => {
176
+ it('throws when called twice', () => {
177
+ loadInstalledPacks({ inMemoryPacks: [] });
178
+ expect(() => loadInstalledPacks({ inMemoryPacks: [] })).toThrowError(/called after engine seal/);
179
+ });
180
+ });
181
+ describe('loadInstalledPacks: pack callback registration', () => {
182
+ it('invokes pack callback with PackRegistrationAPI; lookups return registered values', () => {
183
+ const fakeCallback = (api) => {
184
+ api.registerChunkStrategy('rust-ast', FakeChunker);
185
+ api.registerLanguage('.rs', 'rust', () => '/fake/tree-sitter-rust.wasm');
186
+ };
187
+ const fakePack = {
188
+ name: '@totem/pack-rust-architecture',
189
+ resolvedPath: '/fake/path',
190
+ declaredEngineRange: '^1.19.0',
191
+ };
192
+ loadInstalledPacks({
193
+ engineVersion: '1.21.0',
194
+ inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
195
+ });
196
+ expect(lookupChunker('rust-ast')).toBe(FakeChunker);
197
+ expect(registeredChunkerStrategies()).toContain('rust-ast');
198
+ expect(extensionToLanguage('.rs')).toBe('rust');
199
+ expect(registeredExtensions()).toContain('.rs');
200
+ });
201
+ it('seals both registries after every callback returns', () => {
202
+ const fakeCallback = (api) => {
203
+ api.registerChunkStrategy('rust-ast', FakeChunker);
204
+ };
205
+ const fakePack = {
206
+ name: '@totem/pack-rust-architecture',
207
+ resolvedPath: '/fake/path',
208
+ declaredEngineRange: '^1.19.0',
209
+ };
210
+ loadInstalledPacks({
211
+ engineVersion: '1.21.0',
212
+ inMemoryPacks: [{ pack: fakePack, callback: fakeCallback }],
213
+ });
214
+ expect(isEngineSealed()).toBe(true);
215
+ });
216
+ it('rejects an async (Promise-returning) registration callback before sealing', () => {
217
+ const asyncCallback = (async (api) => {
218
+ api.registerChunkStrategy('async-strat', FakeChunker);
219
+ });
220
+ const fakePack = {
221
+ name: '@totem/pack-async',
222
+ resolvedPath: '/fake/path',
223
+ declaredEngineRange: '^1.19.0',
224
+ };
225
+ let caught;
226
+ try {
227
+ loadInstalledPacks({
228
+ engineVersion: '1.21.0',
229
+ inMemoryPacks: [{ pack: fakePack, callback: asyncCallback }],
230
+ });
231
+ }
232
+ catch (err) {
233
+ caught = err;
234
+ }
235
+ expect(caught).toBeInstanceOf(Error);
236
+ expect(caught.message).toMatch(/registration must be synchronous/);
237
+ expect(caught.message).toMatch(/ADR-097 § 5 Q5/);
238
+ expect(isEngineSealed()).toBe(false);
239
+ });
240
+ it('throws and names the pack when callback throws', () => {
241
+ const errorThrowingCallback = () => {
242
+ throw new Error('pack-side bug');
243
+ };
244
+ const fakePack = {
245
+ name: '@totem/pack-broken',
246
+ resolvedPath: '/fake/path',
247
+ declaredEngineRange: '^1.19.0',
248
+ };
249
+ let caught;
250
+ try {
251
+ loadInstalledPacks({
252
+ engineVersion: '1.21.0',
253
+ inMemoryPacks: [{ pack: fakePack, callback: errorThrowingCallback }],
254
+ });
255
+ }
256
+ catch (err) {
257
+ caught = err;
258
+ }
259
+ expect(caught).toBeInstanceOf(Error);
260
+ const outer = caught;
261
+ expect(outer.message).toMatch(/Pack '@totem\/pack-broken' registration callback threw/);
262
+ expect(outer.message).toMatch(/must be fixed or removed/);
263
+ expect(outer.cause).toBeInstanceOf(Error);
264
+ expect(outer.cause.message).toBe('pack-side bug');
265
+ });
266
+ it('two packs registering same chunk strategy: second fails loud', () => {
267
+ const callback1 = (api) => {
268
+ api.registerChunkStrategy('shared', FakeChunker);
269
+ };
270
+ const callback2 = (api) => {
271
+ api.registerChunkStrategy('shared', FakeChunker);
272
+ };
273
+ let caught;
274
+ try {
275
+ loadInstalledPacks({
276
+ engineVersion: '1.21.0',
277
+ inMemoryPacks: [
278
+ {
279
+ pack: {
280
+ name: '@totem/pack-a',
281
+ resolvedPath: '/a',
282
+ declaredEngineRange: '^1.19.0',
283
+ },
284
+ callback: callback1,
285
+ },
286
+ {
287
+ pack: {
288
+ name: '@totem/pack-b',
289
+ resolvedPath: '/b',
290
+ declaredEngineRange: '^1.19.0',
291
+ },
292
+ callback: callback2,
293
+ },
294
+ ],
295
+ });
296
+ }
297
+ catch (err) {
298
+ caught = err;
299
+ }
300
+ expect(caught).toBeInstanceOf(Error);
301
+ expect(caught.message).toMatch(/Pack '@totem\/pack-b' registration callback threw/);
302
+ expect(caught.cause.message).toMatch(/already registered/);
303
+ });
304
+ it('two packs registering same extension to different langs: second fails loud', () => {
305
+ const callback1 = (api) => {
306
+ api.registerLanguage('.shared', 'lang-a', () => '/a.wasm');
307
+ };
308
+ const callback2 = (api) => {
309
+ api.registerLanguage('.shared', 'lang-b', () => '/b.wasm');
310
+ };
311
+ let caught;
312
+ try {
313
+ loadInstalledPacks({
314
+ engineVersion: '1.21.0',
315
+ inMemoryPacks: [
316
+ {
317
+ pack: {
318
+ name: '@totem/pack-a',
319
+ resolvedPath: '/a',
320
+ declaredEngineRange: '^1.19.0',
321
+ },
322
+ callback: callback1,
323
+ },
324
+ {
325
+ pack: {
326
+ name: '@totem/pack-b',
327
+ resolvedPath: '/b',
328
+ declaredEngineRange: '^1.19.0',
329
+ },
330
+ callback: callback2,
331
+ },
332
+ ],
333
+ });
334
+ }
335
+ catch (err) {
336
+ caught = err;
337
+ }
338
+ expect(caught).toBeInstanceOf(Error);
339
+ expect(caught.message).toMatch(/Pack '@totem\/pack-b' registration callback threw/);
340
+ expect(caught.cause.message).toMatch(/already registered to language/);
341
+ });
342
+ });
343
+ //# sourceMappingURL=pack-discovery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-discovery.test.js","sourceRoot":"","sources":["../src/pack-discovery.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `installed-packs.json` writer (mmnto-ai/totem#1768, Step 4).
3
+ *
4
+ * Walks two source surfaces — the consumer's `package.json` dependencies
5
+ * for `@totem/pack-*` entries, and the consumer's `totem.config.ts`
6
+ * `extends` array — deduplicates, resolves each pack's installed path
7
+ * via npm/pnpm semantics, and writes `.totem/installed-packs.json` for
8
+ * `pack-discovery.ts:loadInstalledPacks()` to consume at boot.
9
+ *
10
+ * Mismatch handling (ADR-097 § 5 Q4 + Open Question 4 disposition):
11
+ * - In both surfaces: union, no warning.
12
+ * - In `extends` only (declared but not resolvable via npm): emit a
13
+ * warning naming the pack; skip from the manifest. Consumer must
14
+ * `pnpm add` the pack to make it work.
15
+ * - In `package.json` only (resolvable but not declared in extends):
16
+ * emit a warning; skip from the manifest. Pack-merge only consumes
17
+ * `extends`-declared packs; including the dep-only entry would be
18
+ * misleading.
19
+ *
20
+ * Atomic write semantics: the writer goes through a temp file +
21
+ * rename, mirroring `writeReviewExtensionsFile` (mmnto-ai/totem#1527).
22
+ *
23
+ * Filesystem access happens at the CLI boundary (this module is invoked
24
+ * by `syncCommand`); the resolver itself takes paths + content as
25
+ * inputs so tests can drive it without disk I/O.
26
+ */
27
+ import type { TotemConfig } from './config-schema.js';
28
+ import type { InstalledPacksManifest } from './pack-discovery.js';
29
+ export interface PackResolutionWarning {
30
+ /** Pack name surfaced in the warning. */
31
+ readonly name: string;
32
+ /**
33
+ * Why the pack didn't make it to the manifest. Two shapes:
34
+ *
35
+ * - `dep-only`: present in package.json but not declared in
36
+ * `totem.config.ts` `extends`. Skipped because pack-merge only
37
+ * consumes `extends`-declared packs.
38
+ * - `extends-only`: declared in `extends` but not installed
39
+ * (`require.resolve` failed). Skipped because the engine cannot
40
+ * load it.
41
+ * - `not-a-pack`: present somewhere but doesn't expose a `register`
42
+ * callback (default export or named `register` function). Skipped
43
+ * because pack-discovery would throw on first attempt.
44
+ */
45
+ readonly reason: 'dep-only' | 'extends-only' | 'not-a-pack';
46
+ }
47
+ export interface PackResolutionResult {
48
+ /** Packs that landed in the manifest (sorted by name). */
49
+ readonly resolved: InstalledPacksManifest['packs'];
50
+ /** Per-pack warnings for the consumer (sorted by name). */
51
+ readonly warnings: readonly PackResolutionWarning[];
52
+ }
53
+ export interface ResolveInstalledPacksInput {
54
+ /** Project root (where `package.json` lives). */
55
+ readonly projectRoot: string;
56
+ /** Loaded totem config (read for `extends`). */
57
+ readonly config: TotemConfig;
58
+ /**
59
+ * Optional override for the package.json read. Tests pass a synthetic
60
+ * dependencies map; production callers leave it undefined and the
61
+ * function reads `<projectRoot>/package.json`.
62
+ */
63
+ readonly packageJsonDeps?: Readonly<Record<string, string>>;
64
+ /**
65
+ * Optional override for the require resolver. Tests stub this to
66
+ * resolve fixture packs without writing to `node_modules`; production
67
+ * callers use the default which delegates to `createRequire`.
68
+ */
69
+ readonly resolvePackPath?: (name: string, fromDir: string) => string | undefined;
70
+ }
71
+ /**
72
+ * Compute the deduplicated union of `package.json` `@totem/pack-*` deps
73
+ * and `totem.config.ts` `extends` entries. Returns the manifest payload
74
+ * shape ready for `installed-packs.json` plus per-pack warnings for any
75
+ * surface mismatch.
76
+ *
77
+ * Pure function — no filesystem write. The CLI integration calls
78
+ * `writeInstalledPacksManifest()` to persist.
79
+ */
80
+ export declare function resolveInstalledPacks(input: ResolveInstalledPacksInput): PackResolutionResult;
81
+ /**
82
+ * Atomically write `<totemDir>/installed-packs.json` with the given
83
+ * manifest payload. Mirrors `writeReviewExtensionsFile` semantics: temp
84
+ * file + rename so a concurrent boot-time read sees the old or new
85
+ * contents, never a partial write.
86
+ */
87
+ export declare function writeInstalledPacksManifest(totemDirAbs: string, manifest: InstalledPacksManifest): string;
88
+ //# sourceMappingURL=pack-manifest-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-manifest-writer.d.ts","sourceRoot":"","sources":["../src/pack-manifest-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAIlE,MAAM,WAAW,qBAAqB;IACpC,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,YAAY,CAAC;CAC7D;AAED,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACnD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,0BAA0B;IACzC,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAClF;AAMD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,oBAAoB,CAkD7F;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,sBAAsB,GAC/B,MAAM,CASR"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * `installed-packs.json` writer (mmnto-ai/totem#1768, Step 4).
3
+ *
4
+ * Walks two source surfaces — the consumer's `package.json` dependencies
5
+ * for `@totem/pack-*` entries, and the consumer's `totem.config.ts`
6
+ * `extends` array — deduplicates, resolves each pack's installed path
7
+ * via npm/pnpm semantics, and writes `.totem/installed-packs.json` for
8
+ * `pack-discovery.ts:loadInstalledPacks()` to consume at boot.
9
+ *
10
+ * Mismatch handling (ADR-097 § 5 Q4 + Open Question 4 disposition):
11
+ * - In both surfaces: union, no warning.
12
+ * - In `extends` only (declared but not resolvable via npm): emit a
13
+ * warning naming the pack; skip from the manifest. Consumer must
14
+ * `pnpm add` the pack to make it work.
15
+ * - In `package.json` only (resolvable but not declared in extends):
16
+ * emit a warning; skip from the manifest. Pack-merge only consumes
17
+ * `extends`-declared packs; including the dep-only entry would be
18
+ * misleading.
19
+ *
20
+ * Atomic write semantics: the writer goes through a temp file +
21
+ * rename, mirroring `writeReviewExtensionsFile` (mmnto-ai/totem#1527).
22
+ *
23
+ * Filesystem access happens at the CLI boundary (this module is invoked
24
+ * by `syncCommand`); the resolver itself takes paths + content as
25
+ * inputs so tests can drive it without disk I/O.
26
+ */
27
+ import * as fs from 'node:fs';
28
+ import { createRequire } from 'node:module';
29
+ import * as path from 'node:path';
30
+ // ─── Resolver ───────────────────────────────────────
31
+ const PACK_NAME_PREFIX = '@totem/pack-';
32
+ /**
33
+ * Compute the deduplicated union of `package.json` `@totem/pack-*` deps
34
+ * and `totem.config.ts` `extends` entries. Returns the manifest payload
35
+ * shape ready for `installed-packs.json` plus per-pack warnings for any
36
+ * surface mismatch.
37
+ *
38
+ * Pure function — no filesystem write. The CLI integration calls
39
+ * `writeInstalledPacksManifest()` to persist.
40
+ */
41
+ export function resolveInstalledPacks(input) {
42
+ const deps = input.packageJsonDeps ?? readPackageJsonDeps(input.projectRoot);
43
+ const extendsList = (input.config.extends ?? []).filter((name) => name.startsWith(PACK_NAME_PREFIX));
44
+ const depPackNames = Object.keys(deps).filter((name) => name.startsWith(PACK_NAME_PREFIX)); // totem-context: Object.keys() returns strings; not fileGlobs ast-grep object form
45
+ const extendsSet = new Set(extendsList);
46
+ const depsSet = new Set(depPackNames);
47
+ const union = new Set([...extendsSet, ...depsSet]);
48
+ const warnings = [];
49
+ const resolved = [];
50
+ for (const name of [...union].sort()) {
51
+ if (!extendsSet.has(name)) {
52
+ // Resolvable dep that the consumer hasn't opted into via `extends`.
53
+ // Pack-merge only consumes extends-declared packs, so this pack
54
+ // would never have its rules merged anyway. Skip with a warning.
55
+ warnings.push({ name, reason: 'dep-only' });
56
+ continue;
57
+ }
58
+ if (!depsSet.has(name)) {
59
+ // Declared in extends but not installed via package manager.
60
+ // Cannot resolve a path; skip with a warning.
61
+ warnings.push({ name, reason: 'extends-only' });
62
+ continue;
63
+ }
64
+ const resolver = input.resolvePackPath ?? defaultResolvePackPath;
65
+ const resolvedPath = resolver(name, input.projectRoot);
66
+ if (!resolvedPath) {
67
+ warnings.push({ name, reason: 'extends-only' });
68
+ continue;
69
+ }
70
+ const declaredEngineRange = readPeerEngineRange(resolvedPath);
71
+ if (!declaredEngineRange) {
72
+ warnings.push({ name, reason: 'not-a-pack' });
73
+ continue;
74
+ }
75
+ resolved.push({
76
+ name,
77
+ resolvedPath,
78
+ declaredEngineRange,
79
+ });
80
+ }
81
+ return { resolved, warnings };
82
+ }
83
+ /**
84
+ * Atomically write `<totemDir>/installed-packs.json` with the given
85
+ * manifest payload. Mirrors `writeReviewExtensionsFile` semantics: temp
86
+ * file + rename so a concurrent boot-time read sees the old or new
87
+ * contents, never a partial write.
88
+ */
89
+ export function writeInstalledPacksManifest(totemDirAbs, manifest) {
90
+ if (!fs.existsSync(totemDirAbs)) {
91
+ fs.mkdirSync(totemDirAbs, { recursive: true });
92
+ }
93
+ const finalPath = path.join(totemDirAbs, 'installed-packs.json');
94
+ const tmpPath = finalPath + '.tmp';
95
+ fs.writeFileSync(tmpPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
96
+ fs.renameSync(tmpPath, finalPath);
97
+ return finalPath;
98
+ }
99
+ // ─── Internal helpers ───────────────────────────────
100
+ function readPackageJsonDeps(projectRoot) {
101
+ const pkgPath = path.join(projectRoot, 'package.json');
102
+ if (!fs.existsSync(pkgPath))
103
+ return {};
104
+ let parsed;
105
+ // totem-context: intentional cleanup — package.json missing/corrupt is non-fatal to sync; treat as no deps
106
+ try {
107
+ parsed = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); // totem-context: sync read at boot; sync command is itself synchronous CLI top-level
108
+ }
109
+ catch {
110
+ return {};
111
+ }
112
+ if (typeof parsed !== 'object' || parsed === null)
113
+ return {};
114
+ const obj = parsed;
115
+ const out = {};
116
+ for (const field of ['dependencies', 'devDependencies']) {
117
+ const section = obj[field];
118
+ if (typeof section !== 'object' || section === null)
119
+ continue;
120
+ for (const [name, value] of Object.entries(section)) {
121
+ if (typeof value === 'string')
122
+ out[name] = value;
123
+ }
124
+ }
125
+ return out;
126
+ }
127
+ function defaultResolvePackPath(name, fromDir) {
128
+ // We resolve from the project root rather than from this module — packs
129
+ // live in the consumer's node_modules, not core's.
130
+ const require = createRequire(path.join(fromDir, 'package.json'));
131
+ try {
132
+ // Resolve to the package's package.json so the directory containing it
133
+ // is the pack's package root. require.resolve(name) would land on the
134
+ // package's `main` entry, which isn't what pack-discovery wants.
135
+ const pkgJsonPath = require.resolve(`${name}/package.json`);
136
+ return path.dirname(pkgJsonPath); // totem-context: intentional cleanup — unresolvable pack returns undefined to flow into the `extends-only` warning path
137
+ }
138
+ catch {
139
+ return undefined;
140
+ }
141
+ }
142
+ function readPeerEngineRange(packResolvedPath) {
143
+ const pkgPath = path.join(packResolvedPath, 'package.json');
144
+ if (!fs.existsSync(pkgPath))
145
+ return undefined;
146
+ let parsed;
147
+ // totem-context: intentional cleanup — corrupted pack package.json returns undefined to flow into the `not-a-pack` warning path
148
+ try {
149
+ parsed = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); // totem-context: sync read at boot; sync command is itself synchronous CLI top-level
150
+ }
151
+ catch {
152
+ return undefined;
153
+ }
154
+ if (typeof parsed !== 'object' || parsed === null)
155
+ return undefined;
156
+ const peer = parsed.peerDependencies; // totem-context: parsed was narrowed via `typeof !== 'object' || === null` guard above; the cast is for index access into already-validated JSON
157
+ if (typeof peer !== 'object' || peer === null)
158
+ return undefined;
159
+ const range = peer['@mmnto/totem']; // totem-context: peer was narrowed via `typeof !== 'object' || === null` guard above; the cast is for index access; result is runtime-checked via `typeof range === 'string'` below
160
+ return typeof range === 'string' ? range : undefined;
161
+ }
162
+ //# sourceMappingURL=pack-manifest-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-manifest-writer.js","sourceRoot":"","sources":["../src/pack-manifest-writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Tests for pack manifest resolution + writer (mmnto-ai/totem#1768
3
+ * Step 4, Q4 disposition).
4
+ *
5
+ * Covers invariant 18 from `.totem/specs/pack-substrate-bundle.md`:
6
+ *
7
+ * - Resolves a pack present in BOTH `package.json` deps AND
8
+ * `totem.config.ts` `extends` → manifest entry, no warning.
9
+ * - Resolves a pack only in deps → no manifest entry, `dep-only` warning.
10
+ * - Resolves a pack only in extends → no manifest entry, `extends-only` warning.
11
+ * - Pack without `peerDependencies['@mmnto/totem']` → `not-a-pack` warning.
12
+ * - Atomic write produces a stable JSON file.
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=pack-manifest-writer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-manifest-writer.test.d.ts","sourceRoot":"","sources":["../src/pack-manifest-writer.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}