atmn 2.0.132 → 2.0.133
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 +70 -11
- package/dist/index.js +61 -6
- package/package.json +2 -2
- package/src/generated/client.ts +155 -0
- package/src/generated/licenses.ts +1 -0
- package/src/generated/lintRules.ts +29 -6
- package/src/generated/lintRuntime.ts +80 -1
- package/src/generated/plans.ts +5 -0
- package/src/generated/skills.ts +2 -2
- package/src/generated/variants.ts +3 -0
- package/src/render/renderPreview.ts +5 -2
package/dist/bin.js
CHANGED
|
@@ -4782,6 +4782,25 @@ var LINT_RULES = {
|
|
|
4782
4782
|
parentIdField: "planId",
|
|
4783
4783
|
parentLabel: "plan",
|
|
4784
4784
|
because: "An archived feature should not gain new customers through a live plan."
|
|
4785
|
+
},
|
|
4786
|
+
{
|
|
4787
|
+
kind: "targetForbids",
|
|
4788
|
+
field: "featureId",
|
|
4789
|
+
in: "features",
|
|
4790
|
+
matching: "featureId",
|
|
4791
|
+
target: "consumable",
|
|
4792
|
+
equals: false,
|
|
4793
|
+
whenEquals: {
|
|
4794
|
+
"price.tierBehavior": "volume",
|
|
4795
|
+
"price.billingMethod": "usage_based"
|
|
4796
|
+
},
|
|
4797
|
+
whenStated: [],
|
|
4798
|
+
whenAnyStated: [
|
|
4799
|
+
"price.tiers.0.flatAmount",
|
|
4800
|
+
"price.tiers.0.additionalCurrencies.*.flatAmount"
|
|
4801
|
+
],
|
|
4802
|
+
whenUnstated: [],
|
|
4803
|
+
because: "A volume-tiered allocated item can't have a first-tier flatAmount: Stripe would charge it even with no seats over included. Move the fee to a later tier."
|
|
4785
4804
|
}
|
|
4786
4805
|
]
|
|
4787
4806
|
},
|
|
@@ -4959,12 +4978,14 @@ var LINT_RULES = {
|
|
|
4959
4978
|
},
|
|
4960
4979
|
rules: [
|
|
4961
4980
|
{
|
|
4962
|
-
kind: "
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4981
|
+
kind: "refusedWhen",
|
|
4982
|
+
whenEquals: {
|
|
4983
|
+
tierBehavior: "volume",
|
|
4984
|
+
billingMethod: "usage_based"
|
|
4985
|
+
},
|
|
4986
|
+
whenStated: [],
|
|
4987
|
+
whenUnstated: ["tiers.1"],
|
|
4988
|
+
because: "Volume-based pricing on a usage-based item needs at least two tiers. Add a tier, or use graduated pricing for a single rate."
|
|
4968
4989
|
}
|
|
4969
4990
|
]
|
|
4970
4991
|
},
|
|
@@ -6654,6 +6675,10 @@ var valueAtPath = ({
|
|
|
6654
6675
|
}) => {
|
|
6655
6676
|
let current = entry;
|
|
6656
6677
|
for (const segment of path.split(".")) {
|
|
6678
|
+
if (Array.isArray(current)) {
|
|
6679
|
+
current = current[Number(segment)];
|
|
6680
|
+
continue;
|
|
6681
|
+
}
|
|
6657
6682
|
if (!isEntry(current))
|
|
6658
6683
|
return;
|
|
6659
6684
|
current = current[segment];
|
|
@@ -6795,6 +6820,21 @@ var COMPARE = {
|
|
|
6795
6820
|
">": { holds: (a, b) => a > b, words: "greater than" },
|
|
6796
6821
|
">=": { holds: (a, b) => a >= b, words: "at least" }
|
|
6797
6822
|
};
|
|
6823
|
+
var entryMatchesWhen = ({
|
|
6824
|
+
entry,
|
|
6825
|
+
rule
|
|
6826
|
+
}) => {
|
|
6827
|
+
const isStatedValue = (value) => value !== undefined && value !== null && value !== 0 && value !== false;
|
|
6828
|
+
const isStated = (path) => {
|
|
6829
|
+
const [head, tail] = path.split(".*.");
|
|
6830
|
+
if (tail === undefined)
|
|
6831
|
+
return isStatedValue(valueAtPath({ entry, path }));
|
|
6832
|
+
const list = valueAtPath({ entry, path: head });
|
|
6833
|
+
return Array.isArray(list) && list.some((element) => isEntry(element) && isStatedValue(element[tail]));
|
|
6834
|
+
};
|
|
6835
|
+
const anyStated = "whenAnyStated" in rule && rule.whenAnyStated ? rule.whenAnyStated.some(isStated) : true;
|
|
6836
|
+
return Object.entries(rule.whenEquals).every(([path, value]) => valueAtPath({ entry, path }) === value) && rule.whenStated.every(isStated) && anyStated && !rule.whenUnstated.some(isStated);
|
|
6837
|
+
};
|
|
6798
6838
|
var entryRuleFailures = ({
|
|
6799
6839
|
entry,
|
|
6800
6840
|
rule,
|
|
@@ -6914,6 +6954,21 @@ var entryRuleFailures = ({
|
|
|
6914
6954
|
`${rule.label} ${show(entry[rule.field])} is ${rule.target}. Unarchive it, or archive ${rule.parentLabel} ${show(parent?.[rule.parentIdField])}. ${rule.because}`
|
|
6915
6955
|
];
|
|
6916
6956
|
}
|
|
6957
|
+
case "refusedWhen":
|
|
6958
|
+
return entryMatchesWhen({ entry, rule }) ? [rule.because] : [];
|
|
6959
|
+
case "targetForbids": {
|
|
6960
|
+
if (!entryMatchesWhen({ entry, rule }))
|
|
6961
|
+
return [];
|
|
6962
|
+
const target = document[rule.in];
|
|
6963
|
+
if (!Array.isArray(target))
|
|
6964
|
+
return [];
|
|
6965
|
+
const row = target.find((candidate) => isEntry(candidate) && candidate[rule.matching] === entry[rule.field]);
|
|
6966
|
+
if (!row || row[rule.target] !== rule.equals)
|
|
6967
|
+
return [];
|
|
6968
|
+
return [
|
|
6969
|
+
`${rule.in} ${show(entry[rule.field])} has ${rule.target} ${show(rule.equals)}, so this is refused. ${rule.because}`
|
|
6970
|
+
];
|
|
6971
|
+
}
|
|
6917
6972
|
case "unique":
|
|
6918
6973
|
case "linkedOnce":
|
|
6919
6974
|
case "exactlyOneActive":
|
|
@@ -7641,7 +7696,7 @@ var createClient = (options) => ({
|
|
|
7641
7696
|
});
|
|
7642
7697
|
|
|
7643
7698
|
// src/version.ts
|
|
7644
|
-
var version = "2.0.
|
|
7699
|
+
var version = "2.0.133";
|
|
7645
7700
|
|
|
7646
7701
|
// src/http/autumnFetch.ts
|
|
7647
7702
|
var runtime = typeof Bun !== "undefined" ? `bun ${Bun.version}` : `node ${process.versions.node}`;
|
|
@@ -10305,7 +10360,7 @@ Billing controls follow the same rule: \`·\` lines under the item they guard, i
|
|
|
10305
10360
|
- A prepaid quantity **includes** the included amount, and so does each tier's \`to\` — the first tier's \`to\` must exceed \`included\`.
|
|
10306
10361
|
- \`billingUnits\` rounds usage **up** when billing.
|
|
10307
10362
|
- Set explicitly, never lean on defaults: \`billingMethod\` and \`interval\` on every item price, \`tierBehavior\` on tiered prices, and every trial field (\`durationLength\`, \`durationType\`, \`cardRequired\`, \`onEnd\`). Explicit defaults cause no spurious diffs.
|
|
10308
|
-
- Volume tiers
|
|
10363
|
+
- Volume tiers on usage-based items bill only usage past the included amount, at the rate of the tier total usage reaches, plus its flat fee. On prepaid items they charge every unit (included ones too) at the reached tier's rate and flat amount. Graduated (the default) sums across brackets. Volume can't be combined with threshold billing, and allocated volume can't have a tier-1 flat amount.
|
|
10309
10364
|
- Rollover needs a resetting allowance; \`max\` and \`maxPercentage\` are mutually exclusive; \`expiryDurationType\` is required.
|
|
10310
10365
|
- \`billingControls\` is a plain object on the plan with camelCase fields like the rest of the config (\`featureId\`, \`overageLimit\`); each control list replaces wholesale on update.
|
|
10311
10366
|
- Plan \`config\` (\`ignorePastDue\`, \`anchorToMonthStart\`) is unmanaged when omitted. Once stated it is the whole set: a flag left out is off, so \`config: {}\` turns every flag off.
|
|
@@ -10879,7 +10934,9 @@ Autumn:
|
|
|
10879
10934
|
2. Finds the tier the total falls into
|
|
10880
10935
|
3. Charges the flat amount for that tier
|
|
10881
10936
|
|
|
10882
|
-
Volume tiers
|
|
10937
|
+
Volume tiers work on prepaid and usage-based (pay-per-use or allocated) items. On usage-based items, volume bills only usage past the included amount, at the rate of the tier total usage reaches, plus its flat fee; at or below the included amount nothing is charged. On prepaid items, every unit (the included ones too) is charged at the rate of the tier the total lands in, plus its flat amount. Allocated items can't have a flat amount on the first tier. A plan with volume-priced allocated seats can't yet be attached to more than one entity on the same customer, because their seats would share one Stripe price.
|
|
10938
|
+
|
|
10939
|
+
Volume tiers can't be combined with threshold billing.
|
|
10883
10940
|
|
|
10884
10941
|
| Total volume | Matching tier | Charge |
|
|
10885
10942
|
|-------------|---------------|--------|
|
|
@@ -23426,8 +23483,10 @@ var formatInterval = (interval, count) => {
|
|
|
23426
23483
|
var formatPrice = (price) => {
|
|
23427
23484
|
if (price === null || price === undefined)
|
|
23428
23485
|
return "Free";
|
|
23429
|
-
if (price.tiers !== undefined && price.tiers.length > 0)
|
|
23430
|
-
|
|
23486
|
+
if (price.tiers !== undefined && price.tiers.length > 0) {
|
|
23487
|
+
const behavior = price.tierBehavior ? `${price.tierBehavior} ` : "";
|
|
23488
|
+
return `${price.tiers.length} ${behavior}tiers ${formatInterval(price.interval, price.intervalCount)}`;
|
|
23489
|
+
}
|
|
23431
23490
|
return `${formatMoney(price.amount ?? 0)} ${formatInterval(price.interval, price.intervalCount)}`;
|
|
23432
23491
|
};
|
|
23433
23492
|
var formatItem = (item) => {
|
package/dist/index.js
CHANGED
|
@@ -451,6 +451,25 @@ var LINT_RULES = {
|
|
|
451
451
|
parentIdField: "planId",
|
|
452
452
|
parentLabel: "plan",
|
|
453
453
|
because: "An archived feature should not gain new customers through a live plan."
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
kind: "targetForbids",
|
|
457
|
+
field: "featureId",
|
|
458
|
+
in: "features",
|
|
459
|
+
matching: "featureId",
|
|
460
|
+
target: "consumable",
|
|
461
|
+
equals: false,
|
|
462
|
+
whenEquals: {
|
|
463
|
+
"price.tierBehavior": "volume",
|
|
464
|
+
"price.billingMethod": "usage_based"
|
|
465
|
+
},
|
|
466
|
+
whenStated: [],
|
|
467
|
+
whenAnyStated: [
|
|
468
|
+
"price.tiers.0.flatAmount",
|
|
469
|
+
"price.tiers.0.additionalCurrencies.*.flatAmount"
|
|
470
|
+
],
|
|
471
|
+
whenUnstated: [],
|
|
472
|
+
because: "A volume-tiered allocated item can't have a first-tier flatAmount: Stripe would charge it even with no seats over included. Move the fee to a later tier."
|
|
454
473
|
}
|
|
455
474
|
]
|
|
456
475
|
},
|
|
@@ -628,12 +647,14 @@ var LINT_RULES = {
|
|
|
628
647
|
},
|
|
629
648
|
rules: [
|
|
630
649
|
{
|
|
631
|
-
kind: "
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
650
|
+
kind: "refusedWhen",
|
|
651
|
+
whenEquals: {
|
|
652
|
+
tierBehavior: "volume",
|
|
653
|
+
billingMethod: "usage_based"
|
|
654
|
+
},
|
|
655
|
+
whenStated: [],
|
|
656
|
+
whenUnstated: ["tiers.1"],
|
|
657
|
+
because: "Volume-based pricing on a usage-based item needs at least two tiers. Add a tier, or use graduated pricing for a single rate."
|
|
637
658
|
}
|
|
638
659
|
]
|
|
639
660
|
},
|
|
@@ -2323,6 +2344,10 @@ var valueAtPath = ({
|
|
|
2323
2344
|
}) => {
|
|
2324
2345
|
let current = entry;
|
|
2325
2346
|
for (const segment of path.split(".")) {
|
|
2347
|
+
if (Array.isArray(current)) {
|
|
2348
|
+
current = current[Number(segment)];
|
|
2349
|
+
continue;
|
|
2350
|
+
}
|
|
2326
2351
|
if (!isEntry(current))
|
|
2327
2352
|
return;
|
|
2328
2353
|
current = current[segment];
|
|
@@ -2464,6 +2489,21 @@ var COMPARE = {
|
|
|
2464
2489
|
">": { holds: (a, b) => a > b, words: "greater than" },
|
|
2465
2490
|
">=": { holds: (a, b) => a >= b, words: "at least" }
|
|
2466
2491
|
};
|
|
2492
|
+
var entryMatchesWhen = ({
|
|
2493
|
+
entry,
|
|
2494
|
+
rule
|
|
2495
|
+
}) => {
|
|
2496
|
+
const isStatedValue = (value) => value !== undefined && value !== null && value !== 0 && value !== false;
|
|
2497
|
+
const isStated = (path) => {
|
|
2498
|
+
const [head, tail] = path.split(".*.");
|
|
2499
|
+
if (tail === undefined)
|
|
2500
|
+
return isStatedValue(valueAtPath({ entry, path }));
|
|
2501
|
+
const list = valueAtPath({ entry, path: head });
|
|
2502
|
+
return Array.isArray(list) && list.some((element) => isEntry(element) && isStatedValue(element[tail]));
|
|
2503
|
+
};
|
|
2504
|
+
const anyStated = "whenAnyStated" in rule && rule.whenAnyStated ? rule.whenAnyStated.some(isStated) : true;
|
|
2505
|
+
return Object.entries(rule.whenEquals).every(([path, value]) => valueAtPath({ entry, path }) === value) && rule.whenStated.every(isStated) && anyStated && !rule.whenUnstated.some(isStated);
|
|
2506
|
+
};
|
|
2467
2507
|
var entryRuleFailures = ({
|
|
2468
2508
|
entry,
|
|
2469
2509
|
rule,
|
|
@@ -2583,6 +2623,21 @@ var entryRuleFailures = ({
|
|
|
2583
2623
|
`${rule.label} ${show(entry[rule.field])} is ${rule.target}. Unarchive it, or archive ${rule.parentLabel} ${show(parent?.[rule.parentIdField])}. ${rule.because}`
|
|
2584
2624
|
];
|
|
2585
2625
|
}
|
|
2626
|
+
case "refusedWhen":
|
|
2627
|
+
return entryMatchesWhen({ entry, rule }) ? [rule.because] : [];
|
|
2628
|
+
case "targetForbids": {
|
|
2629
|
+
if (!entryMatchesWhen({ entry, rule }))
|
|
2630
|
+
return [];
|
|
2631
|
+
const target = document[rule.in];
|
|
2632
|
+
if (!Array.isArray(target))
|
|
2633
|
+
return [];
|
|
2634
|
+
const row = target.find((candidate) => isEntry(candidate) && candidate[rule.matching] === entry[rule.field]);
|
|
2635
|
+
if (!row || row[rule.target] !== rule.equals)
|
|
2636
|
+
return [];
|
|
2637
|
+
return [
|
|
2638
|
+
`${rule.in} ${show(entry[rule.field])} has ${rule.target} ${show(rule.equals)}, so this is refused. ${rule.because}`
|
|
2639
|
+
];
|
|
2640
|
+
}
|
|
2586
2641
|
case "unique":
|
|
2587
2642
|
case "linkedOnce":
|
|
2588
2643
|
case "exactlyOneActive":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atmn",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.133",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The CLI for Autumn — define your pricing in code, sync it to Autumn, and keep everything in version control.",
|
|
6
6
|
"bin": {
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"open": "10.2.0",
|
|
50
50
|
"yaml": "2.9.0"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "18bcf1751d169fd7dae4c28638db67093a6c0687"
|
|
53
53
|
}
|