@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.
- package/CHANGELOG.md +17 -0
- package/dist/{billing-B16zL5xU.d.cts → billing-8ug1Gw05.d.cts} +9 -9
- package/dist/{billing-BF4OGaLI.d.ts → billing-Bs10EcjR.d.ts} +9 -9
- package/dist/{billing-status-BZQN_gm7.d.cts → billing-status-DZkB0VPK.d.cts} +5 -5
- package/dist/{billing-status-BZQN_gm7.d.ts → billing-status-DZkB0VPK.d.ts} +5 -5
- package/dist/{chunk-CCG4F5FK.js → chunk-47J7FYM5.js} +2 -2
- package/dist/chunk-47J7FYM5.js.map +1 -0
- package/dist/{chunk-GT5VBLN5.js → chunk-XUFQG6EA.js} +74 -22
- package/dist/chunk-XUFQG6EA.js.map +1 -0
- package/dist/config.cjs +3 -3
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +9 -9
- package/dist/config.d.ts +9 -9
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/{hosted-domains-DSTQ1GZz.d.ts → hosted-domains-BrnwkHqq.d.ts} +40 -2
- package/dist/{hosted-domains-DwakPG4J.d.cts → hosted-domains-zWm2srOk.d.cts} +40 -2
- package/dist/index.cjs +72 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -48
- package/dist/index.d.ts +70 -48
- package/dist/index.js +2 -2
- package/dist/ingestion.cjs.map +1 -1
- package/dist/ingestion.d.cts +18 -18
- package/dist/ingestion.d.ts +18 -18
- package/dist/ingestion.js.map +1 -1
- package/dist/react.cjs +28 -5
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -6
- package/dist/react.d.ts +6 -6
- package/dist/react.js +2 -2
- package/dist/react.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +11 -11
- package/dist/server.d.ts +11 -11
- package/dist/server.js.map +1 -1
- package/dist/status.cjs.map +1 -1
- package/dist/status.d.cts +1 -1
- package/dist/status.d.ts +1 -1
- package/dist/status.js +1 -1
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +20 -15
- package/dist/webhooks.d.ts +20 -15
- package/dist/webhooks.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-CCG4F5FK.js.map +0 -1
- 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-
|
|
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-
|
|
5
|
+
import './billing-status-DZkB0VPK.cjs';
|
|
6
6
|
import './status.cjs';
|
|
7
|
-
import './hosted-domains-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
|
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-
|
|
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-
|
|
5
|
+
import './billing-status-DZkB0VPK.js';
|
|
6
6
|
import './status.js';
|
|
7
|
-
import './hosted-domains-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
package/dist/config.js.map
CHANGED
|
@@ -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
|
-
/**
|
|
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 {
|
|
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
|
-
/**
|
|
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 {
|
|
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 };
|