@usebillow/sdk 0.9.0 → 0.11.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 (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/{billing-B16zL5xU.d.cts → billing-8ug1Gw05.d.cts} +9 -9
  3. package/dist/{billing-BF4OGaLI.d.ts → billing-Bs10EcjR.d.ts} +9 -9
  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-GT5VBLN5.js → chunk-XUFQG6EA.js} +74 -22
  9. package/dist/chunk-XUFQG6EA.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 +9 -9
  13. package/dist/config.d.ts +9 -9
  14. package/dist/config.js +3 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/{hosted-domains-DSTQ1GZz.d.ts → hosted-domains-BrnwkHqq.d.ts} +40 -2
  17. package/dist/{hosted-domains-DwakPG4J.d.cts → hosted-domains-zWm2srOk.d.cts} +40 -2
  18. package/dist/index.cjs +72 -20
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +70 -48
  21. package/dist/index.d.ts +70 -48
  22. package/dist/index.js +2 -2
  23. package/dist/ingestion.cjs.map +1 -1
  24. package/dist/ingestion.d.cts +18 -18
  25. package/dist/ingestion.d.ts +18 -18
  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 +6 -6
  30. package/dist/react.d.ts +6 -6
  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 +11 -11
  35. package/dist/server.d.ts +11 -11
  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 +20 -15
  43. package/dist/webhooks.d.ts +20 -15
  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-GT5VBLN5.js.map +0 -1
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-B16zL5xU.cjs';
2
+ import { F as FeatureKind, M as MeterConfig, C as CreateProductInput, a as CreateCouponInput } from './billing-8ug1Gw05.cjs';
3
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-DwakPG4J.cjs';
7
+ import './hosted-domains-zWm2srOk.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-BF4OGaLI.js';
2
+ import { F as FeatureKind, M as MeterConfig, C as CreateProductInput, a as CreateCouponInput } from './billing-Bs10EcjR.js';
3
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-DSTQ1GZz.js';
7
+ import './hosted-domains-BrnwkHqq.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"]}
@@ -67,6 +67,17 @@ 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
+ /** A customer's Credit Account status, as `credits.accounts.freeze` and `.unfreeze` answer it. */
71
+ interface CreditAccount {
72
+ /** The customer's external id. */
73
+ customerId: string;
74
+ status: CreditAccountStatus;
75
+ /**
76
+ * Whether this call moved the account to `status`, decided under the account's lock: false for a
77
+ * repeat (freezing a frozen account), so of two racing calls exactly one reports the change.
78
+ */
79
+ changed: boolean;
80
+ }
70
81
  /** What current Included Credit Windows include, and where it stands. */
