atmn 2.0.11 → 2.0.12

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.
package/dist/bin.js CHANGED
@@ -4,7 +4,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/cli.ts
6
6
  import { spawnSync as spawnSync3 } from "node:child_process";
7
- import { join as join13 } from "node:path";
7
+ import { join as join14 } from "node:path";
8
8
  import { Command } from "commander";
9
9
 
10
10
  // src/auth/oauthConfig.ts
@@ -6787,7 +6787,7 @@ var createClient = (options) => ({
6787
6787
  });
6788
6788
 
6789
6789
  // src/version.ts
6790
- var version = "2.0.11";
6790
+ var version = "2.0.12";
6791
6791
 
6792
6792
  // src/http/autumnFetch.ts
6793
6793
  var runtime = typeof Bun !== "undefined" ? `bun ${Bun.version}` : `node ${process.versions.node}`;
@@ -7183,8 +7183,8 @@ var fetchOrgInfo = async ({
7183
7183
  };
7184
7184
 
7185
7185
  // src/actions/init/runInit.ts
7186
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
7187
- import { dirname as dirname5, join as join8, relative as relative2, resolve as resolve2 } from "node:path";
7186
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
7187
+ import { dirname as dirname5, join as join9, relative as relative3, resolve as resolve2 } from "node:path";
7188
7188
 
7189
7189
  // src/config/configPackageName.ts
7190
7190
  var PUBLISHED_PACKAGE = "atmn";
@@ -7723,6 +7723,7 @@ import { dirname as dirname3, join as join6 } from "node:path";
7723
7723
  // src/generated/emit.ts
7724
7724
  var COLLECTIONS = {
7725
7725
  features: {
7726
+ wireKey: "features",
7726
7727
  builder: "feature",
7727
7728
  idField: "featureId",
7728
7729
  responseIdField: "id",
@@ -7798,11 +7799,13 @@ var COLLECTIONS = {
7798
7799
  "providerMarkups.*.markup",
7799
7800
  "type"
7800
7801
  ],
7802
+ mappings: { properties: { processors: { mapping: true } } },
7801
7803
  defaults: {},
7802
7804
  pull: true,
7803
7805
  deprecated: []
7804
7806
  },
7805
7807
  plans: {
7808
+ wireKey: "plans",
7806
7809
  builder: "plan",
7807
7810
  idField: "planId",
7808
7811
  responseIdField: "id",
@@ -8583,6 +8586,43 @@ var COLLECTIONS = {
8583
8586
  "variants.versionSlug",
8584
8587
  "versionSlug"
8585
8588
  ],
8589
+ mappings: {
8590
+ properties: {
8591
+ processors: { mapping: true },
8592
+ price: { properties: { processors: { mapping: true } } },
8593
+ items: {
8594
+ items: {
8595
+ properties: {
8596
+ price: { properties: { processors: { mapping: true } } }
8597
+ }
8598
+ },
8599
+ identity: {
8600
+ responseField: "mappingIdentity",
8601
+ components: [
8602
+ { paths: ["featureId"], default: "" },
8603
+ { paths: ["price.billingMethod"], default: "" },
8604
+ { paths: ["price.interval", "reset.interval"], default: "" },
8605
+ {
8606
+ paths: ["price.intervalCount", "reset.intervalCount"],
8607
+ default: "",
8608
+ defaultWhen: { component: 2, value: 1 }
8609
+ }
8610
+ ]
8611
+ }
8612
+ },
8613
+ variants: {
8614
+ items: {
8615
+ properties: {
8616
+ processors: { mapping: true, sourcePath: ["plan", "processors"] }
8617
+ }
8618
+ },
8619
+ identity: {
8620
+ responseField: "mappingIdentity",
8621
+ components: [{ paths: ["variantPlanId"], default: "" }]
8622
+ }
8623
+ }
8624
+ }
8625
+ },
8586
8626
  defaults: {
8587
8627
  addOn: false,
8588
8628
  autoEnable: false,
@@ -8678,6 +8718,7 @@ var COLLECTIONS = {
8678
8718
  ]
8679
8719
  },
8680
8720
  rewards: {
8721
+ wireKey: "rewards",
8681
8722
  builder: "reward",
8682
8723
  idField: "id",
8683
8724
  responseIdField: "id",
@@ -8820,6 +8861,7 @@ var COLLECTIONS = {
8820
8861
  ]
8821
8862
  },
8822
8863
  referralPrograms: {
8864
+ wireKey: "referral_programs",
8823
8865
  builder: "referralProgram",
8824
8866
  idField: "id",
8825
8867
  responseIdField: "id",
@@ -8974,6 +9016,10 @@ var scaffoldConfig = ({
8974
9016
  return configPath;
8975
9017
  };
8976
9018
 
9019
+ // src/actions/skills/ignoreBundledSkills.ts
9020
+ import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
9021
+ import { isAbsolute as isAbsolute2, join as join8, relative as relative2, sep } from "node:path";
9022
+
8977
9023
  // src/actions/skills/skills.ts
8978
9024
  import { spawnSync } from "node:child_process";
8979
9025
  import {
@@ -8988,16 +9034,16 @@ import { dirname as dirname4, join as join7 } from "node:path";
8988
9034
  import chalk3 from "chalk";
8989
9035
 
8990
9036
  // src/generated/skills.ts
8991
- var SKILLS_VERSION = "2.0.11";
9037
+ var SKILLS_VERSION = "2.0.12";
8992
9038
  var SKILLS = [
8993
9039
  {
8994
9040
  name: "autumn-setup",
8995
9041
  description: "First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.",
8996
- version: "2.0.11",
9042
+ version: "2.0.12",
8997
9043
  markdown: `---
8998
9044
  name: autumn-setup
8999
9045
  description: First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.
9000
- version: 2.0.11
9046
+ version: 2.0.12
9001
9047
  ---
9002
9048
 
9003
9049
  # Setup
@@ -9155,11 +9201,11 @@ Then stop. Don't keep building, don't tour the dashboard, don't deploy anything.
9155
9201
  {
9156
9202
  name: "autumn-catalog",
9157
9203
  description: "Modeling a user's pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.",
9158
- version: "2.0.11",
9204
+ version: "2.0.12",
9159
9205
  markdown: `---
9160
9206
  name: autumn-catalog
9161
9207
  description: Modeling a user's pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.
9162
- version: 2.0.11
9208
+ version: 2.0.12
9163
9209
  ---
9164
9210
 
9165
9211
  # Catalog
@@ -13041,11 +13087,11 @@ Pitch: "Pro $20/mo or $200/yr — 1,000 messages per month either way."
13041
13087
  {
13042
13088
  name: "autumn-integrate",
13043
13089
  description: "Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.",
13044
- version: "2.0.11",
13090
+ version: "2.0.12",
13045
13091
  markdown: `---
13046
13092
  name: autumn-integrate
13047
13093
  description: Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.
13048
- version: 2.0.11
13094
+ version: 2.0.12
13049
13095
  ---
13050
13096
 
13051
13097
  # Integrate
@@ -17569,7 +17615,8 @@ Fail-open is **enabled by default**. When Autumn is unreachable (network errors,
17569
17615
  |--------|-----------------|--------|
17570
17616
  | \`check()\` | \`{ allowed: true }\` | Users retain access to features |
17571
17617
  | \`track()\` | \`{ value: 0, balance: null }\` | Usage event is silently dropped |
17572
- | \`customers.getOrCreate()\` | Sentinel customer with \`id: null\` | Returns a valid but empty customer object |
17618
+
17619
+ Customer and entity operations, including \`customers.getOrCreate()\`, \`customers.get()\`, \`entities.get()\`, and \`entities.create()\`, propagate errors even when fail-open is enabled. Only \`check()\` and \`track()\` return fail-open defaults.
17573
17620
 
17574
17621
  When fail-open triggers, the SDK logs a prominent error to your console so you're immediately aware of the issue.
17575
17622
 
@@ -17618,18 +17665,6 @@ const result = await autumn.check({
17618
17665
  // Fail-open response: allowed is always true, customerId is ""
17619
17666
  \`\`\`
17620
17667
 
17621
- When \`customers.getOrCreate()\` fails open, the returned customer will have \`id: null\`:
17622
-
17623
- \`\`\`typescript
17624
- const customer = await autumn.customers.getOrCreate({
17625
- customerId: "cus_123",
17626
- });
17627
-
17628
- if (customer.id === null) {
17629
- // Autumn was unreachable, handle gracefully
17630
- }
17631
- \`\`\`
17632
-
17633
17668
  ## Console output
17634
17669
 
17635
17670
  When fail-open triggers, you'll see this in your server logs:
@@ -17654,11 +17689,11 @@ Monitor your logs for this message. If you see it, check [status.useautumn.com](
17654
17689
  {
17655
17690
  name: "autumn-concepts",
17656
17691
  description: "Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.",
17657
- version: "2.0.11",
17692
+ version: "2.0.12",
17658
17693
  markdown: `---
17659
17694
  name: autumn-concepts
17660
17695
  description: Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.
17661
- version: 2.0.11
17696
+ version: 2.0.12
17662
17697
  ---
17663
17698
 
17664
17699
  # Concepts
@@ -18210,6 +18245,30 @@ var updateSkills = ({
18210
18245
  return { updated };
18211
18246
  };
18212
18247
 
18248
+ // src/actions/skills/ignoreBundledSkills.ts
18249
+ var ignoreBundledSkills = ({
18250
+ repoRoot,
18251
+ configDir
18252
+ }) => {
18253
+ const path = relative2(repoRoot, join8(configDir, SKILLS_DIR_NAME));
18254
+ if (isAbsolute2(path) || path === ".." || path.startsWith(`..${sep}`))
18255
+ return;
18256
+ if (/[\r\n]/.test(path))
18257
+ return;
18258
+ const pattern = `/${path.split(sep).join("/").replace(/[\\*?[\] ]/g, "\\$&")}/`;
18259
+ const ignorePath = join8(repoRoot, ".gitignore");
18260
+ const contents = existsSync8(ignorePath) ? readFileSync7(ignorePath, "utf8") : "";
18261
+ if (contents.split(/\r?\n/).includes(pattern))
18262
+ return;
18263
+ const newline = contents.includes(`\r
18264
+ `) ? `\r
18265
+ ` : `
18266
+ `;
18267
+ const separator = contents.length > 0 && !contents.endsWith(`
18268
+ `) ? newline : "";
18269
+ writeFileSync5(ignorePath, `${contents}${separator}${pattern}${newline}`, "utf8");
18270
+ };
18271
+
18213
18272
  // src/actions/init/runInit.ts
18214
18273
  var DEFAULT_PACKAGE_DIR = "packages/autumn";
18215
18274
  var DEFAULT_CONFIG_DIR = "autumn";
@@ -18294,8 +18353,8 @@ var authenticate = async ({
18294
18353
  `);
18295
18354
  return after.info;
18296
18355
  };
18297
- var readJson2 = (path) => JSON.parse(readFileSync7(path, "utf8"));
18298
- var writeJson2 = (path, value) => writeFileSync5(path, `${JSON.stringify(value, null, "\t")}
18356
+ var readJson2 = (path) => JSON.parse(readFileSync8(path, "utf8"));
18357
+ var writeJson2 = (path, value) => writeFileSync6(path, `${JSON.stringify(value, null, "\t")}
18299
18358
  `, "utf8");
18300
18359
  var packageJsonFor = ({
18301
18360
  name,
@@ -18307,8 +18366,8 @@ var packageJsonFor = ({
18307
18366
  dependencies: { [PACKAGE_NAME]: dependencySpec }
18308
18367
  });
18309
18368
  var ensureRootManifest = ({ repoRoot }) => {
18310
- const manifestPath = join8(repoRoot, "package.json");
18311
- if (!existsSync8(manifestPath))
18369
+ const manifestPath = join9(repoRoot, "package.json");
18370
+ if (!existsSync9(manifestPath))
18312
18371
  writeJson2(manifestPath, { name: "autumn", private: true, type: "module" });
18313
18372
  return manifestPath;
18314
18373
  };
@@ -18316,7 +18375,7 @@ var addDependency = ({
18316
18375
  manifestPath,
18317
18376
  dependencySpec
18318
18377
  }) => {
18319
- if (!existsSync8(manifestPath))
18378
+ if (!existsSync9(manifestPath))
18320
18379
  return false;
18321
18380
  const manifest = readJson2(manifestPath);
18322
18381
  const deps = manifest.dependencies ?? {};
@@ -18330,11 +18389,11 @@ var addDependency = ({
18330
18389
  return true;
18331
18390
  };
18332
18391
  var packageManager = ({ repoRoot }) => {
18333
- if (existsSync8(join8(repoRoot, "bun.lock")) || existsSync8(join8(repoRoot, "bun.lockb")))
18392
+ if (existsSync9(join9(repoRoot, "bun.lock")) || existsSync9(join9(repoRoot, "bun.lockb")))
18334
18393
  return "bun";
18335
- if (existsSync8(join8(repoRoot, "pnpm-lock.yaml")))
18394
+ if (existsSync9(join9(repoRoot, "pnpm-lock.yaml")))
18336
18395
  return "pnpm";
18337
- if (existsSync8(join8(repoRoot, "yarn.lock")))
18396
+ if (existsSync9(join9(repoRoot, "yarn.lock")))
18338
18397
  return "yarn";
18339
18398
  return "npm";
18340
18399
  };
@@ -18373,9 +18432,9 @@ var runInit = async ({
18373
18432
  defaultValue: DEFAULT_PACKAGE_DIR
18374
18433
  });
18375
18434
  configDir = resolve2(repoRoot, chosen);
18376
- prompter.write(`${done(`Path ${relative2(repoRoot, configDir) || "."}`)}
18435
+ prompter.write(`${done(`Path ${relative3(repoRoot, configDir) || "."}`)}
18377
18436
  `);
18378
- const existingName = existsSync8(join8(configDir, "package.json")) ? readJson2(join8(configDir, "package.json")).name : undefined;
18437
+ const existingName = existsSync9(join9(configDir, "package.json")) ? readJson2(join9(configDir, "package.json")).name : undefined;
18379
18438
  packageName = await ask({
18380
18439
  prompter,
18381
18440
  value: name ?? existingName,
@@ -18389,27 +18448,27 @@ var runInit = async ({
18389
18448
  } else {
18390
18449
  const found = resolveProject({ cwd }).configPath;
18391
18450
  configDir = resolve2(repoRoot, path ?? (found === null ? DEFAULT_CONFIG_DIR : dirname5(found)));
18392
- prompter.write(`${done(`Path ${relative2(repoRoot, configDir) || "."}`)}
18451
+ prompter.write(`${done(`Path ${relative3(repoRoot, configDir) || "."}`)}
18393
18452
  `);
18394
18453
  }
18395
- const configPath = join8(configDir, "autumn.config.ts");
18454
+ const configPath = join9(configDir, "autumn.config.ts");
18396
18455
  mkdirSync3(configDir, { recursive: true });
18397
18456
  const wrote = [];
18398
18457
  let dependencyAdded = false;
18399
- const manifestPath = join8(configDir, "package.json");
18400
- if (packageName !== undefined && !existsSync8(manifestPath)) {
18458
+ const manifestPath = join9(configDir, "package.json");
18459
+ if (packageName !== undefined && !existsSync9(manifestPath)) {
18401
18460
  writeJson2(manifestPath, packageJsonFor({ name: packageName, dependencySpec }));
18402
- wrote.push(`${relative2(repoRoot, manifestPath)}`);
18461
+ wrote.push(`${relative3(repoRoot, manifestPath)}`);
18403
18462
  dependencyAdded = true;
18404
18463
  } else if (addDependency({
18405
- manifestPath: existsSync8(manifestPath) ? manifestPath : ensureRootManifest({ repoRoot }),
18464
+ manifestPath: existsSync9(manifestPath) ? manifestPath : ensureRootManifest({ repoRoot }),
18406
18465
  dependencySpec
18407
18466
  })) {
18408
18467
  dependencyAdded = true;
18409
18468
  prompter.write(`${done(`Added ${PACKAGE_NAME} to package.json`)}
18410
18469
  `);
18411
18470
  }
18412
- if (!existsSync8(configPath)) {
18471
+ if (!existsSync9(configPath)) {
18413
18472
  scaffoldConfig({ directory: configDir });
18414
18473
  wrote.push("autumn.config.ts", "features.ts", "plans.ts", "rewards.ts");
18415
18474
  }
@@ -18432,16 +18491,17 @@ var runInit = async ({
18432
18491
  const count = pulled.appended.length + pulled.replaced.length + pulled.deleted.length;
18433
18492
  prompter.write(`${done(count === 0 ? "Sandbox matches the config; nothing to pull" : `Pulled ${count} ${count === 1 ? "entry" : "entries"}`)}
18434
18493
  `);
18435
- const skillsDir = join8(configDir, SKILLS_DIR_NAME);
18494
+ const skillsDir = join9(configDir, SKILLS_DIR_NAME);
18436
18495
  const { written } = installSkills({ dir: skillsDir, write: () => {} });
18437
- prompter.write(`${done(`Skills: ${relative2(repoRoot, skillsDir)}/${written.join(", ")}`)}
18496
+ ignoreBundledSkills({ repoRoot, configDir });
18497
+ prompter.write(`${done(`Skills: ${relative3(repoRoot, skillsDir)}/${written.join(", ")}`)}
18438
18498
  `);
18439
18499
  prompter.write(`
18440
18500
  Next:
18441
18501
  `);
18442
18502
  prompter.write(`${hint(`${runner} atmn push`)} preview your catalog against the sandbox
18443
18503
  `);
18444
- prompter.write(`${hint(`npx skills add ${relative2(repoRoot, skillsDir)} -y`)} make the skills visible to your agent
18504
+ prompter.write(`${hint(`npx skills add ${relative3(repoRoot, skillsDir)} -y`)} make the skills visible to your agent
18445
18505
  `);
18446
18506
  return { repoRoot, configDir, configPath, org };
18447
18507
  };
@@ -18888,33 +18948,33 @@ Wrote ${writtenKeys.join(" and ")} to ${envPath}
18888
18948
 
18889
18949
  // src/actions/pull.ts
18890
18950
  import {
18891
- existsSync as existsSync11,
18951
+ existsSync as existsSync12,
18892
18952
  mkdirSync as mkdirSync4,
18893
- readFileSync as readFileSync12,
18953
+ readFileSync as readFileSync13,
18894
18954
  unlinkSync,
18895
- writeFileSync as writeFileSync8
18955
+ writeFileSync as writeFileSync9
18896
18956
  } from "node:fs";
18897
- import { basename as basename3, dirname as dirname11, join as join12 } from "node:path";
18957
+ import { basename as basename3, dirname as dirname11, join as join13 } from "node:path";
18898
18958
 
18899
18959
  // src/config/loadConfig.ts
18900
18960
  import { spawnSync as spawnSync2 } from "node:child_process";
18901
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
18902
- import { dirname as dirname7, join as join10 } from "node:path";
18961
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
18962
+ import { dirname as dirname7, join as join11 } from "node:path";
18903
18963
 
18904
18964
  // src/surgery/fixtureLocation.ts
18905
- import { readFileSync as readFileSync8 } from "node:fs";
18906
- import { dirname as dirname6, relative as relative3 } from "node:path";
18965
+ import { readFileSync as readFileSync9 } from "node:fs";
18966
+ import { dirname as dirname6, relative as relative4 } from "node:path";
18907
18967
 
18908
18968
  // src/actions/pull/listSourceFiles.ts
18909
18969
  import { readdirSync } from "node:fs";
18910
- import { join as join9 } from "node:path";
18970
+ import { join as join10 } from "node:path";
18911
18971
  var listSourceFiles = ({
18912
18972
  directory
18913
18973
  }) => {
18914
18974
  const files = [];
18915
18975
  const walk = (dir) => {
18916
18976
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
18917
- const path = join9(dir, entry.name);
18977
+ const path = join10(dir, entry.name);
18918
18978
  if (entry.isDirectory()) {
18919
18979
  if (entry.name === "node_modules" || entry.name.startsWith("."))
18920
18980
  continue;
@@ -19092,7 +19152,7 @@ var candidateFixtures = ({
19092
19152
  });
19093
19153
  });
19094
19154
  };
19095
- var findFixture = ({
19155
+ var findFixtures = ({
19096
19156
  source,
19097
19157
  builder,
19098
19158
  idField,
@@ -19101,6 +19161,7 @@ var findFixture = ({
19101
19161
  allowDynamic = false
19102
19162
  }) => {
19103
19163
  const root = parse3(Lang.TypeScript, source).root();
19164
+ const matches = [];
19104
19165
  for (const { node, object } of candidateFixtures({ root, builder })) {
19105
19166
  const idValue = topLevelPairValue({ object, key: idField });
19106
19167
  if (idValue === null || stringLiteralValue(idValue) !== id)
@@ -19109,10 +19170,11 @@ var findFixture = ({
19109
19170
  continue;
19110
19171
  if (where !== undefined && !satisfiesFixtureConstraints({ object, where }))
19111
19172
  continue;
19112
- return node;
19173
+ matches.push(node);
19113
19174
  }
19114
- return null;
19175
+ return matches;
19115
19176
  };
19177
+ var findFixture = (params) => findFixtures(params)[0] ?? null;
19116
19178
  var SIMPLE_ESCAPES = {
19117
19179
  n: `
19118
19180
  `,
@@ -19170,7 +19232,7 @@ var topLevelPairValue = ({
19170
19232
  if (member.kind() !== "pair")
19171
19233
  continue;
19172
19234
  const [name, value] = member.namedChildren();
19173
- if (name?.text() === key && value !== undefined)
19235
+ if (name !== undefined && (name.text() === key || stringLiteralValue(name) === key) && value !== undefined)
19174
19236
  return value;
19175
19237
  }
19176
19238
  return null;
@@ -19196,7 +19258,10 @@ var satisfiesConstraint = ({
19196
19258
  object,
19197
19259
  constraint
19198
19260
  }) => {
19199
- const pair = object.children().find((child) => child.kind() === "pair" && child.namedChildren()[0]?.text() === constraint.field);
19261
+ const pair = object.children().find((child) => {
19262
+ const key = child.field("key");
19263
+ return child.kind() === "pair" && key !== null && (key.text() === constraint.field || stringLiteralValue(key) === constraint.field);
19264
+ });
19200
19265
  if (pair === undefined)
19201
19266
  return constraint.absentMeans === constraint.equals;
19202
19267
  const value = pair.namedChildren()[1];
@@ -19212,7 +19277,8 @@ var locateFixture = ({
19212
19277
  id,
19213
19278
  internalId,
19214
19279
  where,
19215
- allowDynamic = false
19280
+ allowDynamic = false,
19281
+ requireUnique = false
19216
19282
  }) => {
19217
19283
  const others = [...files.keys()].filter((file) => file !== configPath);
19218
19284
  const ordered = [configPath, ...others];
@@ -19222,21 +19288,37 @@ var locateFixture = ({
19222
19288
  ];
19223
19289
  const shapes = Array.isArray(builder) ? builder : [builder];
19224
19290
  for (const attempt of attempts) {
19291
+ const matches = new Map;
19225
19292
  for (const file of ordered) {
19226
19293
  const source = files.get(file);
19227
19294
  if (source === undefined)
19228
19295
  continue;
19229
19296
  for (const shape of shapes) {
19230
- const node = findFixture({
19297
+ const nodes = findFixtures({
19231
19298
  source,
19232
19299
  builder: shape,
19233
19300
  allowDynamic,
19234
19301
  ...attempt
19235
19302
  });
19236
- if (node !== null)
19237
- return { file, source, node, builder: shape, ...attempt };
19303
+ for (const node of nodes) {
19304
+ const located = { file, source, node, builder: shape, ...attempt };
19305
+ if (!requireUnique)
19306
+ return located;
19307
+ const range = node.range();
19308
+ const key = JSON.stringify([
19309
+ file,
19310
+ range.start.index,
19311
+ range.end.index
19312
+ ]);
19313
+ matches.set(key, located);
19314
+ if (matches.size > 1)
19315
+ return null;
19316
+ }
19238
19317
  }
19239
19318
  }
19319
+ const match = matches.values().next().value;
19320
+ if (match !== undefined)
19321
+ return match;
19240
19322
  }
19241
19323
  return null;
19242
19324
  };
@@ -19250,16 +19332,16 @@ var fixtureLocation = ({
19250
19332
  }) => {
19251
19333
  const directory = dirname6(configPath);
19252
19334
  const files = new Map;
19253
- files.set(configPath, readFileSync8(configPath, "utf8"));
19335
+ files.set(configPath, readFileSync9(configPath, "utf8"));
19254
19336
  for (const file of listSourceFiles({ directory })) {
19255
19337
  if (!files.has(file))
19256
- files.set(file, readFileSync8(file, "utf8"));
19338
+ files.set(file, readFileSync9(file, "utf8"));
19257
19339
  }
19258
19340
  const located = locateFixture({ configPath, files, builder, idField, id });
19259
19341
  if (located === null)
19260
19342
  return null;
19261
19343
  return {
19262
- file: relative3(directory, located.file),
19344
+ file: relative4(directory, located.file),
19263
19345
  line: located.node.range().start.line + 1
19264
19346
  };
19265
19347
  };
@@ -19300,8 +19382,8 @@ Run \`${configPackageName()} init\` to create one, or pass -c <dir>.`);
19300
19382
  var findConfigPath = ({ dirs }) => {
19301
19383
  for (const dir of dirs) {
19302
19384
  for (const filename of CONFIG_FILENAMES2) {
19303
- const path = join10(dir, filename);
19304
- if (existsSync9(path))
19385
+ const path = join11(dir, filename);
19386
+ if (existsSync10(path))
19305
19387
  return path;
19306
19388
  }
19307
19389
  }
@@ -19364,10 +19446,10 @@ var loadConfig = async ({
19364
19446
  dirs,
19365
19447
  configPath
19366
19448
  }) => {
19367
- const path = configPath !== undefined ? existsSync9(configPath) ? configPath : null : findConfigPath({ dirs });
19449
+ const path = configPath !== undefined ? existsSync10(configPath) ? configPath : null : findConfigPath({ dirs });
19368
19450
  if (!path)
19369
19451
  throw new ConfigNotFoundError(configPath ? [configPath] : dirs);
19370
- if (isLegacyConfigText({ text: readFileSync9(path, "utf8") })) {
19452
+ if (isLegacyConfigText({ text: readFileSync10(path, "utf8") })) {
19371
19453
  throw new LegacyConfigError({ path });
19372
19454
  }
19373
19455
  let module;
@@ -19417,7 +19499,7 @@ var looksLikeV2Config = ({
19417
19499
  };
19418
19500
 
19419
19501
  // src/project/chooseConfigDir.ts
19420
- import { relative as relative4, resolve as resolve3 } from "node:path";
19502
+ import { relative as relative5, resolve as resolve3 } from "node:path";
19421
19503
  var defaultConfigDirName = ({ cwd }) => findRepoLayout({ cwd }).hasWorkspaces ? "packages/autumn/" : "autumn/";
19422
19504
  var configFiles = ["autumn.config.ts", ...Object.keys(COLLECTION_FILES)];
19423
19505
  var chooseConfigDir = async ({
@@ -19438,7 +19520,7 @@ var chooseConfigDir = async ({
19438
19520
  defaultValue: fallback
19439
19521
  });
19440
19522
  const configDir = resolve3(repoRoot, chosen);
19441
- prompter.write(`${done(`Path ${relative4(repoRoot, configDir) || "."}`)}
19523
+ prompter.write(`${done(`Path ${relative5(repoRoot, configDir) || "."}`)}
19442
19524
  `);
19443
19525
  return { configDir, repoRoot };
19444
19526
  };
@@ -19784,9 +19866,496 @@ var emitFixture = ({
19784
19866
  `);
19785
19867
  };
19786
19868
 
19787
- // src/surgery/appendToBinding.ts
19869
+ // src/generated/mappingAssignments.ts
19870
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19871
+ var valueAt = ({
19872
+ value,
19873
+ path
19874
+ }) => {
19875
+ for (const key of path) {
19876
+ if (typeof value === "symbol")
19877
+ return value;
19878
+ value = isRecord(value) ? value[key] : undefined;
19879
+ }
19880
+ return value;
19881
+ };
19882
+ var mappingIdentityOf = ({
19883
+ value,
19884
+ identity
19885
+ }) => {
19886
+ const values = [];
19887
+ for (const component of identity.components) {
19888
+ let selected;
19889
+ for (const path of component.paths) {
19890
+ selected = valueAt({ value, path: path.split(".") });
19891
+ if (selected !== undefined && selected !== null)
19892
+ break;
19893
+ }
19894
+ if (selected === undefined || selected === null) {
19895
+ selected = component.defaultWhen && values[component.defaultWhen.component] ? component.defaultWhen.value : component.default;
19896
+ }
19897
+ if (typeof selected !== "string" && typeof selected !== "number")
19898
+ return;
19899
+ values.push(selected);
19900
+ }
19901
+ return JSON.stringify(values);
19902
+ };
19903
+ var hasMappings = ({
19904
+ node,
19905
+ value,
19906
+ local = false
19907
+ }) => {
19908
+ if (value === undefined || value === null)
19909
+ return false;
19910
+ if (node.mapping)
19911
+ return true;
19912
+ const items = node.items;
19913
+ if (items)
19914
+ return Array.isArray(value) && value.some((entry) => hasMappings({ node: items, value: entry, local }));
19915
+ return Object.entries(node.properties ?? {}).some(([key, child]) => hasMappings({
19916
+ node: child,
19917
+ value: valueAt({
19918
+ value,
19919
+ path: local ? [key] : child.sourcePath ?? [key]
19920
+ }),
19921
+ local
19922
+ }));
19923
+ };
19924
+ var mappingAssignments = ({
19925
+ projection,
19926
+ local,
19927
+ remote
19928
+ }) => {
19929
+ const assignments = [];
19930
+ const errors = [];
19931
+ const visit = ({
19932
+ node,
19933
+ localValue,
19934
+ remoteValue,
19935
+ path
19936
+ }) => {
19937
+ if (node.mapping) {
19938
+ if (remoteValue === undefined || remoteValue === null) {
19939
+ if (localValue !== undefined)
19940
+ assignments.push({ path, text: null });
19941
+ return;
19942
+ }
19943
+ assignments.push({ path, text: JSON.stringify(remoteValue) });
19944
+ return;
19945
+ }
19946
+ if (!hasMappings({ node, value: remoteValue }) && !hasMappings({ node, value: localValue, local: true }))
19947
+ return;
19948
+ if (remoteValue === undefined || remoteValue === null)
19949
+ return;
19950
+ if (typeof localValue === "symbol") {
19951
+ errors.push(`${path.join(".")}: cannot address a computed value`);
19952
+ return;
19953
+ }
19954
+ if (localValue === undefined || localValue === null) {
19955
+ errors.push(`${path.join(".")}: mapping container is not stated in the fixture`);
19956
+ return;
19957
+ }
19958
+ if (node.items) {
19959
+ if (!Array.isArray(localValue) || !Array.isArray(remoteValue) || !node.identity) {
19960
+ errors.push(`${path.join(".")}: array identity is unavailable`);
19961
+ return;
19962
+ }
19963
+ const identity = node.identity;
19964
+ const localKeys = localValue.map((value) => mappingIdentityOf({ value, identity }));
19965
+ const remoteKeys = remoteValue.map((value) => valueAt({ value, path: identity.responseField.split(".") }));
19966
+ for (const [index, value] of localValue.entries()) {
19967
+ const key = localKeys[index];
19968
+ const matches = remoteKeys.flatMap((candidate, position) => candidate === key ? [position] : []);
19969
+ const match = matches[0];
19970
+ if (key === undefined || match === undefined || localKeys.filter((candidate) => candidate === key).length !== 1 || matches.length !== 1) {
19971
+ errors.push(`${[...path, index].join(".")}: no unique server identity match`);
19972
+ continue;
19973
+ }
19974
+ visit({
19975
+ node: node.items,
19976
+ localValue: value,
19977
+ remoteValue: remoteValue[match],
19978
+ path: [...path, index]
19979
+ });
19980
+ }
19981
+ return;
19982
+ }
19983
+ for (const [key, child] of Object.entries(node.properties ?? {})) {
19984
+ visit({
19985
+ node: child,
19986
+ localValue: valueAt({ value: localValue, path: [key] }),
19987
+ remoteValue: valueAt({
19988
+ value: remoteValue,
19989
+ path: child.sourcePath ?? [key]
19990
+ }),
19991
+ path: [...path, key]
19992
+ });
19993
+ }
19994
+ };
19995
+ visit({ node: projection, localValue: local, remoteValue: remote, path: [] });
19996
+ return { assignments, errors };
19997
+ };
19998
+
19999
+ // src/surgery/patchFixturePaths.ts
20000
+ import { isDeepStrictEqual } from "node:util";
19788
20001
  import { Lang as Lang2, parse as parse4 } from "@ast-grep/napi";
19789
20002
 
20003
+ // src/surgery/appendPropertyEdit.ts
20004
+ var isMember = (child) => child.kind() === "pair" || child.kind() === "spread_element" || child.kind() === "shorthand_property_identifier";
20005
+ var appendPropertyEdit = ({
20006
+ source,
20007
+ object,
20008
+ pair
20009
+ }) => {
20010
+ const closing = object.range().end.index - 1;
20011
+ const multiline = source.slice(object.range().start.index, closing).includes(`
20012
+ `);
20013
+ const members = object.children().filter(isMember);
20014
+ const last = members[members.length - 1];
20015
+ if (last === undefined) {
20016
+ return multiline ? {
20017
+ startPos: lineStartOf(source, closing),
20018
+ endPos: lineStartOf(source, closing),
20019
+ insertedText: `${leadingIndentOfLine(source, object.range().start.index)} ${pair},
20020
+ `
20021
+ } : {
20022
+ startPos: object.range().start.index + 1,
20023
+ endPos: closing,
20024
+ insertedText: ` ${pair} `
20025
+ };
20026
+ }
20027
+ const lastEnd = last.range().end.index;
20028
+ const between = source.slice(lastEnd, closing);
20029
+ const trailingComma = between.trimStart().startsWith(",");
20030
+ const afterComma = trailingComma ? lastEnd + between.indexOf(",") + 1 : lastEnd;
20031
+ if (multiline) {
20032
+ const indent = leadingIndentOfLine(source, last.range().start.index);
20033
+ return {
20034
+ startPos: afterComma,
20035
+ endPos: afterComma,
20036
+ insertedText: trailingComma ? `
20037
+ ${indent}${pair},` : `,
20038
+ ${indent}${pair},`
20039
+ };
20040
+ }
20041
+ return {
20042
+ startPos: afterComma,
20043
+ endPos: afterComma,
20044
+ insertedText: trailingComma ? ` ${pair},` : `, ${pair}`
20045
+ };
20046
+ };
20047
+ var holdsSpread = ({ object }) => object.children().some((child) => child.kind() === "spread_element");
20048
+
20049
+ // src/surgery/readFixtureValue.ts
20050
+ var DYNAMIC_VALUE = Symbol("dynamic-fixture-value");
20051
+ var fixtureKeyOf = ({ node }) => {
20052
+ if (node.kind() === "property_identifier" || node.kind() === "shorthand_property_identifier")
20053
+ return node.text();
20054
+ return stringLiteralValue(node);
20055
+ };
20056
+ var fixtureArrayElements = ({
20057
+ node
20058
+ }) => {
20059
+ const elements = [];
20060
+ let expectsElement = true;
20061
+ for (const child of node.children()) {
20062
+ const kind = child.kind();
20063
+ if (kind === "[" || kind === "]" || kind === "comment")
20064
+ continue;
20065
+ if (kind === ",") {
20066
+ if (expectsElement)
20067
+ return null;
20068
+ expectsElement = true;
20069
+ continue;
20070
+ }
20071
+ if (!expectsElement || kind === "spread_element")
20072
+ return null;
20073
+ elements.push(child);
20074
+ expectsElement = false;
20075
+ }
20076
+ return elements;
20077
+ };
20078
+ var readFixtureValue = ({
20079
+ node,
20080
+ builders = []
20081
+ }) => {
20082
+ const kind = node.kind();
20083
+ if (kind === "call_expression" && builders.includes(node.field("function")?.text() ?? "")) {
20084
+ const args = node.field("arguments")?.namedChildren().filter((child) => child.kind() !== "comment");
20085
+ return args?.length === 1 && args[0]?.kind() === "object" ? readFixtureValue({ node: args[0], builders }) : DYNAMIC_VALUE;
20086
+ }
20087
+ if (kind === "string")
20088
+ return stringLiteralValue(node) ?? DYNAMIC_VALUE;
20089
+ if (kind === "true")
20090
+ return true;
20091
+ if (kind === "false")
20092
+ return false;
20093
+ if (kind === "null")
20094
+ return null;
20095
+ if (kind === "number" || kind === "unary_expression" && /^[+-]\s*[\d.]/.test(node.text())) {
20096
+ const value2 = Number(node.text().replaceAll("_", "").replace(/\s/g, ""));
20097
+ return Number.isFinite(value2) ? value2 : DYNAMIC_VALUE;
20098
+ }
20099
+ if (kind === "array") {
20100
+ const elements = fixtureArrayElements({ node });
20101
+ return elements === null ? DYNAMIC_VALUE : elements.map((element) => readFixtureValue({ node: element, builders }));
20102
+ }
20103
+ if (kind !== "object")
20104
+ return DYNAMIC_VALUE;
20105
+ const value = {};
20106
+ for (const member of node.namedChildren()) {
20107
+ if (member.kind() === "comment")
20108
+ continue;
20109
+ const shorthand = member.kind() === "shorthand_property_identifier";
20110
+ if (member.kind() !== "pair" && !shorthand)
20111
+ return DYNAMIC_VALUE;
20112
+ const keyNode = shorthand ? member : member.field("key");
20113
+ const key = keyNode === null ? null : fixtureKeyOf({ node: keyNode });
20114
+ if (key === null || Object.getOwnPropertyDescriptor(value, key) !== undefined)
20115
+ return DYNAMIC_VALUE;
20116
+ const child = member.field("value");
20117
+ Object.defineProperty(value, key, {
20118
+ value: child === null ? DYNAMIC_VALUE : readFixtureValue({ node: child, builders }),
20119
+ enumerable: true
20120
+ });
20121
+ }
20122
+ return value;
20123
+ };
20124
+
20125
+ // src/surgery/patchFixturePaths.ts
20126
+ var isDynamic = (value) => {
20127
+ if (value === DYNAMIC_VALUE)
20128
+ return true;
20129
+ if (value === null || typeof value !== "object")
20130
+ return false;
20131
+ return Object.values(value).some(isDynamic);
20132
+ };
20133
+ var matchesLiteral = ({
20134
+ value,
20135
+ text
20136
+ }) => {
20137
+ const root = parse4(Lang2.TypeScript, `const value = (${text});`).root();
20138
+ if (root.find({ rule: { kind: "ERROR" } }) !== null)
20139
+ return false;
20140
+ const expression = root.find({ rule: { kind: "variable_declarator" } })?.field("value");
20141
+ if (expression?.kind() !== "parenthesized_expression")
20142
+ return false;
20143
+ const literal = expression.namedChildren().filter((node) => node.kind() !== "comment");
20144
+ if (literal.length !== 1 || literal[0] === undefined)
20145
+ return false;
20146
+ const proposed = readFixtureValue({ node: literal[0] });
20147
+ return !isDynamic(proposed) && isDeepStrictEqual(value, proposed);
20148
+ };
20149
+ var removeMemberEdits = ({ member }) => {
20150
+ const siblings = member.parent()?.children() ?? [];
20151
+ const index = siblings.findIndex((sibling) => sibling.range().start.index === member.range().start.index);
20152
+ const next = siblings.slice(index + 1).find((sibling) => sibling.kind() !== "comment");
20153
+ const previous = siblings.slice(0, index).reverse().find((sibling) => sibling.kind() !== "comment");
20154
+ const comma = next?.kind() === "," ? next : previous?.kind() === "," ? previous : null;
20155
+ return [member, ...comma === null ? [] : [comma]].map((node) => ({
20156
+ startPos: node.range().start.index,
20157
+ endPos: node.range().end.index,
20158
+ insertedText: ""
20159
+ }));
20160
+ };
20161
+ var pathEdits = ({
20162
+ source,
20163
+ object,
20164
+ path,
20165
+ text,
20166
+ builders
20167
+ }) => {
20168
+ let current = object;
20169
+ for (let index = 0;index < path.length; index += 1) {
20170
+ if (current.kind() === "call_expression" && builders.includes(current.field("function")?.text() ?? "")) {
20171
+ const args = current.field("arguments")?.namedChildren().filter((child) => child.kind() !== "comment");
20172
+ if (args?.length !== 1 || args[0]?.kind() !== "object")
20173
+ return "dynamic-value";
20174
+ current = args[0];
20175
+ }
20176
+ const segment = path[index];
20177
+ const last = index === path.length - 1;
20178
+ let member;
20179
+ let value;
20180
+ if (typeof segment === "number") {
20181
+ if (current.kind() !== "array" || !Number.isSafeInteger(segment) || segment < 0)
20182
+ return "invalid-array-path";
20183
+ const elements = fixtureArrayElements({ node: current });
20184
+ if (elements === null)
20185
+ return "dynamic-array";
20186
+ value = elements[segment];
20187
+ if (value === undefined)
20188
+ return "missing-array-element";
20189
+ if (last && text === null)
20190
+ return "array-element-removal";
20191
+ } else {
20192
+ if (current.kind() !== "object")
20193
+ return "non-object-container";
20194
+ if (readFixtureValue({ node: current }) === DYNAMIC_VALUE)
20195
+ return "ambiguous-object";
20196
+ member = current.namedChildren().find((child) => {
20197
+ const key = child.kind() === "pair" ? child.field("key") : child;
20198
+ return key !== null && fixtureKeyOf({ node: key }) === segment;
20199
+ });
20200
+ if (member === undefined) {
20201
+ if (text === null)
20202
+ return [];
20203
+ const remaining = path.slice(index + 1);
20204
+ if (remaining.some((key) => typeof key === "number"))
20205
+ return "missing-array-element";
20206
+ let inserted = text;
20207
+ for (const key of remaining.reverse())
20208
+ inserted = `{ ${JSON.stringify(key)}: ${inserted} }`;
20209
+ const pair = `${JSON.stringify(segment)}: ${inserted}`;
20210
+ const members = current.namedChildren().filter((child) => child.kind() !== "comment");
20211
+ if (members.length === 0) {
20212
+ const closing = current.range().end.index - 1;
20213
+ return [
20214
+ { startPos: closing, endPos: closing, insertedText: ` ${pair} ` }
20215
+ ];
20216
+ }
20217
+ return [appendPropertyEdit({ source, object: current, pair })];
20218
+ }
20219
+ value = member.field("value");
20220
+ }
20221
+ if (value == null)
20222
+ return "dynamic-value";
20223
+ if (!last) {
20224
+ current = value;
20225
+ continue;
20226
+ }
20227
+ const currentValue = readFixtureValue({ node: value });
20228
+ if (isDynamic(currentValue))
20229
+ return "dynamic-value";
20230
+ if (text === null)
20231
+ return member === undefined ? "array-element-removal" : removeMemberEdits({ member });
20232
+ if (matchesLiteral({ value: currentValue, text }))
20233
+ return [];
20234
+ return [
20235
+ {
20236
+ startPos: value.range().start.index,
20237
+ endPos: value.range().end.index,
20238
+ insertedText: text
20239
+ }
20240
+ ];
20241
+ }
20242
+ return "empty-path";
20243
+ };
20244
+ var patchFixturePaths = ({
20245
+ source,
20246
+ builder,
20247
+ idField,
20248
+ id,
20249
+ where,
20250
+ assignments,
20251
+ builders = []
20252
+ }) => {
20253
+ const selector = { builder, idField, id, where, allowDynamic: true };
20254
+ const initial = findFixtures({ source, ...selector });
20255
+ if (initial.length === 0)
20256
+ return null;
20257
+ const skipped = [];
20258
+ for (const assignment of assignments) {
20259
+ const matches = findFixtures({ source, ...selector });
20260
+ const match = matches[0];
20261
+ const object = matches.length === 1 && match !== undefined ? fixtureObjectOf(match) : null;
20262
+ const edits = object === null ? "ambiguous-fixture" : pathEdits({ source, object, builders, ...assignment });
20263
+ if (typeof edits === "string") {
20264
+ skipped.push({ path: assignment.path, reason: edits });
20265
+ continue;
20266
+ }
20267
+ source = parse4(Lang2.TypeScript, source).root().commitEdits(edits);
20268
+ }
20269
+ return { source, skipped };
20270
+ };
20271
+
20272
+ // src/actions/pull/applyMappings.ts
20273
+ var applyMappings = ({
20274
+ catalog,
20275
+ configPath,
20276
+ files
20277
+ }) => {
20278
+ const replaced = [];
20279
+ const unlocated = [];
20280
+ const builders = Object.values(NESTED_FIXTURES).map(({ builder }) => builder);
20281
+ for (const [collection, collectionSpec] of Object.entries(COLLECTIONS)) {
20282
+ const rows = catalog[collection];
20283
+ if (!collectionSpec.pull || !Array.isArray(rows))
20284
+ continue;
20285
+ for (const entry of rows) {
20286
+ const { spec, row } = resolveBranch({ spec: collectionSpec, row: entry });
20287
+ const id = row[spec.responseIdField];
20288
+ if (!spec.mappings || typeof id !== "string" || row.archived === true)
20289
+ continue;
20290
+ const key = spec.versioned ? `${id}@${row.versionSlug ?? "v1"}` : id;
20291
+ const located = locateFixture({
20292
+ configPath,
20293
+ files,
20294
+ builder: spec.builder,
20295
+ idField: spec.idField,
20296
+ id,
20297
+ internalId: typeof row.internalId === "string" ? row.internalId : undefined,
20298
+ where: spec.versioned ? [
20299
+ {
20300
+ field: "versionSlug",
20301
+ equals: typeof row.versionSlug === "string" ? row.versionSlug : "v1",
20302
+ absentMeans: "v1"
20303
+ }
20304
+ ] : undefined,
20305
+ allowDynamic: true,
20306
+ requireUnique: true
20307
+ });
20308
+ if (!located) {
20309
+ unlocated.push({
20310
+ collection,
20311
+ id: key,
20312
+ action: "cannot locate the fixture to write processor mappings"
20313
+ });
20314
+ continue;
20315
+ }
20316
+ const planned = mappingAssignments({
20317
+ projection: spec.mappings,
20318
+ local: readFixtureValue({
20319
+ node: located.node,
20320
+ builders: [spec.builder, ...builders]
20321
+ }),
20322
+ remote: row
20323
+ });
20324
+ if (planned.errors.length) {
20325
+ unlocated.push(...planned.errors.map((action) => ({ collection, id: key, action })));
20326
+ continue;
20327
+ }
20328
+ if (!planned.assignments.length)
20329
+ continue;
20330
+ const patched = patchFixturePaths({
20331
+ source: located.source,
20332
+ builder: located.builder,
20333
+ idField: located.idField,
20334
+ id: located.id,
20335
+ where: located.where,
20336
+ builders,
20337
+ assignments: planned.assignments
20338
+ });
20339
+ if (!patched || patched.skipped.length) {
20340
+ unlocated.push({
20341
+ collection,
20342
+ id: key,
20343
+ action: "cannot safely splice processor mappings into a computed or ambiguous value"
20344
+ });
20345
+ continue;
20346
+ }
20347
+ if (patched.source === located.source)
20348
+ continue;
20349
+ files.set(located.file, patched.source);
20350
+ replaced.push(key);
20351
+ }
20352
+ }
20353
+ return { replaced, unlocated };
20354
+ };
20355
+
20356
+ // src/surgery/appendToBinding.ts
20357
+ import { Lang as Lang3, parse as parse5 } from "@ast-grep/napi";
20358
+
19790
20359
  // src/surgery/appendToArray.ts
19791
20360
  var appendElementToArray = ({
19792
20361
  source,
@@ -19887,7 +20456,7 @@ var appendToBinding = ({
19887
20456
  name,
19888
20457
  text
19889
20458
  }) => {
19890
- const root = parse4(Lang2.TypeScript, source).root();
20459
+ const root = parse5(Lang3.TypeScript, source).root();
19891
20460
  const array = findArrayBinding({ root, name });
19892
20461
  if (array === null)
19893
20462
  return null;
@@ -19895,13 +20464,13 @@ var appendToBinding = ({
19895
20464
  };
19896
20465
 
19897
20466
  // src/surgery/appendToCollection.ts
19898
- import { Lang as Lang3, parse as parse5 } from "@ast-grep/napi";
20467
+ import { Lang as Lang4, parse as parse6 } from "@ast-grep/napi";
19899
20468
  var appendToCollection = ({
19900
20469
  source,
19901
20470
  collection,
19902
20471
  text
19903
20472
  }) => {
19904
- const root = parse5(Lang3.TypeScript, source).root();
20473
+ const root = parse6(Lang4.TypeScript, source).root();
19905
20474
  const anchor = root.find(`atmn({ $$$, ${collection}: [$$$ITEMS], $$$ })`) ?? root.find(`atmn({ ${collection}: [$$$ITEMS] })`);
19906
20475
  if (anchor === null)
19907
20476
  return null;
@@ -19926,55 +20495,7 @@ var collectionArray = ({
19926
20495
  };
19927
20496
 
19928
20497
  // src/surgery/appendToFixtureArray.ts
19929
- import { Lang as Lang4, parse as parse6 } from "@ast-grep/napi";
19930
-
19931
- // src/surgery/appendPropertyEdit.ts
19932
- var isMember = (child) => child.kind() === "pair" || child.kind() === "spread_element";
19933
- var appendPropertyEdit = ({
19934
- source,
19935
- object,
19936
- pair
19937
- }) => {
19938
- const closing = object.range().end.index - 1;
19939
- const multiline = source.slice(object.range().start.index, closing).includes(`
19940
- `);
19941
- const members = object.children().filter(isMember);
19942
- const last = members[members.length - 1];
19943
- if (last === undefined) {
19944
- return multiline ? {
19945
- startPos: lineStartOf(source, closing),
19946
- endPos: lineStartOf(source, closing),
19947
- insertedText: `${leadingIndentOfLine(source, object.range().start.index)} ${pair},
19948
- `
19949
- } : {
19950
- startPos: object.range().start.index + 1,
19951
- endPos: closing,
19952
- insertedText: ` ${pair} `
19953
- };
19954
- }
19955
- const lastEnd = last.range().end.index;
19956
- const between = source.slice(lastEnd, closing);
19957
- const trailingComma = between.trimStart().startsWith(",");
19958
- const afterComma = trailingComma ? lastEnd + between.indexOf(",") + 1 : lastEnd;
19959
- if (multiline) {
19960
- const indent = leadingIndentOfLine(source, last.range().start.index);
19961
- return {
19962
- startPos: afterComma,
19963
- endPos: afterComma,
19964
- insertedText: trailingComma ? `
19965
- ${indent}${pair},` : `,
19966
- ${indent}${pair},`
19967
- };
19968
- }
19969
- return {
19970
- startPos: afterComma,
19971
- endPos: afterComma,
19972
- insertedText: trailingComma ? ` ${pair},` : `, ${pair}`
19973
- };
19974
- };
19975
- var holdsSpread = ({ object }) => object.children().some((child) => child.kind() === "spread_element");
19976
-
19977
- // src/surgery/appendToFixtureArray.ts
20498
+ import { Lang as Lang5, parse as parse7 } from "@ast-grep/napi";
19978
20499
  var appendToFixtureArray = ({
19979
20500
  source,
19980
20501
  builder,
@@ -19997,7 +20518,7 @@ var appendToFixtureArray = ({
19997
20518
  const object = fixtureObjectOf(call);
19998
20519
  if (object === null)
19999
20520
  return null;
20000
- const root = parse6(Lang4.TypeScript, source).root();
20521
+ const root = parse7(Lang5.TypeScript, source).root();
20001
20522
  const pair = object.children().find((member) => member.kind() === "pair" && member.namedChildren()[0]?.text() === property);
20002
20523
  if (pair === undefined) {
20003
20524
  const seeded = root.commitEdits([
@@ -20085,12 +20606,12 @@ var exportRemovalEdit = ({
20085
20606
  };
20086
20607
 
20087
20608
  // src/surgery/deleteReference.ts
20088
- import { Lang as Lang5, parse as parse7 } from "@ast-grep/napi";
20609
+ import { Lang as Lang6, parse as parse8 } from "@ast-grep/napi";
20089
20610
  var deleteReference = ({
20090
20611
  source,
20091
20612
  name
20092
20613
  }) => {
20093
- const root = parse7(Lang5.TypeScript, source).root();
20614
+ const root = parse8(Lang6.TypeScript, source).root();
20094
20615
  const edits = [];
20095
20616
  for (const identifier of root.findAll({
20096
20617
  rule: { kind: "identifier", pattern: name }
@@ -20124,7 +20645,7 @@ var deleteReference = ({
20124
20645
  };
20125
20646
 
20126
20647
  // src/surgery/ensureBuilderImport.ts
20127
- import { Lang as Lang6, parse as parse8 } from "@ast-grep/napi";
20648
+ import { Lang as Lang7, parse as parse9 } from "@ast-grep/napi";
20128
20649
  var NAMED_IMPORT = /^import\s+\{([^}]*)\}\s*from\s*["']([^"']+)["']/;
20129
20650
  var GENERATED_MODULE = /\/generated\/[A-Za-z]+$/;
20130
20651
  var ensureBuilderImport = ({
@@ -20132,7 +20653,7 @@ var ensureBuilderImport = ({
20132
20653
  builder,
20133
20654
  collection
20134
20655
  }) => {
20135
- const root = parse8(Lang6.TypeScript, source).root();
20656
+ const root = parse9(Lang7.TypeScript, source).root();
20136
20657
  const statements = root.findAll({ rule: { kind: "import_statement" } });
20137
20658
  const parsed = statements.flatMap((statement) => {
20138
20659
  const match = NAMED_IMPORT.exec(statement.text());
@@ -20170,9 +20691,9 @@ var ensureBuilderImport = ({
20170
20691
  };
20171
20692
 
20172
20693
  // src/surgery/insertCollection.ts
20173
- import { Lang as Lang7, parse as parse9 } from "@ast-grep/napi";
20694
+ import { Lang as Lang8, parse as parse10 } from "@ast-grep/napi";
20174
20695
  var rootSpreadNames = ({ source }) => {
20175
- const root = parse9(Lang7.TypeScript, source).root();
20696
+ const root = parse10(Lang8.TypeScript, source).root();
20176
20697
  const call = root.find("atmn($ARG)");
20177
20698
  const object = call?.getMatch("ARG");
20178
20699
  if (object === null || object === undefined || object.kind() !== "object")
@@ -20183,7 +20704,7 @@ var insertCollection = ({
20183
20704
  source,
20184
20705
  collection
20185
20706
  }) => {
20186
- const root = parse9(Lang7.TypeScript, source).root();
20707
+ const root = parse10(Lang8.TypeScript, source).root();
20187
20708
  const call = root.find("atmn($ARG)");
20188
20709
  if (call === null)
20189
20710
  return null;
@@ -20237,7 +20758,7 @@ ${indent}${collection}: [],`
20237
20758
  };
20238
20759
 
20239
20760
  // src/surgery/patchFixtureProperty.ts
20240
- import { Lang as Lang8, parse as parse10 } from "@ast-grep/napi";
20761
+ import { Lang as Lang9, parse as parse11 } from "@ast-grep/napi";
20241
20762
  var pairFor = ({
20242
20763
  object,
20243
20764
  property
@@ -20328,7 +20849,7 @@ var patchFixtureProperty = ({
20328
20849
  const object = fixtureObjectOf(call);
20329
20850
  if (object === null)
20330
20851
  return null;
20331
- const root = parse10(Lang8.TypeScript, source).root();
20852
+ const root = parse11(Lang9.TypeScript, source).root();
20332
20853
  const pair = pairFor({ object, property });
20333
20854
  if (text === null) {
20334
20855
  if (pair === null)
@@ -20433,7 +20954,7 @@ var changedFixtureKeys = ({
20433
20954
 
20434
20955
  // src/actions/pull/resolveCollectionTarget.ts
20435
20956
  import { dirname as dirname8, resolve as resolve4 } from "node:path";
20436
- import { Lang as Lang9, parse as parse11 } from "@ast-grep/napi";
20957
+ import { Lang as Lang10, parse as parse12 } from "@ast-grep/napi";
20437
20958
  var NAMED_IMPORT2 = /import\s+(?:type\s+)?\{([^}]*)\}\s*from\s*["']([^"']+)["']/;
20438
20959
  var resolveCollectionTarget = ({
20439
20960
  configPath,
@@ -20444,7 +20965,7 @@ var resolveCollectionTarget = ({
20444
20965
  const source = files.get(configPath);
20445
20966
  if (source === undefined)
20446
20967
  return null;
20447
- const root = parse11(Lang9.TypeScript, source).root();
20968
+ const root = parse12(Lang10.TypeScript, source).root();
20448
20969
  const value = collectionValue({ root, collection });
20449
20970
  if (value === null)
20450
20971
  return null;
@@ -20484,7 +21005,7 @@ var resolveBinding = ({
20484
21005
  });
20485
21006
  if (file === null)
20486
21007
  return null;
20487
- const module = parse11(Lang9.TypeScript, files.get(file) ?? "").root();
21008
+ const module = parse12(Lang10.TypeScript, files.get(file) ?? "").root();
20488
21009
  return findLiteralBinding({
20489
21010
  root: module,
20490
21011
  name: imported.exportedName,
@@ -20956,7 +21477,7 @@ var applyPreview = ({
20956
21477
  };
20957
21478
 
20958
21479
  // src/surgery/patchSingletonProperty.ts
20959
- import { Lang as Lang10, parse as parse12 } from "@ast-grep/napi";
21480
+ import { Lang as Lang11, parse as parse13 } from "@ast-grep/napi";
20960
21481
  var atmnObject = (root) => {
20961
21482
  const object = root.find("atmn($ARG)")?.getMatch("ARG") ?? null;
20962
21483
  return object !== null && object.kind() === "object" ? object : null;
@@ -21030,7 +21551,7 @@ var patchSingletonProperty = ({
21030
21551
  block,
21031
21552
  edit
21032
21553
  }) => {
21033
- const root = parse12(Lang10.TypeScript, source).root();
21554
+ const root = parse13(Lang11.TypeScript, source).root();
21034
21555
  const object = blockObject({ root, block });
21035
21556
  if (object === null)
21036
21557
  return null;
@@ -21067,7 +21588,7 @@ var insertSingleton = ({
21067
21588
  source,
21068
21589
  singleton
21069
21590
  }) => {
21070
- const root = parse12(Lang10.TypeScript, source).root();
21591
+ const root = parse13(Lang11.TypeScript, source).root();
21071
21592
  const object = atmnObject(root);
21072
21593
  if (object === null)
21073
21594
  return null;
@@ -21080,7 +21601,7 @@ var insertSingleton = ({
21080
21601
  const withArray = insertCollection({ source, collection: singleton });
21081
21602
  if (withArray === null || withArray === source)
21082
21603
  return withArray;
21083
- const seededRoot = parse12(Lang10.TypeScript, withArray).root();
21604
+ const seededRoot = parse13(Lang11.TypeScript, withArray).root();
21084
21605
  const seeded = atmnObject(seededRoot);
21085
21606
  const pair = seeded === null ? null : pairFor2({ object: seeded, key: singleton });
21086
21607
  const value = pair?.namedChildren()[1];
@@ -21170,8 +21691,8 @@ var applySettingsPreview = ({
21170
21691
  };
21171
21692
 
21172
21693
  // src/actions/pull/rewriteConfig.ts
21173
- import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
21174
- import { dirname as dirname9, join as join11 } from "node:path";
21694
+ import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
21695
+ import { dirname as dirname9, join as join12 } from "node:path";
21175
21696
  var PACKAGE_NAMES = ["atmn", "atmn-nightly"];
21176
21697
  var importsConfigPackage = ({
21177
21698
  text,
@@ -21186,17 +21707,17 @@ var rewriteConfig = ({
21186
21707
  const rewritten = [];
21187
21708
  const kept = [];
21188
21709
  for (const [file, collections] of Object.entries(COLLECTION_FILES)) {
21189
- const path = join11(configDir, file);
21190
- const isUsers = existsSync10(path) && !importsConfigPackage({ text: readFileSync10(path, "utf8"), imports });
21710
+ const path = join12(configDir, file);
21711
+ const isUsers = existsSync11(path) && !importsConfigPackage({ text: readFileSync11(path, "utf8"), imports });
21191
21712
  if (isUsers) {
21192
21713
  kept.push(path);
21193
21714
  continue;
21194
21715
  }
21195
- writeFileSync6(path, collectionFileSource({ collections, imports }), "utf8");
21716
+ writeFileSync7(path, collectionFileSource({ collections, imports }), "utf8");
21196
21717
  importedFiles.add(file);
21197
21718
  rewritten.push(path);
21198
21719
  }
21199
- writeFileSync6(configPath, rootSource({ imports, importedFiles }), "utf8");
21720
+ writeFileSync7(configPath, rootSource({ imports, importedFiles }), "utf8");
21200
21721
  return { rewritten: [configPath, ...rewritten], kept };
21201
21722
  };
21202
21723
 
@@ -21733,7 +22254,7 @@ var previewIsEmpty = ({
21733
22254
  }) => !(preview.features ?? []).some(rowHasWork) && !(preview.plans ?? []).some(rowHasWork) && !(preview.rewards ?? []).some(rowHasWork) && !(preview.referralPrograms ?? []).some(rowHasWork) && !settingsHaveWork({ settings: preview.settings });
21734
22255
 
21735
22256
  // src/actions/push/backfillInternalIds.ts
21736
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
22257
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
21737
22258
  import { dirname as dirname10 } from "node:path";
21738
22259
 
21739
22260
  // src/surgery/insertFirstProperty.ts
@@ -21784,7 +22305,7 @@ ${indent}`
21784
22305
  };
21785
22306
 
21786
22307
  // src/surgery/setFixtureProperty.ts
21787
- import { Lang as Lang11, parse as parse13 } from "@ast-grep/napi";
22308
+ import { Lang as Lang12, parse as parse14 } from "@ast-grep/napi";
21788
22309
  var setFixtureProperty = ({
21789
22310
  source,
21790
22311
  builder,
@@ -21813,7 +22334,7 @@ var setFixtureProperty = ({
21813
22334
  const [key, current] = member.namedChildren();
21814
22335
  if (key?.text() !== property || current === undefined)
21815
22336
  continue;
21816
- const root = parse13(Lang11.TypeScript, source).root();
22337
+ const root = parse14(Lang12.TypeScript, source).root();
21817
22338
  return root.commitEdits([
21818
22339
  {
21819
22340
  startPos: current.range().start.index,
@@ -21863,10 +22384,10 @@ var backfillInternalIds = ({
21863
22384
  configPath
21864
22385
  }) => {
21865
22386
  const files = new Map;
21866
- files.set(configPath, readFileSync11(configPath, "utf8"));
22387
+ files.set(configPath, readFileSync12(configPath, "utf8"));
21867
22388
  for (const file of listSourceFiles({ directory: dirname10(configPath) })) {
21868
22389
  if (!files.has(file))
21869
- files.set(file, readFileSync11(file, "utf8"));
22390
+ files.set(file, readFileSync12(file, "utf8"));
21870
22391
  }
21871
22392
  const originals = new Map(files);
21872
22393
  const backfilled = [];
@@ -22100,7 +22621,7 @@ var backfillInternalIds = ({
22100
22621
  }
22101
22622
  for (const [file, source] of files) {
22102
22623
  if (source !== originals.get(file))
22103
- writeFileSync7(file, source, "utf8");
22624
+ writeFileSync8(file, source, "utf8");
22104
22625
  }
22105
22626
  return { backfilled, slugged };
22106
22627
  };
@@ -22426,7 +22947,7 @@ var runPull = async ({
22426
22947
  write(`This rewrites autumn.config.ts and the features.ts, plans.ts and rewards.ts beside it from your org's catalog. Other files are left alone. Re-run with --yes to overwrite.
22427
22948
  `);
22428
22949
  return {
22429
- configPath: project.configPath ?? join12(project.configDir, "autumn.config.ts"),
22950
+ configPath: project.configPath ?? join13(project.configDir, "autumn.config.ts"),
22430
22951
  appended: [],
22431
22952
  replaced: [],
22432
22953
  deleted: []
@@ -22439,7 +22960,7 @@ var runPull = async ({
22439
22960
  if (project.configPath === null) {
22440
22961
  const chosen = await chooseConfigDir({ cwd, prompter });
22441
22962
  scaffoldDir = chosen.configDir;
22442
- scaffoldPath = join12(scaffoldDir, "autumn.config.ts");
22963
+ scaffoldPath = join13(scaffoldDir, "autumn.config.ts");
22443
22964
  if (scaffoldDir !== cwd && writeRootMarker({ repoRoot: chosen.repoRoot, configPath: scaffoldPath }))
22444
22965
  prompter.write(`${done("Recorded in package.json so atmn finds it from anywhere")}
22445
22966
  `);
@@ -22460,11 +22981,11 @@ var runPull = async ({
22460
22981
  client.previewUpdateOrganization(singletons.settings ?? { config: {} })
22461
22982
  ]);
22462
22983
  const files = new Map;
22463
- files.set(configPath, readFileSync12(configPath, "utf8"));
22984
+ files.set(configPath, readFileSync13(configPath, "utf8"));
22464
22985
  for (const file of listSourceFiles({ directory: dirname11(configPath) })) {
22465
22986
  if (file === configPath || files.has(file))
22466
22987
  continue;
22467
- files.set(file, readFileSync12(file, "utf8"));
22988
+ files.set(file, readFileSync13(file, "utf8"));
22468
22989
  }
22469
22990
  const originals = new Map(files);
22470
22991
  const appended = [];
@@ -22512,16 +23033,30 @@ var runPull = async ({
22512
23033
  action: `set by hand: \`${singleton}\` is not an object literal`
22513
23034
  })));
22514
23035
  }
23036
+ if (includeMappings) {
23037
+ const managedCatalog = Object.fromEntries(Object.entries(COLLECTIONS).filter(([, spec]) => spec.wireKey !== undefined && wire[spec.wireKey] !== undefined).map(([collection]) => [collection, catalogRows[collection]]));
23038
+ const mapped = applyMappings({
23039
+ catalog: managedCatalog,
23040
+ configPath,
23041
+ files
23042
+ });
23043
+ unlocated.push(...mapped.unlocated);
23044
+ for (const id of mapped.replaced) {
23045
+ if (!replaced.includes(id) && !appended.includes(id))
23046
+ replaced.push(id);
23047
+ lines.push(`↳ wrote processor mappings into ${id}`);
23048
+ }
23049
+ }
22515
23050
  if (unlocated.length > 0)
22516
23051
  throw new UnlocatableFixturesError({ unlocated });
22517
23052
  for (const [file, source] of files) {
22518
23053
  if (source === originals.get(file))
22519
23054
  continue;
22520
23055
  mkdirSync4(dirname11(file), { recursive: true });
22521
- writeFileSync8(file, source, "utf8");
23056
+ writeFileSync9(file, source, "utf8");
22522
23057
  if (!originals.has(file)) {
22523
- const keep = join12(dirname11(file), ".gitkeep");
22524
- if (existsSync11(keep))
23058
+ const keep = join13(dirname11(file), ".gitkeep");
23059
+ if (existsSync12(keep))
22525
23060
  unlinkSync(keep);
22526
23061
  }
22527
23062
  }
@@ -23008,11 +23543,11 @@ ${hint(flagHint)}
23008
23543
  `);
23009
23544
  throw new NeedsInputError(flagHint);
23010
23545
  }
23011
- return join13(project.configDir, SKILLS_DIR_NAME);
23546
+ return join14(project.configDir, SKILLS_DIR_NAME);
23012
23547
  };
23013
23548
  var writeStaleSkillsHint = ({ command }) => {
23014
23549
  const stale = staleSkillsHint({
23015
- dir: join13(projectOf({ command }).configDir, SKILLS_DIR_NAME)
23550
+ dir: join14(projectOf({ command }).configDir, SKILLS_DIR_NAME)
23016
23551
  });
23017
23552
  if (stale !== null)
23018
23553
  process.stdout.write(`${stale}
@@ -23196,6 +23731,8 @@ Nothing applied. Re-run with --yes to apply these changes.
23196
23731
  skills.command("install").description("write the skills next to your config; --link runs npx skills add").option("--dir <dir>", "write here instead of <config folder>/skills").option("--link", "run `npx skills add <dir> --all` afterwards").action(async (options, command) => {
23197
23732
  const dir = skillsDirOf({ command, dir: options.dir });
23198
23733
  installSkills({ dir, write: (text) => process.stdout.write(text) });
23734
+ if (options.dir === undefined)
23735
+ ignoreBundledSkills(projectOf({ command }));
23199
23736
  if (options.link === true)
23200
23737
  await linkSkills({
23201
23738
  dir,
@@ -23205,6 +23742,8 @@ Nothing applied. Re-run with --yes to apply these changes.
23205
23742
  skills.command("update").description("rewrite installed skills older than this CLI's").option("--dir <dir>", "the install to update; <config folder>/skills by default").action((options, command) => {
23206
23743
  const dir = skillsDirOf({ command, dir: options.dir });
23207
23744
  updateSkills({ dir, write: (text) => process.stdout.write(text) });
23745
+ if (options.dir === undefined)
23746
+ ignoreBundledSkills(projectOf({ command }));
23208
23747
  });
23209
23748
  program.command("pull").description("write your remote catalog back into autumn.config.ts").option("--include-mappings", "keep processor mappings in pulled fixtures").option("--overwrite", "rewrite the config from your org's catalog (e.g. after switching orgs)").option("-y, --yes", "overwrite it").action(async (options, command) => {
23210
23749
  const target = prepareTarget({ command });