@happyvertical/smrt-subscriptions 0.55.7 → 0.55.9

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.
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-subscriptions",
5
- "packageVersion": "0.55.7",
5
+ "packageVersion": "0.55.9",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-subscriptions:SubscriptionPlanCollection": {
8
8
  "name": "subscriptionplancollection",
@@ -5132,5 +5132,16 @@
5132
5132
  "@happyvertical/smrt-core",
5133
5133
  "@happyvertical/smrt-tenancy"
5134
5134
  ],
5135
+ "browser": {
5136
+ "status": "server-only",
5137
+ "issues": [
5138
+ "#3624",
5139
+ "#3626"
5140
+ ],
5141
+ "reason": "core migrations (node:crypto, node:os)",
5142
+ "via": [
5143
+ "@happyvertical/smrt-tenancy"
5144
+ ]
5145
+ },
5135
5146
  "stampsConstructors": true
5136
5147
  }
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-subscriptions",
6
- "packageVersion": "0.55.7",
6
+ "packageVersion": "0.55.9",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "c45ffdb5f614de5c2fee8676d00eaac85ca552a6dcce45a6f7db4259468d7909",
11
- "packageJson": "5d4c48d7890f9e18abe26ebe9ce86d745a65aedc4b10873aced30f1a68efdb56",
10
+ "manifest": "59241efcba7e341f94eeeccd9ffaae8afeaa869cddc24a386a250a2897141f08",
11
+ "packageJson": "7f0a5d5435d3f8fd3850fb907c44f27810158f0574ce9e3ed0983d4b2c7ebfa1",
12
12
  "agents": "89ab9b5f7047d2f7f316a4bbbb1eef225f3fa8e85f21cb9a699ec3ce18bdb9cd"
13
13
  },
14
14
  "exports": [
@@ -3889,5 +3889,16 @@
3889
3889
  "polymorphicAssociations": 0,
3890
3890
  "uuidColumns": 80
3891
3891
  },
