atmn 1.1.25 → 2.0.1

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 (117) hide show
  1. package/README.md +120 -0
  2. package/dist/bin.js +23318 -0
  3. package/dist/index.js +2946 -0
  4. package/dist/tsconfig.tsbuildinfo +1 -1
  5. package/package.json +28 -74
  6. package/src/actions/api/callApi.ts +280 -0
  7. package/src/actions/api/registerApiCommands.ts +121 -0
  8. package/src/actions/env/fetchOrgInfo.ts +32 -0
  9. package/src/actions/env/types/orgInfo.ts +18 -0
  10. package/src/actions/env.ts +94 -0
  11. package/src/actions/init/runInit.ts +401 -0
  12. package/src/actions/login/keyless.ts +135 -0
  13. package/src/actions/login.ts +144 -0
  14. package/src/actions/pull/appendPlanVersionFixture.ts +373 -0
  15. package/src/actions/pull/applyPreview.ts +553 -0
  16. package/src/actions/pull/applySettingsPreview.ts +114 -0
  17. package/src/actions/pull/changedFixtureKeys.ts +88 -0
  18. package/src/actions/pull/listSourceFiles.ts +29 -0
  19. package/src/actions/pull/locateFixture.ts +78 -0
  20. package/src/actions/pull/resolveCollectionTarget.ts +198 -0
  21. package/src/actions/pull/rewriteConfig.ts +57 -0
  22. package/src/actions/pull/scaffoldConfig.ts +118 -0
  23. package/src/actions/pull.ts +399 -0
  24. package/src/actions/push/backfillInternalIds.ts +394 -0
  25. package/src/actions/push/deprecatedFields.ts +55 -0
  26. package/src/actions/push.ts +294 -0
  27. package/src/actions/reset/runReset.ts +58 -0
  28. package/src/actions/sandbox/createSandbox.ts +83 -0
  29. package/src/actions/sandbox/deleteSandbox.ts +91 -0
  30. package/src/actions/sandbox/listSandboxes.ts +28 -0
  31. package/src/actions/sandbox/types/sandboxClient.ts +16 -0
  32. package/src/actions/sandbox/useSandbox.ts +157 -0
  33. package/src/actions/sandbox/withSandboxScopeHint.ts +27 -0
  34. package/src/actions/skills/skills.ts +246 -0
  35. package/src/auth/announceAuthorizationUrl.ts +26 -0
  36. package/src/auth/browser/openSystemBrowser.ts +7 -0
  37. package/src/auth/browser/tryOpenBrowser.ts +17 -0
  38. package/src/auth/browser/watchLauncher.ts +46 -0
  39. package/src/auth/buildAuthorizationUrl.ts +36 -0
  40. package/src/auth/callbackPages.ts +126 -0
  41. package/src/auth/createOrgApiKeys.ts +46 -0
  42. package/src/auth/keyless.ts +119 -0
  43. package/src/auth/oauthConfig.ts +63 -0
  44. package/src/auth/runOAuthFlow.ts +230 -0
  45. package/src/auth/types/browserOpener.ts +5 -0
  46. package/src/auth/types/impersonationTokens.ts +23 -0
  47. package/src/auth/types/oauthTokens.ts +13 -0
  48. package/src/auth/types/orgApiKeys.ts +6 -0
  49. package/src/bin.ts +9 -0
  50. package/src/cli.ts +648 -0
  51. package/src/config/configPackageName.ts +9 -0
  52. package/src/config/legacyConfig.ts +25 -0
  53. package/src/config/loadConfig.ts +231 -0
  54. package/src/env/assertSandboxTarget.ts +20 -0
  55. package/src/env/loadEnv.ts +184 -0
  56. package/src/env/resolveTarget.ts +134 -0
  57. package/src/env/sandboxKeyName.ts +18 -0
  58. package/src/generated/apiRoutes.ts +3937 -0
  59. package/src/generated/client.ts +52579 -0
  60. package/src/generated/emit.ts +1188 -0
  61. package/src/generated/emitRuntime.ts +522 -0
  62. package/src/generated/features.ts +146 -0
  63. package/src/generated/labels.ts +29 -0
  64. package/src/generated/licenses.ts +292 -0
  65. package/src/generated/lintRules.ts +2247 -0
  66. package/src/generated/lintRuntime.ts +865 -0
  67. package/src/generated/plans.ts +1653 -0
  68. package/src/generated/referralPrograms.ts +22 -0
  69. package/src/generated/rewards.ts +58 -0
  70. package/src/generated/settings.ts +21 -0
  71. package/src/generated/skills.ts +305 -0
  72. package/src/generated/variants.ts +949 -0
  73. package/src/generated/wire.ts +334 -0
  74. package/src/http/autumnFetch.ts +30 -0
  75. package/src/index.ts +20 -0
  76. package/src/project/chooseConfigDir.ts +41 -0
  77. package/src/project/resolveProject.ts +115 -0
  78. package/src/project/rootMarker.ts +40 -0
  79. package/src/prompt/prompt.ts +186 -0
  80. package/src/prompt/select.ts +162 -0
  81. package/src/render/renderEnv.ts +77 -0
  82. package/src/render/renderPreview.ts +945 -0
  83. package/src/render/renderSandboxes.ts +92 -0
  84. package/src/render/stripTerminalControls.ts +19 -0
  85. package/src/repo/findRepoRoot.ts +79 -0
  86. package/src/surgery/appendPropertyEdit.ts +67 -0
  87. package/src/surgery/appendToArray.ts +87 -0
  88. package/src/surgery/appendToBinding.ts +19 -0
  89. package/src/surgery/appendToCollection.ts +40 -0
  90. package/src/surgery/appendToFixtureArray.ts +71 -0
  91. package/src/surgery/arrayBinding.ts +30 -0
  92. package/src/surgery/deleteFixtureLiteral.ts +81 -0
  93. package/src/surgery/deleteReference.ts +48 -0
  94. package/src/surgery/ensureBuilderImport.ts +65 -0
  95. package/src/surgery/findFixture.ts +238 -0
  96. package/src/surgery/fixtureEdit.ts +156 -0
  97. package/src/surgery/fixtureLocation.ts +32 -0
  98. package/src/surgery/insertCollection.ts +86 -0
  99. package/src/surgery/insertFirstProperty.ts +73 -0
  100. package/src/surgery/patchFixtureProperty.ts +152 -0
  101. package/src/surgery/patchSingletonProperty.ts +221 -0
  102. package/src/surgery/replaceFixture.ts +28 -0
  103. package/src/surgery/setFixtureProperty.ts +55 -0
  104. package/src/surgery/staticFixtureRule.ts +48 -0
  105. package/src/version.ts +5 -0
  106. package/dist/cli.js +0 -146296
  107. package/dist/compose/index.js +0 -122
  108. package/dist/src/compose/builders/builderFunctions.d.ts +0 -84
  109. package/dist/src/compose/builders/rewardFunctions.d.ts +0 -5
  110. package/dist/src/compose/builders/variantFunctions.d.ts +0 -2
  111. package/dist/src/compose/index.d.ts +0 -19
  112. package/dist/src/compose/models/featureModels.d.ts +0 -262
  113. package/dist/src/compose/models/index.d.ts +0 -3
  114. package/dist/src/compose/models/planModels.d.ts +0 -562
  115. package/dist/src/compose/models/rewardModels.d.ts +0 -52
  116. package/dist/src/compose/models/variantModels.d.ts +0 -34
  117. package/readme.md +0 -186
