@usebillow/sdk 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{billing-B-VyZCXV.d.cts → billing-8ug1Gw05.d.cts} +14 -10
  3. package/dist/{billing-CgjcWmvb.d.ts → billing-Bs10EcjR.d.ts} +14 -10
  4. package/dist/{billing-status-BZQN_gm7.d.cts → billing-status-DZkB0VPK.d.cts} +5 -5
  5. package/dist/{billing-status-BZQN_gm7.d.ts → billing-status-DZkB0VPK.d.ts} +5 -5
  6. package/dist/{chunk-CCG4F5FK.js → chunk-47J7FYM5.js} +2 -2
  7. package/dist/chunk-47J7FYM5.js.map +1 -0
  8. package/dist/{chunk-J6JIZGWB.js → chunk-XKN7ZRAR.js} +81 -22
  9. package/dist/chunk-XKN7ZRAR.js.map +1 -0
  10. package/dist/config.cjs +3 -3
  11. package/dist/config.cjs.map +1 -1
  12. package/dist/config.d.cts +10 -10
  13. package/dist/config.d.ts +10 -10
  14. package/dist/config.js +3 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/{hosted-domains-BbuyxnuH.d.cts → hosted-domains-Ci6jeNAe.d.cts} +124 -17
  17. package/dist/{hosted-domains-BP91tg9P.d.ts → hosted-domains-DSs5Iw9U.d.ts} +124 -17
  18. package/dist/index.cjs +79 -20
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +89 -55
  21. package/dist/index.d.ts +89 -55
  22. package/dist/index.js +2 -2
  23. package/dist/ingestion.cjs.map +1 -1
  24. package/dist/ingestion.d.cts +19 -19
  25. package/dist/ingestion.d.ts +19 -19
  26. package/dist/ingestion.js.map +1 -1
  27. package/dist/react.cjs +28 -5
  28. package/dist/react.cjs.map +1 -1
  29. package/dist/react.d.cts +7 -7
  30. package/dist/react.d.ts +7 -7
  31. package/dist/react.js +2 -2
  32. package/dist/react.js.map +1 -1
  33. package/dist/server.cjs.map +1 -1
  34. package/dist/server.d.cts +12 -12
  35. package/dist/server.d.ts +12 -12
  36. package/dist/server.js.map +1 -1
  37. package/dist/status.cjs.map +1 -1
  38. package/dist/status.d.cts +1 -1
  39. package/dist/status.d.ts +1 -1
  40. package/dist/status.js +1 -1
  41. package/dist/webhooks.cjs.map +1 -1
  42. package/dist/webhooks.d.cts +27 -14
  43. package/dist/webhooks.d.ts +27 -14
  44. package/dist/webhooks.js.map +1 -1
  45. package/package.json +1 -1
  46. package/dist/chunk-CCG4F5FK.js.map +0 -1
  47. package/dist/chunk-J6JIZGWB.js.map +0 -1
  48. package/dist/{index.d-CKQAhQfJ.d.ts → index.d-DSEYhV2c.d.cts} +2 -2
  49. package/dist/{index.d-CKQAhQfJ.d.cts → index.d-DSEYhV2c.d.ts} +2 -2
package/dist/config.d.cts CHANGED
@@ -1,18 +1,18 @@
1
1
  import { Billow } from './index.cjs';
2
- import { F as FeatureKind, M as MeterConfig, C as CreateProductInput, a as CreateCouponInput } from './billing-B-VyZCXV.cjs';
3
- import './index.d-CKQAhQfJ.cjs';
2
+ import { F as FeatureKind, M as MeterConfig, C as CreateProductInput, a as CreateCouponInput } from './billing-8ug1Gw05.cjs';
3
+ import './index.d-DSEYhV2c.cjs';
4
4
  import 'zod';
5
- import './billing-status-BZQN_gm7.cjs';
5
+ import './billing-status-DZkB0VPK.cjs';
6
6
  import './status.cjs';
7
- import './hosted-domains-BbuyxnuH.cjs';
7
+ import './hosted-domains-Ci6jeNAe.cjs';
8
8
 
9
9
  /**
10
10
  * Code-as-config sync (ARCHITECTURE §12 "code-as-config sync"). Keep your catalog
11
- * — features, products, coupons — in a typed config file and `syncConfig` it into
11
+ * - features, products, coupons - in a typed config file and `syncConfig` it into
12
12
  * a billow server. Sync is **declarative and additive**: it creates whatever is
13
13
  * missing and *reports drift* on what already exists, but never deletes and never
14
14
  * rewrites an existing product (catalog edits are made deliberately through the
15
- * dashboard/API — ADR-0015 — not silently on every sync).
15
+ * dashboard/API - ADR-0015 - not silently on every sync).
16
16
  *
17
17
  * import { defineConfig, syncConfig } from "@usebillow/sdk/config";
18
18
  * const config = defineConfig({ features: [...], products: [...], coupons: [...] });
@@ -25,15 +25,15 @@ interface ConfigFeature {
25
25
  kind: FeatureKind;
26
26
  /**
27
27
  * For a `metered` feature: how its usage aggregates (ADR-0011). Omit to get a `sum`
28
- * meter. Set it here so sync creates the meter you want instead of the default — after
28
+ * meter. Set it here so sync creates the meter you want instead of the default - after
29
29
  * usage is recorded the aggregation can no longer change.
30
30
  */
31
31
  meter?: MeterConfig;
32
32
  }
33
- /** A product entry — the same shape as `billow.products.create`. */
33
+ /** A product entry - the same shape as `billow.products.create`. */
34
34
  type ConfigProduct = CreateProductInput;
35
35
  /**
36
- * A coupon entry. Unlike `billow.coupons.create`, the `code` is **required** — it
36
+ * A coupon entry. Unlike `billow.coupons.create`, the `code` is **required** - it
37
37
  * is the stable identity sync diffs against (a code-less coupon can't be matched).
38
38
  */
