@happyvertical/smrt-subscriptions 0.30.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/AGENTS.md +26 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/collections/SubscriptionPlanCollection.d.ts +9 -0
- package/dist/collections/SubscriptionPlanCollection.d.ts.map +1 -0
- package/dist/collections/TenantSubscriptionCollection.d.ts +29 -0
- package/dist/collections/TenantSubscriptionCollection.d.ts.map +1 -0
- package/dist/collections/TenantUsageMetricCollection.d.ts +28 -0
- package/dist/collections/TenantUsageMetricCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +4 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1271 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +1291 -0
- package/dist/models/SubscriptionPlan.d.ts +30 -0
- package/dist/models/SubscriptionPlan.d.ts.map +1 -0
- package/dist/models/TenantSubscription.d.ts +59 -0
- package/dist/models/TenantSubscription.d.ts.map +1 -0
- package/dist/models/TenantUsageMetric.d.ts +35 -0
- package/dist/models/TenantUsageMetric.d.ts.map +1 -0
- package/dist/models/index.d.ts +4 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/subscription-resolver.d.ts +96 -0
- package/dist/services/subscription-resolver.d.ts.map +1 -0
- package/dist/services/threshold-evaluator.d.ts +4 -0
- package/dist/services/threshold-evaluator.d.ts.map +1 -0
- package/dist/services/usage-meter.d.ts +32 -0
- package/dist/services/usage-meter.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +883 -0
- package/dist/svelte/PlanPicker.svelte +82 -0
- package/dist/svelte/PlanPicker.svelte.d.ts +10 -0
- package/dist/svelte/PlanPicker.svelte.d.ts.map +1 -0
- package/dist/svelte/SubscriptionSummary.svelte +65 -0
- package/dist/svelte/SubscriptionSummary.svelte.d.ts +8 -0
- package/dist/svelte/SubscriptionSummary.svelte.d.ts.map +1 -0
- package/dist/svelte/UsageThresholds.svelte +71 -0
- package/dist/svelte/UsageThresholds.svelte.d.ts +8 -0
- package/dist/svelte/UsageThresholds.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +5 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +9 -0
- package/dist/svelte/index.d.ts +4 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +3 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +59 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +80 -0
- package/scripts/migrate-1454-drop-legacy-conflict-index.ts +110 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Migration script for smrt#1454: polymorphic subscriber conflict-key drift.
|
|
5
|
+
*
|
|
6
|
+
* Drops the legacy single-column unique index
|
|
7
|
+
* `_smrt_tenant_subscriptions_tenant_id_idx` left behind on databases that
|
|
8
|
+
* were created against the pre-polymorphic conflictColumns: ['tenant_id'].
|
|
9
|
+
*
|
|
10
|
+
* Why this exists: `smrt db:migrate` (without `--drop-indexes`) does not
|
|
11
|
+
* sweep orphan indexes by design, so the new three-column unique index for
|
|
12
|
+
* `(tenant_id, subscriber_kind, subscriber_external_id)` lands alongside the
|
|
13
|
+
* old single-column one. The OLD index still enforces "one subscription per
|
|
14
|
+
* tenant" and rejects external subscribers — defeating the whole point of
|
|
15
|
+
* the polymorphic feature on upgraded databases.
|
|
16
|
+
*
|
|
17
|
+
* Fresh databases never see this script: their schema is generated from the
|
|
18
|
+
* post-#1454 manifest directly.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* npx tsx node_modules/@happyvertical/smrt-subscriptions/scripts/migrate-1454-drop-legacy-conflict-index.ts <database-url>
|
|
22
|
+
*
|
|
23
|
+
* Examples:
|
|
24
|
+
* # SQLite
|
|
25
|
+
* npx tsx ...migrate-1454-drop-legacy-conflict-index.ts ./data/app.db
|
|
26
|
+
*
|
|
27
|
+
* # Postgres
|
|
28
|
+
* npx tsx ...migrate-1454-drop-legacy-conflict-index.ts postgres://user:pass@host/db
|
|
29
|
+
*
|
|
30
|
+
* Idempotent: re-running is a no-op (`DROP INDEX IF EXISTS`).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { getDatabase } from '@happyvertical/sql';
|
|
34
|
+
|
|
35
|
+
const LEGACY_INDEX_NAME = '_smrt_tenant_subscriptions_tenant_id_idx';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect the database engine from the URL / path. Supports the engines SMRT
|
|
39
|
+
* itself supports — Postgres, DuckDB, SQLite — with SQLite as the last-resort
|
|
40
|
+
* fallback. DuckDB is detected only so we can refuse it with a helpful error
|
|
41
|
+
* (see {@link main}); this script cannot safely rewrite DuckDB schemas.
|
|
42
|
+
*/
|
|
43
|
+
function detectDatabaseType(input: string): 'postgres' | 'duckdb' | 'sqlite' {
|
|
44
|
+
if (
|
|
45
|
+
input.startsWith('postgres://') ||
|
|
46
|
+
input.startsWith('postgresql://') ||
|
|
47
|
+
input.startsWith('postgres:') ||
|
|
48
|
+
input.startsWith('postgresql:')
|
|
49
|
+
) {
|
|
50
|
+
return 'postgres';
|
|
51
|
+
}
|
|
52
|
+
if (input.startsWith('duckdb://') || /\.duckdb($|\?)/i.test(input)) {
|
|
53
|
+
return 'duckdb';
|
|
54
|
+
}
|
|
55
|
+
return 'sqlite';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function main(): Promise<void> {
|
|
59
|
+
const databaseUrl = process.argv[2];
|
|
60
|
+
if (!databaseUrl) {
|
|
61
|
+
process.stderr.write(
|
|
62
|
+
'Usage: migrate-1454-drop-legacy-conflict-index.ts <database-url>\n',
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const type = detectDatabaseType(databaseUrl);
|
|
68
|
+
|
|
69
|
+
// DuckDB stores UNIQUE constraints as inline `UNIQUE(...)` table
|
|
70
|
+
// constraints rather than as named indexes, so `DROP INDEX IF EXISTS` is a
|
|
71
|
+
// no-op against DuckDB and the legacy single-column uniqueness on
|
|
72
|
+
// `tenant_id` would persist after this script "succeeds". Fixing it
|
|
73
|
+
// safely needs a full table rebuild (CREATE new, copy, DROP old, RENAME)
|
|
74
|
+
// that is too risky to bundle here as a one-shot script. Refuse loudly
|
|
75
|
+
// with the manual procedure rather than pretending to migrate.
|
|
76
|
+
if (type === 'duckdb') {
|
|
77
|
+
process.stderr.write(
|
|
78
|
+
'DuckDB databases cannot be migrated automatically with this script.\n' +
|
|
79
|
+
'DuckDB stores the legacy `UNIQUE(tenant_id)` as an inline table constraint,\n' +
|
|
80
|
+
'not as a named index, so DROP INDEX would silently do nothing. The safe\n' +
|
|
81
|
+
'manual procedure is to rebuild the `_smrt_tenant_subscriptions` table:\n\n' +
|
|
82
|
+
' 1. Create a temp copy with the new conflict constraint\n' +
|
|
83
|
+
' UNIQUE (tenant_id, subscriber_kind, subscriber_external_id)\n' +
|
|
84
|
+
' 2. INSERT INTO temp SELECT ... FROM _smrt_tenant_subscriptions\n' +
|
|
85
|
+
' 3. DROP TABLE _smrt_tenant_subscriptions\n' +
|
|
86
|
+
' 4. ALTER TABLE temp RENAME TO _smrt_tenant_subscriptions\n\n' +
|
|
87
|
+
'Run inside a transaction; back up first.\n',
|
|
88
|
+
);
|
|
89
|
+
process.exit(2);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const db = await getDatabase({ type, url: databaseUrl });
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
process.stdout.write(
|
|
96
|
+
`Connecting via ${type} adapter. Dropping legacy index ${LEGACY_INDEX_NAME} (if present)...\n`,
|
|
97
|
+
);
|
|
98
|
+
await db.query(`DROP INDEX IF EXISTS "${LEGACY_INDEX_NAME}"`);
|
|
99
|
+
process.stdout.write('Done.\n');
|
|
100
|
+
} finally {
|
|
101
|
+
await db.close?.();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main().catch((error) => {
|
|
106
|
+
process.stderr.write(
|
|
107
|
+
`${error instanceof Error ? error.stack || error.message : String(error)}\n`,
|
|
108
|
+
);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
});
|