@@ -0,0 +1,152 @@
1
+ import { type Edit, Lang, parse, type SgNode } from "@ast-grep/napi";
2
+ import { appendPropertyEdit } from "./appendPropertyEdit";
3
+ import {
4
+ type FixtureConstraint,
5
+ type FixtureShape,
6
+ findFixture,
7
+ fixtureObjectOf,
8
+ } from "./findFixture";
9
+ import { lineEndInclusive, lineEndOf, lineStartOf } from "./fixtureEdit";
10
+
11
+ const pairFor = ({
12
+ object,
13
+ property,
14
+ }: {
15
+ object: SgNode;
16
+ property: string;
17
+ }): SgNode | null => {
18
+ for (const member of object.children()) {
19
+ if (member.kind() !== "pair") continue;
20
+ const [key] = member.namedChildren();
21
+ const name = key?.text().replace(/^["']|["']$/g, "");
22
+ if (name === property) return member;
23
+ }
24
+ return null;
25
+ };
26
+
27
+ /** Whether the fixture states this top-level property at all. A dynamic value
28
+ * counts: it still wins over a pair inserted beside it. */
29
+ export const fixtureStatesProperty = ({
30
+ call,
31
+ property,
32
+ }: {
33
+ call: SgNode;
34
+ property: string;
35
+ }): boolean => {
36
+ const object = fixtureObjectOf(call);
37
+ if (object === null) return false;
38
+ return pairFor({ object, property }) !== null;
39
+ };
40
+
41
+ /** The string a fixture literal states for one top-level property, or null. */
42
+ export const fixturePropertyString = ({
43
+ call,
44
+ property,
45
+ }: {
46
+ call: SgNode;
47
+ property: string;
48
+ }): string | null => {
49
+ const object = fixtureObjectOf(call);
50
+ if (object === null) return null;
51
+ const pair = pairFor({ object, property });
52
+ const value = pair?.namedChildren()[1];
53
+ if (value === undefined || value.kind() !== "string") return null;
54
+ return value.text().slice(1, -1);
55
+ };
56
+
57
+ /** Remove a pair: its line when it stands alone, else the pair plus one adjacent comma. */
58
+ const removePairEdit = ({
59
+ source,
60
+ pair,
61
+ }: {
62
+ source: string;
63
+ pair: SgNode;
64
+ }): Edit => {
65
+ const start = pair.range().start.index;
66
+ const end = pair.range().end.index;
67
+ const lineStart = lineStartOf(source, start);
68
+ const lineEnd = lineEndOf(source, end);
69
+ const after = source.slice(end, lineEnd);
70
+ const commaIndex = after.indexOf(",");
71
+ const hasTrailingComma =
72
+ commaIndex !== -1 && after.slice(0, commaIndex).trim() === "";
73
+ const restAfterComma = hasTrailingComma ? after.slice(commaIndex + 1) : after;
74
+ const standsAlone =
75
+ source.slice(lineStart, start).trim() === "" &&
76
+ restAfterComma.trim() === "";
77
+ if (standsAlone) {
78
+ return {
79
+ startPos: lineStart,
80
+ endPos: lineEndInclusive(source, end),
81
+ insertedText: "",
82
+ };
83
+ }
84
+ if (hasTrailingComma) {
85
+ const afterComma = end + commaIndex + 1;
86
+ const spacing = /^[ \t]*/.exec(source.slice(afterComma))?.[0].length ?? 0;
87
+ return { startPos: start, endPos: afterComma + spacing, insertedText: "" };
88
+ }
89
+ const commaBefore = source.lastIndexOf(",", start);
90
+ return {
91
+ startPos: commaBefore === -1 ? start : commaBefore,
92
+ endPos: end,
93
+ insertedText: "",
94
+ };
95
+ };
96
+
97
+ /**
98
+ * Set one top-level property of a fixture literal to raw source text: overwrite
99
+ * it where it stands, append it when absent, remove it when `text` is null.
100
+ * Everything else in the literal keeps its bytes. Null when the fixture is not there.
101
+ */
102
+ export const patchFixtureProperty = ({
103
+ source,
104
+ builder,
105
+ idField,
106
+ id,
107
+ where,
108
+ property,
109
+ text,
110
+ }: {
111
+ source: string;
112
+ builder: FixtureShape;
113
+ idField: string;
114
+ id: string;
115
+ where?: FixtureConstraint[];
116
+ property: string;
117
+ text: string | null;
118
+ }): string | null => {
119
+ // A splice keeps every other byte, so a literal the pull rewriter refuses
120
+ // (it names another fixture) still takes the new value.
121
+ const call = findFixture({
122
+ source,
123
+ builder,
124
+ idField,
125
+ id,
126
+ where,
127
+ allowDynamic: true,
128
+ });
129
+ if (call === null) return null;
130
+ const object = fixtureObjectOf(call);
131
+ if (object === null) return null;
132
+ const root = parse(Lang.TypeScript, source).root();
133
+ const pair = pairFor({ object, property });
134
+ if (text === null) {
135
+ if (pair === null) return source;
136
+ return root.commitEdits([removePairEdit({ source, pair })]);
137
+ }
138
+ if (pair === null) {
139
+ return root.commitEdits([
140
+ appendPropertyEdit({ source, object, pair: `${property}: ${text}` }),
141
+ ]);
142
+ }
143
+ const [, current] = pair.namedChildren();
144
+ if (current === undefined) return null;
145
+ return root.commitEdits([
146
+ {
147
+ startPos: current.range().start.index,
148
+ endPos: current.range().end.index,
149
+ insertedText: text,
150
+ },
151
+ ]);
152
+ };
@@ -0,0 +1,221 @@
1
+ import { Lang, parse, type SgNode } from "@ast-grep/napi";
2
+ import { appendPropertyEdit } from "./appendPropertyEdit";
3
+ import { findLiteralBinding } from "./arrayBinding";
4
+ import { lineStartOf } from "./fixtureEdit";
5
+ import { insertCollection } from "./insertCollection";
6
+
7
+ /** One key of a singleton block, and the literal it should hold — or nothing. */
8
+ export type SingletonEdit = { key: string; text: string | null };
9
+
10
+ /** Where the block's object literal lives: inline under the key, or a const. */
11
+ export type SingletonBlock =
12
+ | { kind: "inline"; singleton: string }
13
+ | { kind: "binding"; name: string };
14
+
15
+ const atmnObject = (root: SgNode): SgNode | null => {
16
+ const object = root.find("atmn($ARG)")?.getMatch("ARG") ?? null;
17
+ return object !== null && object.kind() === "object" ? object : null;
18
+ };
19
+
20
+ const pairFor = ({
21
+ object,
22
+ key,
23
+ }: {
24
+ object: SgNode;
25
+ key: string;
26
+ }): SgNode | null => {
27
+ for (const member of object.children()) {
28
+ if (member.kind() !== "pair") continue;
29
+ const [name] = member.namedChildren();
30
+ if (name?.text().replace(/^["']|["']$/g, "") === key) return member;
31
+ }
32
+ return null;
33
+ };
34
+
35
+ /** A block built from a spread cannot be edited by pairs: a later spread
36
+ * overrides them, and dropping the block would drop what the spread holds. */
37
+ const isPlainObject = (node: SgNode): boolean =>
38
+ node.kind() === "object" &&
39
+ !node.children().some((child) => child.kind() === "spread_element");
40
+
41
+ const blockObject = ({
42
+ root,
43
+ block,
44
+ }: {
45
+ root: SgNode;
46
+ block: SingletonBlock;
47
+ }): SgNode | null => {
48
+ const object =
49
+ block.kind === "binding"
50
+ ? findLiteralBinding({ root, name: block.name, kind: "object" })
51
+ : atmnObjectPairValue({ root, key: block.singleton });
52
+ return object !== null && isPlainObject(object) ? object : null;
53
+ };
54
+
55
+ const atmnObjectPairValue = ({
56
+ root,
57
+ key,
58
+ }: {
59
+ root: SgNode;
60
+ key: string;
61
+ }): SgNode | null => {
62
+ const object = atmnObject(root);
63
+ if (object === null) return null;
64
+ return pairFor({ object, key })?.namedChildren()[1] ?? null;
65
+ };
66
+
67
+ const isTrivia = (text: string): boolean =>
68
+ text
69
+ .replace(/\/\*[\s\S]*?\*\//g, "")
70
+ .replace(/\/\/.*$/, "")
71
+ .trim() === "";
72
+
73
+ /** Remove a pair with its line when it stands alone, else with one adjacent comma. */
74
+ const removePairEdit = ({
75
+ source,
76
+ pair,
77
+ }: {
78
+ source: string;
79
+ pair: SgNode;
80
+ }): { startPos: number; endPos: number; insertedText: string } => {
81
+ const start = pair.range().start.index;
82
+ const end = pair.range().end.index;
83
+ const lineStart = lineStartOf(source, start);
84
+ const newline = source.indexOf("\n", end);
85
+ const lineEnd = newline === -1 ? source.length : newline;
86
+ const after = source.slice(end, lineEnd);
87
+ const commaIndex = after.indexOf(",");
88
+ // Only whitespace and comments may sit between the pair and its comma.
89
+ const hasTrailingComma =
90
+ commaIndex !== -1 && isTrivia(after.slice(0, commaIndex));
91
+ const rest = hasTrailingComma ? after.slice(commaIndex + 1) : after;
92
+ if (source.slice(lineStart, start).trim() === "" && isTrivia(rest)) {
93
+ return {
94
+ startPos: lineStart,
95
+ endPos: newline === -1 ? source.length : newline + 1,
96
+ insertedText: "",
97
+ };
98
+ }
99
+ if (hasTrailingComma) {
100
+ const afterComma = end + commaIndex + 1;
101
+ const spacing = /^[ \t]*/.exec(source.slice(afterComma))?.[0].length ?? 0;
102
+ return { startPos: start, endPos: afterComma + spacing, insertedText: "" };
103
+ }
104
+ const commaBefore = source.lastIndexOf(",", start);
105
+ return {
106
+ startPos: commaBefore === -1 ? start : commaBefore,
107
+ endPos: end,
108
+ insertedText: "",
109
+ };
110
+ };
111
+
112
+ /**
113
+ * One edit to a singleton's object literal: set a key's literal where it
114
+ * stands, append it when absent, drop it when `text` is null. Null when the
115
+ * block is not a plain object literal this can edit.
116
+ */
117
+ export const patchSingletonProperty = ({
118
+ source,
119
+ block,
120
+ edit,
121
+ }: {
122
+ source: string;
123
+ block: SingletonBlock;
124
+ edit: SingletonEdit;
125
+ }): string | null => {
126
+ const root = parse(Lang.TypeScript, source).root();
127
+ const object = blockObject({ root, block });
128
+ if (object === null) return null;
129
+
130
+ const existing = pairFor({ object, key: edit.key });
131
+ if (edit.text === null) {
132
+ if (existing === null) return source;
133
+ // The last pair out leaves `{}`, never `{ }` or a dangling comma.
134
+ const onlyPair =
135
+ object.children().filter((child) => child.kind() === "pair").length === 1;
136
+ return root.commitEdits([
137
+ onlyPair
138
+ ? {
139
+ startPos: object.range().start.index,
140
+ endPos: object.range().end.index,
141
+ insertedText: "{}",
142
+ }
143
+ : removePairEdit({ source, pair: existing }),
144
+ ]);
145
+ }
146
+ if (existing === null) {
147
+ return root.commitEdits([
148
+ appendPropertyEdit({ source, object, pair: `${edit.key}: ${edit.text}` }),
149
+ ]);
150
+ }
151
+ const [, current] = existing.namedChildren();
152
+ if (current === undefined) return null;
153
+ return root.commitEdits([
154
+ {
155
+ startPos: current.range().start.index,
156
+ endPos: current.range().end.index,
157
+ insertedText: edit.text,
158
+ },
159
+ ]);
160
+ };
161
+
162
+ /** `settings: {}` beside the collections, when the config has no such key. */
163
+ export const insertSingleton = ({
164
+ source,
165
+ singleton,
166
+ }: {
167
+ source: string;
168
+ singleton: string;
169
+ }): string | null => {
170
+ const root = parse(Lang.TypeScript, source).root();
171
+ const object = atmnObject(root);
172
+ if (object === null) return null;
173
+ // Already stated, quoted or not: nothing to seed.
174
+ if (pairFor({ object, key: singleton }) !== null) return source;
175
+ if (
176
+ object
177
+ .children()
178
+ .some(
179
+ (child) =>
180
+ child.kind() === "shorthand_property_identifier" &&
181
+ child.text() === singleton,
182
+ )
183
+ )
184
+ return source;
185
+ // A root spread may already hold the key, and a pair inserted beside it
186
+ // would silently win or lose to it depending on order.
187
+ if (object.children().some((child) => child.kind() === "spread_element"))
188
+ return null;
189
+ const withArray = insertCollection({ source, collection: singleton });
190
+ if (withArray === null || withArray === source) return withArray;
191
+ // insertCollection seeds `key: []`; a singleton is an object.
192
+ const seededRoot = parse(Lang.TypeScript, withArray).root();
193
+ const seeded = atmnObject(seededRoot);
194
+ const pair =
195
+ seeded === null ? null : pairFor({ object: seeded, key: singleton });
196
+ const value = pair?.namedChildren()[1];
197
+ if (value === undefined || value.kind() !== "array") return null;
198
+ return seededRoot.commitEdits([
199
+ {
200
+ startPos: value.range().start.index,
201
+ endPos: value.range().end.index,
202
+ insertedText: "{}",
203
+ },
204
+ ]);
205
+ };
206
+
207
+ /** The literal text a singleton block states for one key, or null. */
208
+ export const singletonPropertyText = ({
209
+ source,
210
+ block,
211
+ key,
212
+ }: {
213
+ source: string;
214
+ block: SingletonBlock;
215
+ key: string;
216
+ }): string | null => {
217
+ const root = parse(Lang.TypeScript, source).root();
218
+ const object = blockObject({ root, block });
219
+ if (object === null) return null;
220
+ return pairFor({ object, key })?.namedChildren()[1]?.text() ?? null;
221
+ };
@@ -0,0 +1,28 @@
1
+ import { type FixtureConstraint, findFixture } from "./findFixture";
2
+
3
+ export const replaceFixture = ({
4
+ source,
5
+ builder,
6
+ idField,
7
+ id,
8
+ where,
9
+ text,
10
+ }: {
11
+ source: string;
12
+ builder: string;
13
+ idField: string;
14
+ id: string;
15
+ where?: FixtureConstraint[];
16
+ text: string;
17
+ }): string | null => {
18
+ const call = findFixture({ source, builder, idField, id, where });
19
+ if (call === null) return null;
20
+ const { start, end } = call.range();
21
+ // Raw byte splice, not replace(): replace() treats `$` as a metavariable.
22
+ return call
23
+ .getRoot()
24
+ .root()
25
+ .commitEdits([
26
+ { startPos: start.index, endPos: end.index, insertedText: text },
27
+ ]);
28
+ };
@@ -0,0 +1,55 @@
1
+ import { Lang, parse } from "@ast-grep/napi";
2
+ import {
3
+ type FixtureConstraint,
4
+ type FixtureShape,
5
+ findFixture,
6
+ fixtureObjectOf,
7
+ } from "./findFixture";
8
+
9
+ /** Overwrite one top-level property's value in a fixture literal; null when
10
+ * the fixture or the property is not there. */
11
+ export const setFixtureProperty = ({
12
+ source,
13
+ builder,
14
+ idField,
15
+ id,
16
+ where,
17
+ property,
18
+ value,
19
+ }: {
20
+ source: string;
21
+ builder: FixtureShape;
22
+ idField: string;
23
+ id: string;
24
+ where?: FixtureConstraint[];
25
+ property: string;
26
+ value: string;
27
+ }): string | null => {
28
+ // A splice keeps every other byte, so a literal the pull rewriter refuses
29
+ // (it names another fixture) still takes the new value.
30
+ const call = findFixture({
31
+ source,
32
+ builder,
33
+ idField,
34
+ id,
35
+ where,
36
+ allowDynamic: true,
37
+ });
38
+ if (call === null) return null;
39
+ const object = fixtureObjectOf(call);
40
+ if (object === null) return null;
41
+ for (const member of object.children()) {
42
+ if (member.kind() !== "pair") continue;
43
+ const [key, current] = member.namedChildren();
44
+ if (key?.text() !== property || current === undefined) continue;
45
+ const root = parse(Lang.TypeScript, source).root();
46
+ return root.commitEdits([
47
+ {
48
+ startPos: current.range().start.index,
49
+ endPos: current.range().end.index,
50
+ insertedText: JSON.stringify(value),
51
+ },
52
+ ]);
53
+ }
54
+ return null;
55
+ };
@@ -0,0 +1,48 @@
1
+ import type { NapiConfig, SgNode } from "@ast-grep/napi";
2
+ import { Lang, parse } from "@ast-grep/napi";
3
+ import { dynamicValueKinds } from "./fixtureEdit";
4
+
5
+ /**
6
+ * Matches only `<builder>(<static object literal>)`: exactly one argument that is
7
+ * an object with no identifier, member, call, spread, or computed value inside.
8
+ * A blacklist, not a whitelist — ast-grep has no "every value is a literal".
9
+ */
10
+ export const staticFixtureRule = ({
11
+ builder,
12
+ }: {
13
+ builder: string;
14
+ }): NapiConfig => ({
15
+ rule: {
16
+ all: [{ kind: "call_expression" }, { pattern: `${builder}($ARGUMENTS)` }],
17
+ },
18
+ constraints: {
19
+ ARGUMENTS: {
20
+ kind: "object",
21
+ not: {
22
+ any: dynamicValueKinds.map((dynamicKind) => ({
23
+ has: { kind: dynamicKind, stopBy: "end" },
24
+ })),
25
+ },
26
+ },
27
+ },
28
+ });
29
+
30
+ export const findDynamicFixtures = ({
31
+ source,
32
+ builder,
33
+ }: {
34
+ source: string;
35
+ builder: string;
36
+ }): Array<SgNode> => {
37
+ const root = parse(Lang.TypeScript, source).root();
38
+ const staticIds = new Set(
39
+ root.findAll(staticFixtureRule({ builder })).map((node) => node.id()),
40
+ );
41
+ return root
42
+ .findAll({
43
+ rule: {
44
+ all: [{ kind: "call_expression" }, { pattern: `${builder}($$$)` }],
45
+ },
46
+ })
47
+ .filter((node) => !staticIds.has(node.id()));
48
+ };
package/src/version.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** Replaced at build time by bun.config.ts; falls back when run from source. */
2
+ declare const VERSION: string | undefined;
3
+
4
+ export const version: string =
5
+ typeof VERSION === "string" ? VERSION : "0.0.0-dev";