3892
- "agentDoc": "# @happyvertical/smrt-subscriptions\n\nTenant subscriptions, plan features, immutable/idempotent usage evidence,\neffective-dated client pricing, scoped spending policies, and entitlement\nresolution for SMRT applications.\n\n## Validation\n\n```bash\npnpm --filter @happyvertical/smrt-subscriptions test\npnpm --filter @happyvertical/smrt-subscriptions test:postgres\npnpm --filter @happyvertical/smrt-subscriptions typecheck\npnpm --filter @happyvertical/smrt-subscriptions build\n```\n\n`test:postgres` is the lane that holds the money-column line — SQLite's type\naffinity stores a REAL in an INTEGER column without complaint, so a money field\nthat reverted to DECIMAL passes every SQLite suite.\n\n## Money\n\n- **Money is integer minor units** (cents) — `$19.99` is `1999`.\n `SubscriptionPlan.priceAmount`, `ClientCharge.amount`, `RetailCharge.amount`,\n `CreditGrant.amount`,\n `BillingAdjustment.amount` and `SpendingPolicy.limitAmount` all initialize\n `= 0`, never `= 0.0`: the integer literal is what maps them to INTEGER\n columns (BIGINT on fresh PostgreSQL/DuckDB databases; #2401, #2373). `ClientCharge.quantity` and `TenantUsageMetric.quantity` are\n the opposite case — metered quantities are genuinely fractional and stay\n DECIMAL.\n- **`PricingRule.terms` are minor units *per unit of usage*, and stay\n fractional.** A per-token rate is routinely a fraction of a cent, so an\n integer `unitPrice` would truncate it to zero. `calculateAmount()` rounds the\n computed result to a whole minor unit — that is the single boundary in the\n package where a rate meets money, and it is what lets `evaluateSpending()`\n compare charge sums against `limitAmount` exactly.\n- **`adjust()` rejects a fractional amount.** Negative amounts are legitimate\n (a credit); fractional ones almost always mean the caller passed major units.\n- **Billing rejects immediate JSON write-back.** The JSON adapter's default\n `writeStrategy: 'immediate'` and DuckDB with that write-back strategy export\n files before transaction commit, while adapter rollback restores only the\n database transaction. `CommercialUsageService` therefore fails before setup\n on those configurations. PostgreSQL, SQLite, ordinary DuckDB, and explicit\n non-immediate write strategies remain supported. When supplying an already\n resolved database handle, also pass `billingStorage` so this capability\n decision is explicit rather than inferred from private adapter state.\n- **Embedded sourced adjustments serialize per database.** SQLite, DuckDB, and\n supported non-immediate JSON handles multiplex local writes; the complete\n deterministic adjustment flow, including recovery reads, runs under one\n database-scoped lock. Every supported embedded adjustment transaction,\n sourced or unsourced, also holds the core embedded write queue for its full\n lifetime; transaction-bound model saves re-enter that hold so adjustments\n cannot absorb, roll back, or deadlock with unrelated ordinary writes.\n- **UI formats by dividing, never by `toFixed`.** `PlanPicker` and\n `CommercialOverview` scale back to major units using the currency's own\n minor-unit exponent, so zero-decimal currencies (JPY, KRW) are not divided.\n- **Migrating an existing database**: `preflightSubscriptionsMoneyMinorUnits(db)`\n reports which columns still hold major units and which rows would be rounded\n or exceed JavaScript's safe-integer range; `migrateSubscriptionsMoneyToMinorUnits(db)` converts them\n (idempotent via `_smrt_backfills`). On SQLite the values are rescaled but the\n declared type needs the table-rebuild path (#2370). Existing\n `PricingRule.terms` are **not** migrated automatically — only the operator\n knows which keys in a given rule are prices rather than quantities or ratios.\n- **Range and existing deployments**: fresh PostgreSQL/DuckDB INTEGER columns\n are BIGINT, and hydration rejects values outside JavaScript's safe-integer\n range. Existing PostgreSQL `int4` columns require the explicit widening in #2424.\n\n## Reseller billing (#3059)\n\n- Billing ownership is consumed from `smrt-tenancy`'s\n `BillingRelationshipService` (`BillingRelationshipReader`); never re-derive\n parentage or owners here.\n- `ClientCharge` stays one row per usage event (`usage_event_id` unique) and\n `tenantId` is always the payer. That uniqueness is the double-billing guard\n across owner changes; do not widen its conflict key. The retail leg lives in\n `RetailCharge` (also one per event), written in the same transaction.\n- Price-book rules are `PricingRule` rows with `priceBookId`; `price()` must\n keep ignoring them, and `rateUsage()` only trusts rules whose tenant owns the\n book.\n- Delegated `SpendingPolicy` rows (`setByTenantId`) are guarded in\n `validateBeforeSave` and a `beforeDelete` hook, by id and by conflict key;\n `CreditGrant` applies the same parent-only rule to delegated balances.\n `PriceBookAssignmentCollection` is deliberately not a root export.\n- `ResellerBillingService` authorizes first, then writes in a system context\n (parent writes land on child-owned rows). The wholesale leg is authorized by\n the book's publisher and may never be published by the payer; whether a\n publisher is a legitimate provider for the reseller is a host obligation\n (the model records no upstream seller). Relationship reads precede\n authorization and stay under smrt-tenancy's own read rules. The only\n cross-tenant read in evaluation is the bounded `wholesale`-basis sum.\n- `balance` policies take their limit from `CreditGrant`s; `limitAmount` must\n be 0, and currency and period are fixed once saved (the ledger is scoped to\n them). Auto top-up is a host hook only — no payment provider calls. The\n evaluator re-reads credit after the hook returns (grant or not) and hands\n it `currentShortfall()`, so a hook that charges asynchronously can see a\n concurrent top-up (smrt-commerce's `autoTopUpHook()`, #3139).\n\n## Notes\n\n- Keep Stripe-specific fields as provider binding metadata. Runtime Stripe API\n calls belong in the HappyVertical SDK accounting provider.\n- Thresholds are evaluated from tenant usage metrics and optional AI usage\n summaries. Resolve multiple thresholds with the batch usage path where\n available; do not bypass tenant context in application code.\n- Use `SubscriptionResolver.create()` or an explicitly injected\n `SubscriptionResolver` once per request when resolving entitlements multiple\n times. Load and pass `EntitlementResolutionContext` to avoid re-reading the\n current subscription and plan.\n- Subscription UI components should stay provider-neutral and receive actions\n from the host app.\n"
3892
+ "agentDoc": "# @happyvertical/smrt-subscriptions\n\nTenant subscriptions, plan features, immutable/idempotent usage evidence,\neffective-dated client pricing, scoped spending policies, and entitlement\nresolution for SMRT applications.\n\n## Validation\n\n```bash\npnpm --filter @happyvertical/smrt-subscriptions test\npnpm --filter @happyvertical/smrt-subscriptions test:postgres\npnpm --filter @happyvertical/smrt-subscriptions typecheck\npnpm --filter @happyvertical/smrt-subscriptions build\n```\n\n`test:postgres` is the lane that holds the money-column line — SQLite's type\naffinity stores a REAL in an INTEGER column without complaint, so a money field\nthat reverted to DECIMAL passes every SQLite suite.\n\n## Money\n\n- **Money is integer minor units** (cents) — `$19.99` is `1999`.\n `SubscriptionPlan.priceAmount`, `ClientCharge.amount`, `RetailCharge.amount`,\n `CreditGrant.amount`,\n `BillingAdjustment.amount` and `SpendingPolicy.limitAmount` all initialize\n `= 0`, never `= 0.0`: the integer literal is what maps them to INTEGER\n columns (BIGINT on fresh PostgreSQL/DuckDB databases; #2401, #2373). `ClientCharge.quantity` and `TenantUsageMetric.quantity` are\n the opposite case — metered quantities are genuinely fractional and stay\n DECIMAL.\n- **`PricingRule.terms` are minor units *per unit of usage*, and stay\n fractional.** A per-token rate is routinely a fraction of a cent, so an\n integer `unitPrice` would truncate it to zero. `calculateAmount()` rounds the\n computed result to a whole minor unit — that is the single boundary in the\n package where a rate meets money, and it is what lets `evaluateSpending()`\n compare charge sums against `limitAmount` exactly.\n- **`adjust()` rejects a fractional amount.** Negative amounts are legitimate\n (a credit); fractional ones almost always mean the caller passed major units.\n- **Billing rejects immediate JSON write-back.** The JSON adapter's default\n `writeStrategy: 'immediate'` and DuckDB with that write-back strategy export\n files before transaction commit, while adapter rollback restores only the\n database transaction. `CommercialUsageService` therefore fails before setup\n on those configurations. PostgreSQL, SQLite, ordinary DuckDB, and explicit\n non-immediate write strategies remain supported. When supplying an already\n resolved database handle, also pass `billingStorage` so this capability\n decision is explicit rather than inferred from private adapter state.\n- **Embedded sourced adjustments serialize per database.** SQLite, DuckDB, and\n supported non-immediate JSON handles multiplex local writes; the complete\n deterministic adjustment flow, including recovery reads, runs under one\n database-scoped lock. Every supported embedded adjustment transaction,\n sourced or unsourced, also holds the core embedded write queue for its full\n lifetime; transaction-bound model saves re-enter that hold so adjustments\n cannot absorb, roll back, or deadlock with unrelated ordinary writes.\n- **UI formats by dividing, never by `toFixed`.** `PlanPicker` and\n `CommercialOverview` scale back to major units using the currency's own\n minor-unit exponent, so zero-decimal currencies (JPY, KRW) are not divided.\n- **Migrating an existing database**: `preflightSubscriptionsMoneyMinorUnits(db)`\n reports which columns still hold major units and which rows would be rounded\n or exceed JavaScript's safe-integer range; `migrateSubscriptionsMoneyToMinorUnits(db)` converts them\n (idempotent via `_smrt_backfills`). On SQLite the values are rescaled but the\n declared type needs the table-rebuild path (#2370). Existing\n `PricingRule.terms` are **not** migrated automatically — only the operator\n knows which keys in a given rule are prices rather than quantities or ratios.\n- **Range and existing deployments**: fresh PostgreSQL/DuckDB INTEGER columns\n are BIGINT, and hydration rejects values outside JavaScript's safe-integer\n range. Existing PostgreSQL `int4` columns require the explicit widening in #2424.\n\n## Reseller billing (#3059)\n\n- Billing ownership is consumed from `smrt-tenancy`'s\n `BillingRelationshipService` (`BillingRelationshipReader`); never re-derive\n parentage or owners here.\n- `ClientCharge` stays one row per usage event (`usage_event_id` unique) and\n `tenantId` is always the payer. That uniqueness is the double-billing guard\n across owner changes; do not widen its conflict key. The retail leg lives in\n `RetailCharge` (also one per event), written in the same transaction.\n- Price-book rules are `PricingRule` rows with `priceBookId`; `price()` must\n keep ignoring them, and `rateUsage()` only trusts rules whose tenant owns the\n book.\n- Delegated `SpendingPolicy` rows (`setByTenantId`) are guarded in\n `validateBeforeSave` and a `beforeDelete` hook, by id and by conflict key;\n `CreditGrant` applies the same parent-only rule to delegated balances.\n `PriceBookAssignmentCollection` is deliberately not a root export.\n- `ResellerBillingService` authorizes first, then writes in a system context\n (parent writes land on child-owned rows). The wholesale leg is authorized by\n the book's publisher and may never be published by the payer; whether a\n publisher is a legitimate provider for the reseller is a host obligation\n (the model records no upstream seller). Relationship reads precede\n authorization and stay under smrt-tenancy's own read rules. The only\n cross-tenant read in evaluation is the bounded `wholesale`-basis sum.\n- `balance` policies take their limit from `CreditGrant`s; `limitAmount` must\n be 0, and currency and period are fixed once saved (the ledger is scoped to\n them). Auto top-up is a host hook only — no payment provider calls. The\n evaluator re-reads credit after the hook returns (grant or not) and hands\n it `currentShortfall()`, so a hook that charges asynchronously can see a\n concurrent top-up (smrt-commerce's `autoTopUpHook()`, #3139).\n\n## Notes\n\n- Keep Stripe-specific fields as provider binding metadata. Runtime Stripe API\n calls belong in the HappyVertical SDK accounting provider.\n- Thresholds are evaluated from tenant usage metrics and optional AI usage\n summaries. Resolve multiple thresholds with the batch usage path where\n available; do not bypass tenant context in application code.\n- Use `SubscriptionResolver.create()` or an explicitly injected\n `SubscriptionResolver` once per request when resolving entitlements multiple\n times. Load and pass `EntitlementResolutionContext` to avoid re-reading the\n current subscription and plan.\n- Subscription UI components should stay provider-neutral and receive actions\n from the host app.\n",
3893
+ "browser": {
3894
+ "status": "server-only",
3895
+ "issues": [
3896
+ "#3624",
3897
+ "#3626"
3898
+ ],
3899
+ "reason": "core migrations (node:crypto, node:os)",
3900
+ "via": [
3901
+ "@happyvertical/smrt-tenancy"
3902
+ ]
3903
+ }
3893
3904
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-subscriptions",
3
- "version": "0.55.7",
3
+ "version": "0.55.9",
4
4
  "description": "Tenant subscriptions, entitlement resolution, usage thresholds, and subscription UI for SMRT",
5
5
  "type": "module",
6
6
  "smrtRawPrimitives": "strict",
@@ -26,10 +26,10 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@happyvertical/smrt-core": "0.55.7",
30
- "@happyvertical/smrt-tenancy": "0.55.7",
31
- "@happyvertical/smrt-ui": "0.55.7",
32
- "@happyvertical/sql": "^0.102.5"
29
+ "@happyvertical/smrt-core": "0.55.9",
30
+ "@happyvertical/smrt-tenancy": "0.55.9",
31
+ "@happyvertical/smrt-ui": "0.55.9",
32
+ "@happyvertical/sql": "^0.103.1"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "svelte": "^5.57.0"
@@ -40,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
- "@happyvertical/smrt-vitest": "0.55.7",
43
+ "@happyvertical/smrt-vitest": "0.55.9",
44
44
  "@sveltejs/package": "^2.5.8",
45
45
  "@types/node": "24.13.6",
46
46
  "svelte": "^5.57.1",