71
82
  interface IncludedCredits {
72
83
  /** What the windows include. */
@@ -155,7 +166,11 @@ type CreditTransactionType = "grant" | "hold" | "release" | "consume" | "expire"
155
166
  interface CreditLedgerEntry {
156
167
  id: string;
157
168
  type: CreditTransactionType;
158
- /** Why Billow posted it, as a system code (`api_grant`, `grant_expired`, ...); never free text. */
169
+ /**
170
+ * Why Billow posted it, as a system code (`api_grant`, `grant_expired`, `subscription_period`,
171
+ * `subscription_upgrade`, `subscription_canceled`, `subscription_charged_back`, ...); never free
172
+ * text.
173
+ */
159
174
  reason: string | null;
160
175
  /** How it moved the account's unheld credits (signed). */
161
176
  availableDelta: string;
@@ -164,6 +179,11 @@ interface CreditLedgerEntry {
164
179
  availableAfter: string;
165
180
  heldAfter: string;
166
181
  grantId: string | null;
182
+ /**
183
+ * The kind of `grantId`'s grant - a promotional grant and an adjustment both read `api_grant` as
184
+ * their `reason`; null when the transaction names no one grant (a hold, a commit, a reversal).
185
+ */
186
+ grantKind: CreditGrantKind | null;
167
187
  reservationId: string | null;
168
188
  topUpId: string | null;
169
189
  /** On a reversal: the consumption (its `consume` transaction) it gives back. */
@@ -399,8 +419,18 @@ interface CreditIncludedWindow {
399
419
  blockedCause: "capacity" | "error" | null;
400
420
  /** For a `pending` or `blocked` window: since when it has been due. */
401
421
  dueSince: string | null;
422
+ /** When an `ended` window ended early; null otherwise. */
402
423
  endedAt: string | null;
424
+ /**
425
+ * Why an `ended` window ended early: `canceled` (the subscription was canceled immediately) or
426
+ * `charged_back` (the payment for its period was lost to a chargeback); null otherwise.
427
+ */
403
428
  endCause: "canceled" | "charged_back" | null;
429
+ /**
430
+ * When a plan upgrade inside the period last raised what the window includes (or added the
431
+ * window, to a period that had none); null for a window as its period was granted.
432
+ */
433
+ raisedAt: string | null;
404
434
  createdAt: string;
405
435
  }
406
436
  /**
@@ -432,6 +462,8 @@ interface CreditTopUp {
432
462
  /** The customer's external id. */
433
463
  customerId: string;
434
464
  packId: string;
465
+ /** The pack's name when it was bought (renaming the pack later changes no purchase). */
466
+ packName: string;
435
467
  status: CreditTopUpStatus;
436
468
  /** The terms bought: what success grants. */
437
469
  credits: string;
@@ -447,6 +479,12 @@ interface CreditTopUp {
447
479
  checkoutUrl: string | null;
448
480
  purchasedGrantId: string | null;
449
481
  bonusGrantId: string | null;
482
+ /**
483
+ * Microcredits of the credits it granted that the customer has spent (consumed, net of
484
+ * reversals). A refund can never take these back: refunding the whole payment leaves them as its
485
+ * `shortfall`.
486
+ */
487
+ consumed: string;
450
488
  /** Microcredits refunds and lost chargebacks of the payment have revoked so far. */
451
489
  revoked: string;
452
490
  /** Of the revoked credits, those already spent and not paid back since. */
@@ -520,4 +558,4 @@ interface HostedDomainDisabledData extends HostedDomainEventBase {
520
558
  reason: Extract<HostedDomainStatusReason, "dns_lost" | "reassigned" | "entitlement_lapsed" | "suspended">;
521
559
  }
522
560
 
523
- 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 };
561
+ export type { CreditMoney as A, CreditPackBreakdown as B, CreditGrantKind as C, CreditPackListItem as D, CreditTopUpStatus as E, CreditTransactionType as F, CreditUsageGroupBy as G, HostedDomainActivatedData as H, CreditWindowState as I, HostedDomainEventBase as J, HostedDomainStatus as K, HostedDomainStatusReason as L, IncludedCredits as M, IncludedPeriod as N, 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, CreditAccount as j, CreditPacks as k, CreateCreditPackInput as l, CreditPack as m, CreateCreditIncludedRuleInput as n, CreditIncludedRule as o, UpdateCreditIncludedRuleInput as p, CreditIncludedWindowListParams as q, CreateCreditTopUpInput as r, CreditTopUpRequestResult as s, CreditTopUpListParams as t, CreditBalance as u, CreditLedgerEntry as v, CreditUsageParams as w, CreditUsage as x, CreditAccountStatus as y, CreditAllocationInterval as z };
@@ -67,6 +67,17 @@ 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
+ /** A customer's Credit Account status, as `credits.accounts.freeze` and `.unfreeze` answer it. */
71
+ interface CreditAccount {
72
+ /** The customer's external id. */
73
+ customerId: string;
74
+ status: CreditAccountStatus;
75
+ /**
76
+ * Whether this call moved the account to `status`, decided under the account's lock: false for a
77
+ * repeat (freezing a frozen account), so of two racing calls exactly one reports the change.
78
+ */
79
+ changed: boolean;
80
+ }
70
81
  /** What current Included Credit Windows include, and where it stands. */
71
82
  interface IncludedCredits {
72
83
  /** What the windows include. */
@@ -155,7 +166,11 @@ type CreditTransactionType = "grant" | "hold" | "release" | "consume" | "expire"
155
166
  interface CreditLedgerEntry {
156
167
  id: string;
157
168
  type: CreditTransactionType;
158
- /** Why Billow posted it, as a system code (`api_grant`, `grant_expired`, ...); never free text. */
169
+ /**
170
+ * Why Billow posted it, as a system code (`api_grant`, `grant_expired`, `subscription_period`,
171
+ * `subscription_upgrade`, `subscription_canceled`, `subscription_charged_back`, ...); never free
172
+ * text.
173
+ */
159
174
  reason: string | null;
160
175
  /** How it moved the account's unheld credits (signed). */
161
176
  availableDelta: string;
@@ -164,6 +179,11 @@ interface CreditLedgerEntry {
164
179
  availableAfter: string;
165
180
  heldAfter: string;
166
181
  grantId: string | null;
182
+ /**
183
+ * The kind of `grantId`'s grant - a promotional grant and an adjustment both read `api_grant` as
184
+ * their `reason`; null when the transaction names no one grant (a hold, a commit, a reversal).
185
+ */
186
+ grantKind: CreditGrantKind | null;
167
187
  reservationId: string | null;
168
188
  topUpId: string | null;
169
189
  /** On a reversal: the consumption (its `consume` transaction) it gives back. */
@@ -399,8 +419,18 @@ interface CreditIncludedWindow {
399
419
  blockedCause: "capacity" | "error" | null;
400
420
  /** For a `pending` or `blocked` window: since when it has been due. */
401
421
  dueSince: string | null;
422
+ /** When an `ended` window ended early; null otherwise. */
402
423
  endedAt: string | null;
424
+ /**
425
+ * Why an `ended` window ended early: `canceled` (the subscription was canceled immediately) or
426
+ * `charged_back` (the payment for its period was lost to a chargeback); null otherwise.
427
+ */
403
428
  endCause: "canceled" | "charged_back" | null;
429
+ /**
430
+ * When a plan upgrade inside the period last raised what the window includes (or added the
431
+ * window, to a period that had none); null for a window as its period was granted.
432
+ */
433
+ raisedAt: string | null;
404
434
  createdAt: string;
405
435
  }
406
436
  /**
@@ -432,6 +462,8 @@ interface CreditTopUp {
432
462
  /** The customer's external id. */
433
463
  customerId: string;
434
464
  packId: string;
465
+ /** The pack's name when it was bought (renaming the pack later changes no purchase). */
466
+ packName: string;
435
467
  status: CreditTopUpStatus;
436
468
  /** The terms bought: what success grants. */
437
469
  credits: string;
@@ -447,6 +479,12 @@ interface CreditTopUp {
447
479
  checkoutUrl: string | null;
448
480
  purchasedGrantId: string | null;
449
481
  bonusGrantId: string | null;
482
+ /**
483
+ * Microcredits of the credits it granted that the customer has spent (consumed, net of
484
+ * reversals). A refund can never take these back: refunding the whole payment leaves them as its
485
+ * `shortfall`.
486
+ */
487
+ consumed: string;
450
488
  /** Microcredits refunds and lost chargebacks of the payment have revoked so far. */
451
489
  revoked: string;
452
490
  /** Of the revoked credits, those already spent and not paid back since. */
@@ -520,4 +558,4 @@ interface HostedDomainDisabledData extends HostedDomainEventBase {
520
558
  reason: Extract<HostedDomainStatusReason, "dns_lost" | "reassigned" | "entitlement_lapsed" | "suspended">;
521
559
  }
522
560
 
523
- 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 };
561
+ export type { CreditMoney as A, CreditPackBreakdown as B, CreditGrantKind as C, CreditPackListItem as D, CreditTopUpStatus as E, CreditTransactionType as F, CreditUsageGroupBy as G, HostedDomainActivatedData as H, CreditWindowState as I, HostedDomainEventBase as J, HostedDomainStatus as K, HostedDomainStatusReason as L, IncludedCredits as M, IncludedPeriod as N, 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, CreditAccount as j, CreditPacks as k, CreateCreditPackInput as l, CreditPack as m, CreateCreditIncludedRuleInput as n, CreditIncludedRule as o, UpdateCreditIncludedRuleInput as p, CreditIncludedWindowListParams as q, CreateCreditTopUpInput as r, CreditTopUpRequestResult as s, CreditTopUpListParams as t, CreditBalance as u, CreditLedgerEntry as v, CreditUsageParams as w, CreditUsage as x, CreditAccountStatus as y, CreditAllocationInterval as z };