39
39
  type ConfigCoupon = Omit<CreateCouponInput, "code"> & {
@@ -44,7 +44,7 @@ interface BillowConfig {
44
44
  products?: ConfigProduct[];
45
45
  coupons?: ConfigCoupon[];
46
46
  }
47
- /** Identity helper — returns its argument, typed, for editor completion + checks. */
47
+ /** Identity helper - returns its argument, typed, for editor completion + checks. */
48
48
  declare function defineConfig(config: BillowConfig): BillowConfig;
49
49
  type SyncAction = "create" | "unchanged" | "drift";
50
50
  interface SyncItem {
package/dist/config.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import { Billow } from './index.js';
2
- import { F as FeatureKind, M as MeterConfig, C as CreateProductInput, a as CreateCouponInput } from './billing-CgjcWmvb.js';
3
- import './index.d-CKQAhQfJ.js';
2
+ import { F as FeatureKind, M as MeterConfig, C as CreateProductInput, a as CreateCouponInput } from './billing-Bs10EcjR.js';
3
+ import './index.d-DSEYhV2c.js';
4
4
  import 'zod';
5
- import './billing-status-BZQN_gm7.js';
5
+ import './billing-status-DZkB0VPK.js';
6
6
  import './status.js';
7
- import './hosted-domains-BP91tg9P.js';
7
+ import './hosted-domains-DSs5Iw9U.js';
8
8
 
9
9
  /**
10
10
  * Code-as-config sync (ARCHITECTURE §12 "code-as-config sync"). Keep your catalog
11
- * — features, products, coupons — in a typed config file and `syncConfig` it into
11
+ * - features, products, coupons - in a typed config file and `syncConfig` it into
12
12
  * a billow server. Sync is **declarative and additive**: it creates whatever is
13
13
  * missing and *reports drift* on what already exists, but never deletes and never
14
14
  * rewrites an existing product (catalog edits are made deliberately through the
15
- * dashboard/API — ADR-0015 — not silently on every sync).
15
+ * dashboard/API - ADR-0015 - not silently on every sync).
16
16
  *
17
17
  * import { defineConfig, syncConfig } from "@usebillow/sdk/config";
18
18
  * const config = defineConfig({ features: [...], products: [...], coupons: [...] });
@@ -25,15 +25,15 @@ interface ConfigFeature {
25
25
  kind: FeatureKind;
26
26
  /**
27
27
  * For a `metered` feature: how its usage aggregates (ADR-0011). Omit to get a `sum`
28
- * meter. Set it here so sync creates the meter you want instead of the default — after
28
+ * meter. Set it here so sync creates the meter you want instead of the default - after
29
29
  * usage is recorded the aggregation can no longer change.
30
30
  */
31
31
  meter?: MeterConfig;
32
32
  }
33
- /** A product entry — the same shape as `billow.products.create`. */
33
+ /** A product entry - the same shape as `billow.products.create`. */
34
34
  type ConfigProduct = CreateProductInput;
35
35
  /**
36
- * A coupon entry. Unlike `billow.coupons.create`, the `code` is **required** — it
36
+ * A coupon entry. Unlike `billow.coupons.create`, the `code` is **required** - it
37
37
  * is the stable identity sync diffs against (a code-less coupon can't be matched).
38
38
  */
39
39
  type ConfigCoupon = Omit<CreateCouponInput, "code"> & {
@@ -44,7 +44,7 @@ interface BillowConfig {
44
44
  products?: ConfigProduct[];
45
45
  coupons?: ConfigCoupon[];
46
46
  }
47
- /** Identity helper — returns its argument, typed, for editor completion + checks. */
47
+ /** Identity helper - returns its argument, typed, for editor completion + checks. */
48
48
  declare function defineConfig(config: BillowConfig): BillowConfig;
49
49
  type SyncAction = "create" | "unchanged" | "drift";
50
50
  interface SyncItem {
package/dist/config.js CHANGED
@@ -147,7 +147,7 @@ function normMeter(kind, meter) {
147
147
  return JSON.stringify({
148
148
  aggregation: meter?.aggregation ?? "sum",
149
149
  eventProperty: meter?.eventProperty ?? null,
150
- // The filter list and each filter's values are unordered sets — sort both so a mere
150
+ // The filter list and each filter's values are unordered sets - sort both so a mere
151
151
  // reordering isn't reported as drift (matches the server's no-op semantics).
152
152
  filters: (meter?.filters ?? []).map((f) => ({ key: f.key, values: [...f.values].sort() })).sort((a, b) => {
153
153
  if (a.key !== b.key) return a.key < b.key ? -1 : 1;
@@ -158,7 +158,7 @@ function normMeter(kind, meter) {
158
158
  roundingMode: meter?.roundingMode ?? "none",
159
159
  roundingScale: meter?.roundingScale ?? null,
160
160
  // Reset anchoring (plan 07). Server omits these for cycle meters, so default to cycle/null
161
- // on both sides — an omitted anchor and an explicit `cycle` compare equal (no false drift).
161
+ // on both sides - an omitted anchor and an explicit `cycle` compare equal (no false drift).
162
162
  resetAnchor: meter?.resetAnchor ?? "cycle",
163
163
  resetTz: meter?.resetTz ?? null
164
164
  });
@@ -177,7 +177,7 @@ function normPrice(p) {
177
177
  usageTiers: canonTiers(p.usageTiers),
178
178
  packageSize: p.packageSize ?? null,
179
179
  rateBps: p.rateBps ?? null,
180
- // Tax overrides (Phase E) — null = inherit org default; included so a change
180
+ // Tax overrides (Phase E) - null = inherit org default; included so a change
181
181
  // is detected as drift.
182
182
  taxRateBps: p.taxRateBps ?? null,
183
183
  taxBehavior: p.taxBehavior ?? null
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts"],"names":[],"mappings":"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","file":"config.js","sourcesContent":["/**\n * Code-as-config sync (ARCHITECTURE §12 \"code-as-config sync\"). Keep your catalog\n * — features, products, coupons — in a typed config file and `syncConfig` it into\n * a billow server. Sync is **declarative and additive**: it creates whatever is\n * missing and *reports drift* on what already exists, but never deletes and never\n * rewrites an existing product (catalog edits are made deliberately through the\n * dashboard/API — ADR-0015 — not silently on every sync).\n *\n * import { defineConfig, syncConfig } from \"@usebillow/sdk/config\";\n * const config = defineConfig({ features: [...], products: [...], coupons: [...] });\n * await syncConfig(billow, config, { onProgress: console.log });\n */\nimport type {\n Billow,\n Coupon,\n CreateCouponInput,\n CreateProductInput,\n Feature,\n FeatureKind,\n MeterConfig,\n Product,\n} from \"./index\";\n\nexport interface ConfigFeature {\n slug: string;\n name: string;\n kind: FeatureKind;\n /**\n * For a `metered` feature: how its usage aggregates (ADR-0011). Omit to get a `sum`\n * meter. Set it here so sync creates the meter you want instead of the default — after\n * usage is recorded the aggregation can no longer change.\n */\n meter?: MeterConfig;\n}\n\n/** A product entry — the same shape as `billow.products.create`. */\nexport type ConfigProduct = CreateProductInput;\n\n/**\n * A coupon entry. Unlike `billow.coupons.create`, the `code` is **required** — it\n * is the stable identity sync diffs against (a code-less coupon can't be matched).\n */\nexport type ConfigCoupon = Omit<CreateCouponInput, \"code\"> & { code: string };\n\nexport interface BillowConfig {\n features?: ConfigFeature[];\n products?: ConfigProduct[];\n coupons?: ConfigCoupon[];\n}\n\n/** Identity helper — returns its argument, typed, for editor completion + checks. */\nexport function defineConfig(config: BillowConfig): BillowConfig {\n return config;\n}\n\nexport type SyncAction = \"create\" | \"unchanged\" | \"drift\";\n\nexport interface SyncItem {\n kind: \"feature\" | \"product\" | \"coupon\";\n /** Slug (features/products) or code (coupons). */\n id: string;\n action: SyncAction;\n /** For `drift`: which aspects differ from the server (human-readable). */\n changes?: string[];\n}\n\nexport interface SyncPlan {\n features: SyncItem[];\n products: SyncItem[];\n coupons: SyncItem[];\n}\n\nexport interface SyncResult {\n plan: SyncPlan;\n created: { features: string[]; products: string[]; coupons: string[] };\n /**\n * Items that still differ from the config after the sync - reported, not applied. With\n * `applyMetadata`, product metadata that was written is removed from here (see `applied`).\n */\n drift: SyncItem[];\n /** Product slugs whose metadata was written this run (only when `applyMetadata` is set). */\n applied: { productMetadata: string[] };\n}\n\n/**\n * Diff a config against the server without writing anything. Each entry is\n * `create` (missing), `unchanged` (matches), or `drift` (exists but differs).\n */\nexport async function planConfig(billow: Billow, config: BillowConfig): Promise<SyncPlan> {\n // Collect the whole catalog in as few round-trips as the server allows (page size caps at 100).\n const [features, products, coupons] = await Promise.all([\n billow.features.list({ limit: 100 }).listAll(),\n billow.products.list(),\n billow.coupons.list({ limit: 100 }).listAll(),\n ]);\n\n const featureBySlug = new Map(features.map((f) => [f.slug, f]));\n const productBySlug = new Map(products.map((p) => [p.slug, p]));\n const couponByCode = new Map(coupons.filter((c) => c.code).map((c) => [c.code as string, c]));\n\n return {\n features: (config.features ?? []).map((f) => planFeature(f, featureBySlug.get(f.slug))),\n products: (config.products ?? []).map((p) => planProduct(p, productBySlug.get(p.slug))),\n coupons: (config.coupons ?? []).map((c) => planCoupon(c, couponByCode.get(c.code))),\n };\n}\n\n/**\n * Apply a config: create everything missing (features → products → coupons, the\n * dependency order, since products reference feature slugs), and return what was\n * created plus any drift it found.\n *\n * Structural drift (prices, entitlements, name, ...) is never auto-applied - those edits are\n * version-bearing (ADR-0015: collected at renewal) and belong to a deliberate `products.update`.\n * With `applyMetadata: true`, product **metadata** drift IS written, because metadata lives on\n * the product row (not the version), so updating it is version-independent and cannot rewrite\n * billed history. This closes the write-side gap where a config's product metadata could only\n * be reported, never pushed. The write is a full-map replace (config is authoritative): a key\n * present on the server but absent from the config is cleared.\n */\nexport async function syncConfig(\n billow: Billow,\n config: BillowConfig,\n opts: { onProgress?: (message: string) => void; applyMetadata?: boolean } = {},\n): Promise<SyncResult> {\n const plan = await planConfig(billow, config);\n const progress = opts.onProgress ?? (() => {});\n\n // Create in dependency order — features → products → coupons — since products\n // reference feature slugs. Each kind is created before the next begins.\n const created = {\n features: await createMissing(\n plan.features,\n config.features ?? [],\n (f) => f.slug,\n \"feature\",\n (f) => billow.features.create(f),\n progress,\n ),\n products: await createMissing(\n plan.products,\n config.products ?? [],\n (p) => p.slug,\n \"product\",\n (p) => billow.products.create(p),\n progress,\n ),\n coupons: await createMissing(\n plan.coupons,\n config.coupons ?? [],\n (c) => c.code,\n \"coupon\",\n (c) => billow.coupons.create(c),\n progress,\n ),\n };\n\n // Version-independent metadata is the one field safe to auto-write (a product-row column,\n // not a version). Apply it before computing drift so a resolved item drops out of the report.\n const applied = { productMetadata: [] as string[] };\n if (opts.applyMetadata) {\n const isMetadataChange = (c: string) => c.startsWith(\"metadata\");\n const productDefBySlug = new Map((config.products ?? []).map((p) => [p.slug, p]));\n for (const item of plan.products) {\n if (item.action !== \"drift\" || !item.changes?.some(isMetadataChange)) continue;\n const def = productDefBySlug.get(item.id);\n if (!def) continue;\n await billow.products.update(item.id, { metadata: def.metadata ?? {} });\n applied.productMetadata.push(item.id);\n progress(`~ product ${item.id} (metadata applied)`);\n // Reflect the write in the plan: drop metadata from this item, and mark it unchanged if\n // it had no other drift, so the returned drift no longer reports what we just fixed.\n item.changes = item.changes.filter((c) => !isMetadataChange(c));\n if (item.changes.length === 0) item.action = \"unchanged\";\n }\n }\n\n const drift = [...plan.features, ...plan.products, ...plan.coupons].filter(\n (i) => i.action === \"drift\",\n );\n return { plan, created, drift, applied };\n}\n\n/**\n * Create the `action: \"create\"` items of one kind, matching each plan item back to\n * its config definition by id (a Map, not a scan), and return the ids created.\n */\nasync function createMissing<T>(\n items: SyncItem[],\n defs: T[],\n idOf: (def: T) => string,\n label: string,\n create: (def: T) => Promise<unknown>,\n progress: (message: string) => void,\n): Promise<string[]> {\n const byId = new Map(defs.map((d) => [idOf(d), d]));\n const created: string[] = [];\n for (const item of items) {\n if (item.action !== \"create\") continue;\n const def = byId.get(item.id);\n if (!def) continue;\n await create(def);\n created.push(item.id);\n progress(`+ ${label} ${item.id}`);\n }\n return created;\n}\n\n/** A one-line-per-item summary of a plan, for CLI output. */\nexport function formatPlan(plan: SyncPlan): string {\n const mark = { create: \"+\", unchanged: \"=\", drift: \"~\" } as const;\n const lines: string[] = [];\n for (const item of [...plan.features, ...plan.products, ...plan.coupons]) {\n const detail = item.changes?.length ? ` (${item.changes.join(\", \")})` : \"\";\n lines.push(`${mark[item.action]} ${item.kind} ${item.id}${detail}`);\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Per-entity diffing\n// ---------------------------------------------------------------------------\n\nfunction planFeature(def: ConfigFeature, existing: Feature | undefined): SyncItem {\n if (!existing) return { kind: \"feature\", id: def.slug, action: \"create\" };\n const changes = diffFields([\n [\"name\", def.name, existing.name],\n [\"kind\", def.kind, existing.kind],\n // A metered feature with no explicit meter gets a `sum` meter server-side; normalize\n // both sides so an aggregation change is reported as drift, not \"unchanged\".\n [\n \"meter\",\n normMeter(def.kind, def.meter),\n normMeter(existing.kind, existing.meter ?? undefined),\n ],\n ]);\n return { kind: \"feature\", id: def.slug, action: changes.length ? \"drift\" : \"unchanged\", changes };\n}\n\nfunction planProduct(def: ConfigProduct, existing: Product | undefined): SyncItem {\n if (!existing) return { kind: \"product\", id: def.slug, action: \"create\" };\n const changes = diffFields([\n [\"name\", def.name, existing.name],\n [\"type\", def.type, existing.type],\n [\"isAddOn\", def.isAddOn ?? false, existing.isAddOn],\n [\"metadata\", normMeta(def.metadata), normMeta(existing.metadata)],\n ]);\n if (!sameSet(def.prices.map(normPrice), (existing.prices ?? []).map(normPrice))) {\n changes.push(\"prices\");\n }\n if (!sameSet((def.entitlements ?? []).map(normEnt), (existing.entitlements ?? []).map(normEnt))) {\n changes.push(\"entitlements\");\n }\n return { kind: \"product\", id: def.slug, action: changes.length ? \"drift\" : \"unchanged\", changes };\n}\n\nfunction planCoupon(def: ConfigCoupon, existing: Coupon | undefined): SyncItem {\n if (!existing) return { kind: \"coupon\", id: def.code, action: \"create\" };\n const changes = diffFields([\n [\"kind\", def.kind, existing.kind],\n [\"value\", def.value, existing.value],\n [\"currency\", def.currency?.toUpperCase() ?? null, existing.currency],\n [\"duration\", def.duration, existing.duration],\n [\"durationCycles\", def.durationCycles ?? null, existing.durationCycles],\n [\"maxRedemptions\", def.maxRedemptions ?? null, existing.maxRedemptions],\n [\"expiresAt\", epochOrNull(def.expiresAt), epochOrNull(existing.expiresAt)],\n ]);\n if (!sameSet(def.productRestrictions ?? [], existing.productRestrictions ?? [])) {\n changes.push(\"productRestrictions\");\n }\n return { kind: \"coupon\", id: def.code, action: changes.length ? \"drift\" : \"unchanged\", changes };\n}\n\n// ---------------------------------------------------------------------------\n// Normalization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Canonicalize a feature's meter for drift comparison. A boolean feature has no meter\n * (null); a metered feature with no explicit config gets the server's `sum` default, so\n * an omitted meter and an explicit `sum` meter compare equal. Filter objects are reshaped\n * to a fixed key order (a jsonb round-trip can reorder keys) while list order is preserved.\n */\n/**\n * Canonicalize a product's metadata map for drift comparison: sort keys so a mere\n * key-order difference (jsonb round-trips keys in insertion order) isn't reported as drift.\n * An omitted map and an empty map compare equal.\n */\nfunction normMeta(metadata: Record<string, string> | undefined): string {\n const entries = Object.entries(metadata ?? {}).sort((a, b) =>\n a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0,\n );\n return JSON.stringify(entries);\n}\n\nfunction normMeter(kind: FeatureKind, meter: MeterConfig | undefined): string | null {\n if (kind !== \"metered\") return null;\n return JSON.stringify({\n aggregation: meter?.aggregation ?? \"sum\",\n eventProperty: meter?.eventProperty ?? null,\n // The filter list and each filter's values are unordered sets — sort both so a mere\n // reordering isn't reported as drift (matches the server's no-op semantics).\n filters: (meter?.filters ?? [])\n .map((f) => ({ key: f.key, values: [...f.values].sort() }))\n // Total order: by key, then by (already-sorted) values, so same-key duplicate\n // filters canonicalize too rather than keeping input order.\n .sort((a, b) => {\n if (a.key !== b.key) return a.key < b.key ? -1 : 1;\n const av = JSON.stringify(a.values);\n const bv = JSON.stringify(b.values);\n return av < bv ? -1 : av > bv ? 1 : 0;\n }),\n roundingMode: meter?.roundingMode ?? \"none\",\n roundingScale: meter?.roundingScale ?? null,\n // Reset anchoring (plan 07). Server omits these for cycle meters, so default to cycle/null\n // on both sides — an omitted anchor and an explicit `cycle` compare equal (no false drift).\n resetAnchor: meter?.resetAnchor ?? \"cycle\",\n resetTz: meter?.resetTz ?? null,\n });\n}\n\nfunction normPrice(p: {\n kind: string;\n interval?: string | null;\n intervalCount?: number;\n amount: number;\n currency: string;\n meterFeature?: string | null;\n usageModel?: string | null;\n usageTiers?: unknown;\n packageSize?: number | null;\n rateBps?: number | null;\n taxRateBps?: number | null;\n taxBehavior?: string | null;\n}): string {\n return JSON.stringify({\n kind: p.kind,\n interval: p.interval ?? null,\n intervalCount: p.intervalCount ?? 1,\n amount: p.amount,\n currency: p.currency.toUpperCase(),\n meterFeature: p.meterFeature ?? null,\n // Usage prices default to graduated server-side; normalize so a model change\n // (or package/percentage param change) is detected as drift, not \"unchanged\".\n usageModel: p.kind === \"usage\" ? (p.usageModel ?? \"graduated\") : null,\n usageTiers: canonTiers(p.usageTiers),\n packageSize: p.packageSize ?? null,\n rateBps: p.rateBps ?? null,\n // Tax overrides (Phase E) — null = inherit org default; included so a change\n // is detected as drift.\n taxRateBps: p.taxRateBps ?? null,\n taxBehavior: p.taxBehavior ?? null,\n });\n}\n\n/**\n * Canonicalize usage tiers to a fixed key order before comparison. A jsonb\n * round-trip can reorder object keys, so raw `JSON.stringify` would report drift\n * on an unchanged tier set — normalize each tier to `{ upTo, unitAmount, flatAmount }`.\n */\nfunction canonTiers(\n tiers: unknown,\n): { upTo: unknown; unitAmount: unknown; flatAmount: unknown }[] | null {\n if (!Array.isArray(tiers)) return null;\n return tiers.map((t) => {\n const tier = (t ?? {}) as { upTo?: unknown; unitAmount?: unknown; flatAmount?: unknown };\n return {\n upTo: tier.upTo ?? null,\n unitAmount: tier.unitAmount ?? null,\n flatAmount: tier.flatAmount ?? null,\n };\n });\n}\n\nfunction normEnt(e: {\n feature: string;\n allowance?: number | null;\n resetInterval?: string | null;\n rollover?: boolean;\n}): string {\n return JSON.stringify({\n feature: e.feature,\n allowance: e.allowance ?? null,\n resetInterval: e.resetInterval ?? null,\n rollover: e.rollover ?? false,\n });\n}\n\n/** Field-by-field equality, returning `\"field: a → b\"` for each mismatch. */\nfunction diffFields(fields: [string, unknown, unknown][]): string[] {\n const changes: string[] = [];\n for (const [name, want, have] of fields) {\n if (want !== have) changes.push(`${name}: ${fmt(have)} → ${fmt(want)}`);\n }\n return changes;\n}\n\n/** Order-insensitive equality of two string lists (compared as multisets). */\nfunction sameSet(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n const sortedA = [...a].sort();\n const sortedB = [...b].sort();\n return sortedA.every((v, i) => v === sortedB[i]);\n}\n\nfunction epochOrNull(value: string | null | undefined): number | null {\n return value ? new Date(value).getTime() : null;\n}\n\nfunction fmt(value: unknown): string {\n if (value === null || value === undefined) return \"∅\";\n return String(value);\n}\n"]}
1
+ {"version":3,"sources":["../src/config.ts"],"names":[],"mappings":"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","file":"config.js","sourcesContent":["/**\n * Code-as-config sync (ARCHITECTURE §12 \"code-as-config sync\"). Keep your catalog\n * - features, products, coupons - in a typed config file and `syncConfig` it into\n * a billow server. Sync is **declarative and additive**: it creates whatever is\n * missing and *reports drift* on what already exists, but never deletes and never\n * rewrites an existing product (catalog edits are made deliberately through the\n * dashboard/API - ADR-0015 - not silently on every sync).\n *\n * import { defineConfig, syncConfig } from \"@usebillow/sdk/config\";\n * const config = defineConfig({ features: [...], products: [...], coupons: [...] });\n * await syncConfig(billow, config, { onProgress: console.log });\n */\nimport type {\n Billow,\n Coupon,\n CreateCouponInput,\n CreateProductInput,\n Feature,\n FeatureKind,\n MeterConfig,\n Product,\n} from \"./index\";\n\nexport interface ConfigFeature {\n slug: string;\n name: string;\n kind: FeatureKind;\n /**\n * For a `metered` feature: how its usage aggregates (ADR-0011). Omit to get a `sum`\n * meter. Set it here so sync creates the meter you want instead of the default - after\n * usage is recorded the aggregation can no longer change.\n */\n meter?: MeterConfig;\n}\n\n/** A product entry - the same shape as `billow.products.create`. */\nexport type ConfigProduct = CreateProductInput;\n\n/**\n * A coupon entry. Unlike `billow.coupons.create`, the `code` is **required** - it\n * is the stable identity sync diffs against (a code-less coupon can't be matched).\n */\nexport type ConfigCoupon = Omit<CreateCouponInput, \"code\"> & { code: string };\n\nexport interface BillowConfig {\n features?: ConfigFeature[];\n products?: ConfigProduct[];\n coupons?: ConfigCoupon[];\n}\n\n/** Identity helper - returns its argument, typed, for editor completion + checks. */\nexport function defineConfig(config: BillowConfig): BillowConfig {\n return config;\n}\n\nexport type SyncAction = \"create\" | \"unchanged\" | \"drift\";\n\nexport interface SyncItem {\n kind: \"feature\" | \"product\" | \"coupon\";\n /** Slug (features/products) or code (coupons). */\n id: string;\n action: SyncAction;\n /** For `drift`: which aspects differ from the server (human-readable). */\n changes?: string[];\n}\n\nexport interface SyncPlan {\n features: SyncItem[];\n products: SyncItem[];\n coupons: SyncItem[];\n}\n\nexport interface SyncResult {\n plan: SyncPlan;\n created: { features: string[]; products: string[]; coupons: string[] };\n /**\n * Items that still differ from the config after the sync - reported, not applied. With\n * `applyMetadata`, product metadata that was written is removed from here (see `applied`).\n */\n drift: SyncItem[];\n /** Product slugs whose metadata was written this run (only when `applyMetadata` is set). */\n applied: { productMetadata: string[] };\n}\n\n/**\n * Diff a config against the server without writing anything. Each entry is\n * `create` (missing), `unchanged` (matches), or `drift` (exists but differs).\n */\nexport async function planConfig(billow: Billow, config: BillowConfig): Promise<SyncPlan> {\n // Collect the whole catalog in as few round-trips as the server allows (page size caps at 100).\n const [features, products, coupons] = await Promise.all([\n billow.features.list({ limit: 100 }).listAll(),\n billow.products.list(),\n billow.coupons.list({ limit: 100 }).listAll(),\n ]);\n\n const featureBySlug = new Map(features.map((f) => [f.slug, f]));\n const productBySlug = new Map(products.map((p) => [p.slug, p]));\n const couponByCode = new Map(coupons.filter((c) => c.code).map((c) => [c.code as string, c]));\n\n return {\n features: (config.features ?? []).map((f) => planFeature(f, featureBySlug.get(f.slug))),\n products: (config.products ?? []).map((p) => planProduct(p, productBySlug.get(p.slug))),\n coupons: (config.coupons ?? []).map((c) => planCoupon(c, couponByCode.get(c.code))),\n };\n}\n\n/**\n * Apply a config: create everything missing (features → products → coupons, the\n * dependency order, since products reference feature slugs), and return what was\n * created plus any drift it found.\n *\n * Structural drift (prices, entitlements, name, ...) is never auto-applied - those edits are\n * version-bearing (ADR-0015: collected at renewal) and belong to a deliberate `products.update`.\n * With `applyMetadata: true`, product **metadata** drift IS written, because metadata lives on\n * the product row (not the version), so updating it is version-independent and cannot rewrite\n * billed history. This closes the write-side gap where a config's product metadata could only\n * be reported, never pushed. The write is a full-map replace (config is authoritative): a key\n * present on the server but absent from the config is cleared.\n */\nexport async function syncConfig(\n billow: Billow,\n config: BillowConfig,\n opts: { onProgress?: (message: string) => void; applyMetadata?: boolean } = {},\n): Promise<SyncResult> {\n const plan = await planConfig(billow, config);\n const progress = opts.onProgress ?? (() => {});\n\n // Create in dependency order - features → products → coupons - since products\n // reference feature slugs. Each kind is created before the next begins.\n const created = {\n features: await createMissing(\n plan.features,\n config.features ?? [],\n (f) => f.slug,\n \"feature\",\n (f) => billow.features.create(f),\n progress,\n ),\n products: await createMissing(\n plan.products,\n config.products ?? [],\n (p) => p.slug,\n \"product\",\n (p) => billow.products.create(p),\n progress,\n ),\n coupons: await createMissing(\n plan.coupons,\n config.coupons ?? [],\n (c) => c.code,\n \"coupon\",\n (c) => billow.coupons.create(c),\n progress,\n ),\n };\n\n // Version-independent metadata is the one field safe to auto-write (a product-row column,\n // not a version). Apply it before computing drift so a resolved item drops out of the report.\n const applied = { productMetadata: [] as string[] };\n if (opts.applyMetadata) {\n const isMetadataChange = (c: string) => c.startsWith(\"metadata\");\n const productDefBySlug = new Map((config.products ?? []).map((p) => [p.slug, p]));\n for (const item of plan.products) {\n if (item.action !== \"drift\" || !item.changes?.some(isMetadataChange)) continue;\n const def = productDefBySlug.get(item.id);\n if (!def) continue;\n await billow.products.update(item.id, { metadata: def.metadata ?? {} });\n applied.productMetadata.push(item.id);\n progress(`~ product ${item.id} (metadata applied)`);\n // Reflect the write in the plan: drop metadata from this item, and mark it unchanged if\n // it had no other drift, so the returned drift no longer reports what we just fixed.\n item.changes = item.changes.filter((c) => !isMetadataChange(c));\n if (item.changes.length === 0) item.action = \"unchanged\";\n }\n }\n\n const drift = [...plan.features, ...plan.products, ...plan.coupons].filter(\n (i) => i.action === \"drift\",\n );\n return { plan, created, drift, applied };\n}\n\n/**\n * Create the `action: \"create\"` items of one kind, matching each plan item back to\n * its config definition by id (a Map, not a scan), and return the ids created.\n */\nasync function createMissing<T>(\n items: SyncItem[],\n defs: T[],\n idOf: (def: T) => string,\n label: string,\n create: (def: T) => Promise<unknown>,\n progress: (message: string) => void,\n): Promise<string[]> {\n const byId = new Map(defs.map((d) => [idOf(d), d]));\n const created: string[] = [];\n for (const item of items) {\n if (item.action !== \"create\") continue;\n const def = byId.get(item.id);\n if (!def) continue;\n await create(def);\n created.push(item.id);\n progress(`+ ${label} ${item.id}`);\n }\n return created;\n}\n\n/** A one-line-per-item summary of a plan, for CLI output. */\nexport function formatPlan(plan: SyncPlan): string {\n const mark = { create: \"+\", unchanged: \"=\", drift: \"~\" } as const;\n const lines: string[] = [];\n for (const item of [...plan.features, ...plan.products, ...plan.coupons]) {\n const detail = item.changes?.length ? ` (${item.changes.join(\", \")})` : \"\";\n lines.push(`${mark[item.action]} ${item.kind} ${item.id}${detail}`);\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Per-entity diffing\n// ---------------------------------------------------------------------------\n\nfunction planFeature(def: ConfigFeature, existing: Feature | undefined): SyncItem {\n if (!existing) return { kind: \"feature\", id: def.slug, action: \"create\" };\n const changes = diffFields([\n [\"name\", def.name, existing.name],\n [\"kind\", def.kind, existing.kind],\n // A metered feature with no explicit meter gets a `sum` meter server-side; normalize\n // both sides so an aggregation change is reported as drift, not \"unchanged\".\n [\n \"meter\",\n normMeter(def.kind, def.meter),\n normMeter(existing.kind, existing.meter ?? undefined),\n ],\n ]);\n return { kind: \"feature\", id: def.slug, action: changes.length ? \"drift\" : \"unchanged\", changes };\n}\n\nfunction planProduct(def: ConfigProduct, existing: Product | undefined): SyncItem {\n if (!existing) return { kind: \"product\", id: def.slug, action: \"create\" };\n const changes = diffFields([\n [\"name\", def.name, existing.name],\n [\"type\", def.type, existing.type],\n [\"isAddOn\", def.isAddOn ?? false, existing.isAddOn],\n [\"metadata\", normMeta(def.metadata), normMeta(existing.metadata)],\n ]);\n if (!sameSet(def.prices.map(normPrice), (existing.prices ?? []).map(normPrice))) {\n changes.push(\"prices\");\n }\n if (!sameSet((def.entitlements ?? []).map(normEnt), (existing.entitlements ?? []).map(normEnt))) {\n changes.push(\"entitlements\");\n }\n return { kind: \"product\", id: def.slug, action: changes.length ? \"drift\" : \"unchanged\", changes };\n}\n\nfunction planCoupon(def: ConfigCoupon, existing: Coupon | undefined): SyncItem {\n if (!existing) return { kind: \"coupon\", id: def.code, action: \"create\" };\n const changes = diffFields([\n [\"kind\", def.kind, existing.kind],\n [\"value\", def.value, existing.value],\n [\"currency\", def.currency?.toUpperCase() ?? null, existing.currency],\n [\"duration\", def.duration, existing.duration],\n [\"durationCycles\", def.durationCycles ?? null, existing.durationCycles],\n [\"maxRedemptions\", def.maxRedemptions ?? null, existing.maxRedemptions],\n [\"expiresAt\", epochOrNull(def.expiresAt), epochOrNull(existing.expiresAt)],\n ]);\n if (!sameSet(def.productRestrictions ?? [], existing.productRestrictions ?? [])) {\n changes.push(\"productRestrictions\");\n }\n return { kind: \"coupon\", id: def.code, action: changes.length ? \"drift\" : \"unchanged\", changes };\n}\n\n// ---------------------------------------------------------------------------\n// Normalization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Canonicalize a feature's meter for drift comparison. A boolean feature has no meter\n * (null); a metered feature with no explicit config gets the server's `sum` default, so\n * an omitted meter and an explicit `sum` meter compare equal. Filter objects are reshaped\n * to a fixed key order (a jsonb round-trip can reorder keys) while list order is preserved.\n */\n/**\n * Canonicalize a product's metadata map for drift comparison: sort keys so a mere\n * key-order difference (jsonb round-trips keys in insertion order) isn't reported as drift.\n * An omitted map and an empty map compare equal.\n */\nfunction normMeta(metadata: Record<string, string> | undefined): string {\n const entries = Object.entries(metadata ?? {}).sort((a, b) =>\n a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0,\n );\n return JSON.stringify(entries);\n}\n\nfunction normMeter(kind: FeatureKind, meter: MeterConfig | undefined): string | null {\n if (kind !== \"metered\") return null;\n return JSON.stringify({\n aggregation: meter?.aggregation ?? \"sum\",\n eventProperty: meter?.eventProperty ?? null,\n // The filter list and each filter's values are unordered sets - sort both so a mere\n // reordering isn't reported as drift (matches the server's no-op semantics).\n filters: (meter?.filters ?? [])\n .map((f) => ({ key: f.key, values: [...f.values].sort() }))\n // Total order: by key, then by (already-sorted) values, so same-key duplicate\n // filters canonicalize too rather than keeping input order.\n .sort((a, b) => {\n if (a.key !== b.key) return a.key < b.key ? -1 : 1;\n const av = JSON.stringify(a.values);\n const bv = JSON.stringify(b.values);\n return av < bv ? -1 : av > bv ? 1 : 0;\n }),\n roundingMode: meter?.roundingMode ?? \"none\",\n roundingScale: meter?.roundingScale ?? null,\n // Reset anchoring (plan 07). Server omits these for cycle meters, so default to cycle/null\n // on both sides - an omitted anchor and an explicit `cycle` compare equal (no false drift).\n resetAnchor: meter?.resetAnchor ?? \"cycle\",\n resetTz: meter?.resetTz ?? null,\n });\n}\n\nfunction normPrice(p: {\n kind: string;\n interval?: string | null;\n intervalCount?: number;\n amount: number;\n currency: string;\n meterFeature?: string | null;\n usageModel?: string | null;\n usageTiers?: unknown;\n packageSize?: number | null;\n rateBps?: number | null;\n taxRateBps?: number | null;\n taxBehavior?: string | null;\n}): string {\n return JSON.stringify({\n kind: p.kind,\n interval: p.interval ?? null,\n intervalCount: p.intervalCount ?? 1,\n amount: p.amount,\n currency: p.currency.toUpperCase(),\n meterFeature: p.meterFeature ?? null,\n // Usage prices default to graduated server-side; normalize so a model change\n // (or package/percentage param change) is detected as drift, not \"unchanged\".\n usageModel: p.kind === \"usage\" ? (p.usageModel ?? \"graduated\") : null,\n usageTiers: canonTiers(p.usageTiers),\n packageSize: p.packageSize ?? null,\n rateBps: p.rateBps ?? null,\n // Tax overrides (Phase E) - null = inherit org default; included so a change\n // is detected as drift.\n taxRateBps: p.taxRateBps ?? null,\n taxBehavior: p.taxBehavior ?? null,\n });\n}\n\n/**\n * Canonicalize usage tiers to a fixed key order before comparison. A jsonb\n * round-trip can reorder object keys, so raw `JSON.stringify` would report drift\n * on an unchanged tier set - normalize each tier to `{ upTo, unitAmount, flatAmount }`.\n */\nfunction canonTiers(\n tiers: unknown,\n): { upTo: unknown; unitAmount: unknown; flatAmount: unknown }[] | null {\n if (!Array.isArray(tiers)) return null;\n return tiers.map((t) => {\n const tier = (t ?? {}) as { upTo?: unknown; unitAmount?: unknown; flatAmount?: unknown };\n return {\n upTo: tier.upTo ?? null,\n unitAmount: tier.unitAmount ?? null,\n flatAmount: tier.flatAmount ?? null,\n };\n });\n}\n\nfunction normEnt(e: {\n feature: string;\n allowance?: number | null;\n resetInterval?: string | null;\n rollover?: boolean;\n}): string {\n return JSON.stringify({\n feature: e.feature,\n allowance: e.allowance ?? null,\n resetInterval: e.resetInterval ?? null,\n rollover: e.rollover ?? false,\n });\n}\n\n/** Field-by-field equality, returning `\"field: a → b\"` for each mismatch. */\nfunction diffFields(fields: [string, unknown, unknown][]): string[] {\n const changes: string[] = [];\n for (const [name, want, have] of fields) {\n if (want !== have) changes.push(`${name}: ${fmt(have)} → ${fmt(want)}`);\n }\n return changes;\n}\n\n/** Order-insensitive equality of two string lists (compared as multisets). */\nfunction sameSet(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n const sortedA = [...a].sort();\n const sortedB = [...b].sort();\n return sortedA.every((v, i) => v === sortedB[i]);\n}\n\nfunction epochOrNull(value: string | null | undefined): number | null {\n return value ? new Date(value).getTime() : null;\n}\n\nfunction fmt(value: unknown): string {\n if (value === null || value === undefined) return \"∅\";\n return String(value);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { H as HostedDomainResponse } from './index.d-CKQAhQfJ.cjs';
1
+ import { H as HostedDomainResponse } from './index.d-DSEYhV2c.cjs';
2
2
 
3
3
  /**
4
4
  * Public SDK types for prepaid credits. Re-exported by ../types.ts.
@@ -67,9 +67,33 @@ interface CreateCreditGrantInput {
67
67
  }
68
68
  /** A Credit Account's status: `frozen` takes no new reservations; `closed` (erased) takes nothing. */
69
69
  type CreditAccountStatus = "active" | "frozen" | "closed";
70
+ /** What current Included Credit Windows include, and where it stands. */
71
+ interface IncludedCredits {
72
+ /** What the windows include. */
73
+ granted: string;
74
+ /** Consumed from their credits, net of reversals. */
75
+ used: string;
76
+ held: string;
77
+ /** Unheld and spendable, credits recorded but not issued yet included. */
78
+ remaining: string;
79
+ }
80
+ /** One subscription's current Included Credit Window, in a customer's balance. */
81
+ interface IncludedPeriod extends IncludedCredits {
82
+ subscriptionId: string;
83
+ /** The window (ISO 8601), half-open: its credits expire at `end`. */
84
+ start: string;
85
+ end: string;
86
+ /** The billing period it belongs to (a trial's is the trial). */
87
+ periodStart: string;
88
+ periodEnd: string;
89
+ /** The next window's start within the paid period (a monthly refill), or null. */
90
+ nextRefillAt: string | null;
91
+ }
70
92
  /**
71
93
  * A customer's credit balance, true at `asOf`. A balance never grants spending authority - only a
72
- * reservation does. A customer with no credit account yet reads as zeros, `active`, `exhausted`.
94
+ * reservation does. A customer with no credit account yet reads as their subscriptions' recorded
95
+ * included credits alone (zeros without any), `active`, `exhausted` when nothing is spendable.
96
+ * Included credits count from the instant their window is current, before Billow issues them.
73
97
  */
74
98
  interface CreditBalance {
75
99
  /** The customer's external id. */
@@ -79,7 +103,10 @@ interface CreditBalance {
79
103
  spendable: string;
80
104
  /** Credits held by open reservations. */
81
105
  held: string;
82
- /** Credits consumed since `periodStart`, net of reversals. */
106
+ /**
107
+ * Credits consumed since `periodStart`, net of their reversals: a reversal counts against the
108
+ * period of the consumption it reverses, so this is never negative.
109
+ */
83
110
  consumedThisPeriod: string;
84
111
  /** The current included period's start, else the first instant of the current UTC month. */
85
112
  periodStart: string;
@@ -99,22 +126,23 @@ interface CreditBalance {
99
126
  /** Spendable on a category not listed: the grants that pay for every category. */
100
127
  otherCategories: string;
101
128
  };
102
- /** The active included grants (a subscription's period allowance); null when there are none. */
103
- currentPeriod: {
104
- granted: string;
105
- /** Consumed from them, net of reversals. */
106
- used: string;
107
- held: string;
108
- /** Unheld and spendable. */
109
- remaining: string;
129
+ /**
130
+ * The current Included Credit Windows together - the billing period, or the current month under
131
+ * a monthly allocation - across the customer's subscriptions; null when none is current (between
132
+ * a period's end and the payment that grants the next).
133
+ */
134
+ currentPeriod: (IncludedCredits & {
135
+ /** The earliest current window's start. */
110
136
  start: string;
111
- /** When they expire (the period end); null if one never does. */
112
- end: string | null;
113
- } | null;
137
+ /** The earliest current window's end: when the first of these credits stop. */
138
+ end: string;
139
+ }) | null;
140
+ /** Each subscription's current window, soonest end first (empty when none is current). */
141
+ includedPeriods: IncludedPeriod[];
114
142
  thresholds: {
115
143
  lowPercent: number;
116
144
  criticalPercent: number;
117
- /** What the percentages apply to: the current period's credits, else the last top-up's. */
145
+ /** What the percentages apply to: the current windows' credits, else the last top-up's. */
118
146
  reference: string | null;
119
147
  level: CreditThresholdLevel;
120
148
  };
@@ -127,7 +155,11 @@ type CreditTransactionType = "grant" | "hold" | "release" | "consume" | "expire"
127
155
  interface CreditLedgerEntry {
128
156
  id: string;
129
157
  type: CreditTransactionType;
130
- /** Why Billow posted it, as a system code (`api_grant`, `grant_expired`, ...); never free text. */
158
+ /**
159
+ * Why Billow posted it, as a system code (`api_grant`, `grant_expired`, `subscription_period`,
160
+ * `subscription_upgrade`, `subscription_canceled`, `subscription_charged_back`, ...); never free
161
+ * text.
162
+ */
131
163
  reason: string | null;
132
164
  /** How it moved the account's unheld credits (signed). */
133
165
  availableDelta: string;
@@ -144,6 +176,8 @@ interface CreditLedgerEntry {
144
176
  callerReason: string | null;
145
177
  /** The metadata of the resource it belongs to: its reservation, reversal or top-up, else its grant. */
146
178
  metadata: Record<string, string> | null;
179
+ /** On an included grant's transaction: the subscription whose window issued the grant. */
180
+ subscriptionId: string | null;
147
181
  createdAt: string;
148
182
  }
149
183
  /** What `credits.usage.get` totals each row by. */
@@ -326,6 +360,79 @@ interface CreateCreditIncludedRuleInput {
326
360
  type UpdateCreditIncludedRuleInput = Partial<Omit<CreateCreditIncludedRuleInput, "priceId">> & {
327
361
  archived?: boolean;
328
362
  };
363
+ /**
364
+ * Where an Included Credit Window stands: `scheduled` (a later window, not started), `pending`
365
+ * (current, waiting to be issued - Billow issues it within seconds), `blocked` (current, but its
366
+ * last issue attempt could not go through - see `blockedCause` - and Billow retries it with
367
+ * backoff), `issued`, `suspended` (held by a pause), `lapsed` (over without being issued: a period
368
+ * paid after it ended, or one that passed while paused), `ended` (ended early, with `endCause`),
369
+ * or `account_closed` (the customer was erased, so it never issues).
370
+ */
371
+ type CreditWindowState = "scheduled" | "pending" | "blocked" | "issued" | "suspended" | "lapsed" | "ended" | "account_closed";
372
+ /**
373
+ * An Included Credit Window: what one window of a period Billow granted a subscription includes -
374
+ * the whole billing period, one month of it under a monthly rule, or a trial - and how far its
375
+ * credits have been issued.
376
+ */
377
+ interface CreditIncludedWindow {
378
+ id: string;
379
+ subscriptionId: string;
380
+ /** The customer's external id. */
381
+ customerId: string;
382
+ /** The window (ISO 8601), half-open: its credits expire at `end`. */
383
+ start: string;
384
+ end: string;
385
+ /** The billing period it belongs to (a trial's is the trial). */
386
+ periodStart: string;
387
+ periodEnd: string;
388
+ /** The next window's start within the period (a monthly refill), or null for its last window. */
389
+ nextRefillAt: string | null;
390
+ /** How the rule allocated the period when it was granted. */
391
+ allocation: CreditAllocationInterval;
392
+ /** The categories its credits may pay for; null = every category. */
393
+ eligibility: string[] | null;
394
+ /** Microcredits the window includes. */
395
+ entitled: string;
396
+ /** Microcredits issued to the customer for it so far. */
397
+ issued: string;
398
+ state: CreditWindowState;
399
+ /**
400
+ * For a `blocked` window: `capacity` (its credits would take the customer's balance past the
401
+ * largest total Billow holds) or `error` (an unexpected fault); null otherwise.
402
+ */
403
+ blockedCause: "capacity" | "error" | null;
404
+ /** For a `pending` or `blocked` window: since when it has been due. */
405
+ dueSince: string | null;
406
+ /** When an `ended` window ended early; null otherwise. */
407
+ endedAt: string | null;
408
+ /**
409
+ * Why an `ended` window ended early: `canceled` (the subscription was canceled immediately) or
410
+ * `charged_back` (the payment for its period was lost to a chargeback); null otherwise.
411
+ */
412
+ endCause: "canceled" | "charged_back" | null;
413
+ /**
414
+ * When a plan upgrade inside the period last raised what the window includes (or added the
415
+ * window, to a period that had none); null for a window as its period was granted.
416
+ */
417
+ raisedAt: string | null;
418
+ createdAt: string;
419
+ }
420
+ /**
421
+ * Whose Included Credit Windows to list: one or more subscriptions, or a customer (by external id)
422
+ * - exactly one of the two.
423
+ */
424
+ type CreditIncludedWindowListParams = ({
425
+ subscription: string | string[];
426
+ customer?: never;
427
+ } | {
428
+ customer: string;
429
+ subscription?: never;
430
+ }) & {
431
+ /** Only each subscription's latest window that has started (what the subscription shows now). */
432
+ current?: boolean;
433
+ limit?: number;
434
+ cursor?: string;
435
+ };
329
436
  /**
330
437
  * A top-up's state: `pending` until its payment settles; `succeeded` (credits granted) or
331
438
  * `failed` (a later payment on the same checkout still succeeds it); `unfulfilled` - paid after
@@ -427,4 +534,4 @@ interface HostedDomainDisabledData extends HostedDomainEventBase {
427
534
  reason: Extract<HostedDomainStatusReason, "dns_lost" | "reassigned" | "entitlement_lapsed" | "suspended">;
428
535
  }
429
536
 
430
- export type { CreditTopUpStatus as A, CreditTransactionType as B, CreditGrantKind as C, CreditUsageGroupBy as D, HostedDomainEventBase as E, HostedDomainStatus as F, HostedDomainStatusReason as G, HostedDomainActivatedData as H, UpdateCreditPackInput as U, CreditThresholdLevel as a, HostedDomainDnsFailingData as b, HostedDomainReassignmentPendingData as c, HostedDomainDisabledData as d, CreditGrant as e, CreditTopUp as f, CreateCreditGrantInput as g, CreditGrantRequestResult as h, CreditPacks as i, CreateCreditPackInput as j, CreditPack as k, CreateCreditIncludedRuleInput as l, CreditIncludedRule as m, UpdateCreditIncludedRuleInput as n, CreateCreditTopUpInput as o, CreditTopUpRequestResult as p, CreditTopUpListParams as q, CreditBalance as r, CreditLedgerEntry as s, CreditUsageParams as t, CreditUsage as u, CreditAccountStatus as v, CreditAllocationInterval as w, CreditMoney as x, CreditPackBreakdown as y, CreditPackListItem as z };
537
+ export type { CreditPackBreakdown as A, CreditPackListItem as B, CreditGrantKind as C, CreditTopUpStatus as D, CreditTransactionType as E, CreditUsageGroupBy as F, CreditWindowState as G, HostedDomainActivatedData as H, HostedDomainEventBase as I, HostedDomainStatus as J, HostedDomainStatusReason as K, IncludedCredits as L, IncludedPeriod as M, UpdateCreditPackInput as U, CreditThresholdLevel as a, HostedDomainDnsFailingData as b, HostedDomainReassignmentPendingData as c, HostedDomainDisabledData as d, CreditGrant as e, CreditTopUp as f, CreditIncludedWindow as g, CreateCreditGrantInput as h, CreditGrantRequestResult as i, CreditPacks as j, CreateCreditPackInput as k, CreditPack as l, CreateCreditIncludedRuleInput as m, CreditIncludedRule as n, UpdateCreditIncludedRuleInput as o, CreditIncludedWindowListParams as p, CreateCreditTopUpInput as q, CreditTopUpRequestResult as r, CreditTopUpListParams as s, CreditBalance as t, CreditLedgerEntry as u, CreditUsageParams as v, CreditUsage as w, CreditAccountStatus as x, CreditAllocationInterval as y, CreditMoney as z };