@intelligo-dev/core 1.0.0-beta.1 → 1.0.0-beta.13
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/NOTICE +6 -0
- package/README.md +102 -0
- package/dist/attachments/errors.d.ts +7 -0
- package/dist/attachments/errors.d.ts.map +1 -0
- package/dist/attachments/errors.js +11 -0
- package/dist/attachments/errors.js.map +1 -0
- package/dist/attachments/index.d.ts +8 -0
- package/dist/attachments/index.d.ts.map +1 -0
- package/dist/attachments/index.js +7 -0
- package/dist/attachments/index.js.map +1 -0
- package/dist/attachments/service.d.ts +45 -0
- package/dist/attachments/service.d.ts.map +1 -0
- package/dist/attachments/service.js +100 -0
- package/dist/attachments/service.js.map +1 -0
- package/dist/attachments/types.d.ts +6 -0
- package/dist/attachments/types.d.ts.map +1 -0
- package/dist/attachments/types.js +2 -0
- package/dist/attachments/types.js.map +1 -0
- package/dist/conversations/errors.d.ts +2 -8
- package/dist/conversations/errors.d.ts.map +1 -1
- package/dist/conversations/errors.js +2 -8
- package/dist/conversations/errors.js.map +1 -1
- package/dist/conversations/index.d.ts +4 -12
- package/dist/conversations/index.d.ts.map +1 -1
- package/dist/conversations/index.js +2 -11
- package/dist/conversations/index.js.map +1 -1
- package/dist/conversations/service.d.ts +49 -61
- package/dist/conversations/service.d.ts.map +1 -1
- package/dist/conversations/service.js +110 -83
- package/dist/conversations/service.js.map +1 -1
- package/dist/conversations/types.d.ts +1 -4
- package/dist/conversations/types.d.ts.map +1 -1
- package/dist/conversations/types.js +0 -3
- package/dist/conversations/types.js.map +1 -1
- package/dist/db/client.d.ts +21 -10
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +43 -18
- package/dist/db/client.js.map +1 -1
- package/dist/db/index.d.ts +3 -3
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +3 -7
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema/agents.d.ts +14 -51
- package/dist/db/schema/agents.d.ts.map +1 -1
- package/dist/db/schema/agents.js +9 -21
- package/dist/db/schema/agents.js.map +1 -1
- package/dist/db/schema/ai.d.ts +86 -1556
- package/dist/db/schema/ai.d.ts.map +1 -1
- package/dist/db/schema/ai.js +32 -236
- package/dist/db/schema/ai.js.map +1 -1
- package/dist/db/schema/auth.d.ts +5 -34
- package/dist/db/schema/auth.d.ts.map +1 -1
- package/dist/db/schema/auth.js +25 -56
- package/dist/db/schema/auth.js.map +1 -1
- package/dist/db/schema/billing.d.ts +136 -107
- package/dist/db/schema/billing.d.ts.map +1 -1
- package/dist/db/schema/billing.js +72 -60
- package/dist/db/schema/billing.js.map +1 -1
- package/dist/db/schema/feature-flags.d.ts +3 -12
- package/dist/db/schema/feature-flags.d.ts.map +1 -1
- package/dist/db/schema/feature-flags.js +3 -12
- package/dist/db/schema/feature-flags.js.map +1 -1
- package/dist/db/schema/identity.d.ts +12 -274
- package/dist/db/schema/identity.d.ts.map +1 -1
- package/dist/db/schema/identity.js +19 -104
- package/dist/db/schema/identity.js.map +1 -1
- package/dist/db/schema/index.d.ts +8 -15
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/index.js +12 -42
- package/dist/db/schema/index.js.map +1 -1
- package/dist/db/schema/notifications.d.ts +3 -17
- package/dist/db/schema/notifications.d.ts.map +1 -1
- package/dist/db/schema/notifications.js +3 -17
- package/dist/db/schema/notifications.js.map +1 -1
- package/dist/db/schema/usage.d.ts +94 -77
- package/dist/db/schema/usage.d.ts.map +1 -1
- package/dist/db/schema/usage.js +59 -89
- package/dist/db/schema/usage.js.map +1 -1
- package/dist/db/validate.d.ts +2 -11
- package/dist/db/validate.d.ts.map +1 -1
- package/dist/db/validate.js +6 -17
- package/dist/db/validate.js.map +1 -1
- package/dist/db/workspace-queries.d.ts +5 -29
- package/dist/db/workspace-queries.d.ts.map +1 -1
- package/dist/db/workspace-queries.js +5 -29
- package/dist/db/workspace-queries.js.map +1 -1
- package/dist/documents/classifier.d.ts +5 -12
- package/dist/documents/classifier.d.ts.map +1 -1
- package/dist/documents/classifier.js +16 -16
- package/dist/documents/classifier.js.map +1 -1
- package/dist/documents/errors.d.ts +2 -10
- package/dist/documents/errors.d.ts.map +1 -1
- package/dist/documents/errors.js +2 -10
- package/dist/documents/errors.js.map +1 -1
- package/dist/documents/index.d.ts +4 -13
- package/dist/documents/index.d.ts.map +1 -1
- package/dist/documents/index.js +3 -12
- package/dist/documents/index.js.map +1 -1
- package/dist/documents/service.d.ts +19 -26
- package/dist/documents/service.d.ts.map +1 -1
- package/dist/documents/service.js +44 -38
- package/dist/documents/service.js.map +1 -1
- package/dist/documents/types.d.ts +6 -10
- package/dist/documents/types.d.ts.map +1 -1
- package/dist/documents/types.js +0 -3
- package/dist/documents/types.js.map +1 -1
- package/dist/email/brand.d.ts +17 -0
- package/dist/email/brand.d.ts.map +1 -0
- package/dist/email/brand.js +9 -0
- package/dist/email/brand.js.map +1 -0
- package/dist/email/index.d.ts +4 -4
- package/dist/email/index.js +4 -4
- package/dist/email/provider.d.ts +1 -7
- package/dist/email/provider.d.ts.map +1 -1
- package/dist/email/provider.js +10 -19
- package/dist/email/provider.js.map +1 -1
- package/dist/email/send.d.ts +12 -12
- package/dist/email/send.d.ts.map +1 -1
- package/dist/email/send.js +5 -19
- package/dist/email/send.js.map +1 -1
- package/dist/email/senders.d.ts +17 -30
- package/dist/email/senders.d.ts.map +1 -1
- package/dist/email/senders.js +33 -47
- package/dist/email/senders.js.map +1 -1
- package/dist/email/templates/base-layout.d.ts +1 -1
- package/dist/email/templates/base-layout.d.ts.map +1 -1
- package/dist/email/templates/base-layout.js +4 -6
- package/dist/email/templates/base-layout.js.map +1 -1
- package/dist/email/templates/index.d.ts +10 -10
- package/dist/email/templates/index.d.ts.map +1 -1
- package/dist/email/templates/index.js +10 -16
- package/dist/email/templates/index.js.map +1 -1
- package/dist/email/templates/password-reset.d.ts +2 -2
- package/dist/email/templates/password-reset.d.ts.map +1 -1
- package/dist/email/templates/password-reset.js +10 -22
- package/dist/email/templates/password-reset.js.map +1 -1
- package/dist/email/templates/payment-failed.d.ts +2 -2
- package/dist/email/templates/payment-failed.d.ts.map +1 -1
- package/dist/email/templates/payment-failed.js +11 -24
- package/dist/email/templates/payment-failed.js.map +1 -1
- package/dist/email/templates/quota-warning.d.ts +4 -4
- package/dist/email/templates/quota-warning.d.ts.map +1 -1
- package/dist/email/templates/quota-warning.js +19 -39
- package/dist/email/templates/quota-warning.js.map +1 -1
- package/dist/email/templates/subscription-confirmed.d.ts +2 -2
- package/dist/email/templates/subscription-confirmed.d.ts.map +1 -1
- package/dist/email/templates/subscription-confirmed.js +13 -28
- package/dist/email/templates/subscription-confirmed.js.map +1 -1
- package/dist/email/templates/trial-expiry.d.ts +2 -2
- package/dist/email/templates/trial-expiry.d.ts.map +1 -1
- package/dist/email/templates/trial-expiry.js +12 -36
- package/dist/email/templates/trial-expiry.js.map +1 -1
- package/dist/email/templates/trial-warning.d.ts +2 -2
- package/dist/email/templates/trial-warning.d.ts.map +1 -1
- package/dist/email/templates/trial-warning.js +15 -32
- package/dist/email/templates/trial-warning.js.map +1 -1
- package/dist/email/templates/verify-email.d.ts +2 -2
- package/dist/email/templates/verify-email.d.ts.map +1 -1
- package/dist/email/templates/verify-email.js +10 -22
- package/dist/email/templates/verify-email.js.map +1 -1
- package/dist/email/templates/welcome.d.ts +2 -2
- package/dist/email/templates/welcome.d.ts.map +1 -1
- package/dist/email/templates/welcome.js +13 -20
- package/dist/email/templates/welcome.js.map +1 -1
- package/dist/email/templates/workspace-invitation.d.ts +2 -2
- package/dist/email/templates/workspace-invitation.d.ts.map +1 -1
- package/dist/email/templates/workspace-invitation.js +12 -26
- package/dist/email/templates/workspace-invitation.js.map +1 -1
- package/dist/env.d.ts +5 -10
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +41 -31
- package/dist/env.js.map +1 -1
- package/dist/identity/audit.d.ts +11 -24
- package/dist/identity/audit.d.ts.map +1 -1
- package/dist/identity/audit.js +13 -27
- package/dist/identity/audit.js.map +1 -1
- package/dist/identity/errors.d.ts +4 -12
- package/dist/identity/errors.d.ts.map +1 -1
- package/dist/identity/errors.js +2 -8
- package/dist/identity/errors.js.map +1 -1
- package/dist/identity/index.d.ts +5 -13
- package/dist/identity/index.d.ts.map +1 -1
- package/dist/identity/index.js +4 -12
- package/dist/identity/index.js.map +1 -1
- package/dist/identity/service.d.ts +13 -47
- package/dist/identity/service.d.ts.map +1 -1
- package/dist/identity/service.js +31 -78
- package/dist/identity/service.js.map +1 -1
- package/dist/identity/types.d.ts +1 -4
- package/dist/identity/types.d.ts.map +1 -1
- package/dist/identity/types.js +0 -3
- package/dist/identity/types.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -10
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +7 -18
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +45 -49
- package/dist/logger.js.map +1 -1
- package/dist/money.d.ts +95 -0
- package/dist/money.d.ts.map +1 -0
- package/dist/money.js +208 -0
- package/dist/money.js.map +1 -0
- package/dist/notifications/index.d.ts +9 -43
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +9 -49
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/triggers.d.ts +9 -32
- package/dist/notifications/triggers.d.ts.map +1 -1
- package/dist/notifications/triggers.js +21 -58
- package/dist/notifications/triggers.js.map +1 -1
- package/dist/notifications/types.d.ts +1 -10
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/notifications/types.js +0 -9
- package/dist/notifications/types.js.map +1 -1
- package/dist/prompt.d.ts +38 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +118 -0
- package/dist/prompt.js.map +1 -0
- package/dist/registry.d.ts +32 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +60 -0
- package/dist/registry.js.map +1 -0
- package/dist/request-context.d.ts +49 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +100 -0
- package/dist/request-context.js.map +1 -0
- package/dist/storage/index.d.ts +65 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +109 -0
- package/dist/storage/index.js.map +1 -0
- package/package.json +74 -24
- package/src/attachments/errors.ts +18 -0
- package/src/attachments/index.ts +22 -0
- package/src/attachments/service.ts +178 -0
- package/src/attachments/types.ts +6 -0
- package/src/conversations/errors.ts +45 -0
- package/src/conversations/index.ts +39 -0
- package/src/conversations/service.ts +594 -0
- package/src/conversations/types.ts +15 -0
- package/src/db/client.ts +91 -0
- package/src/db/index.ts +3 -0
- package/src/db/migrations/0000_baseline.sql +652 -0
- package/src/db/migrations/0001_reconcile_chain_built_databases.sql +53 -0
- package/src/db/migrations/0002_webhook_claim_lease.sql +1 -0
- package/src/db/migrations/README.md +74 -36
- package/src/db/migrations/legacy-chain.json +197 -0
- package/src/db/migrations/meta/0000_snapshot.json +4182 -157
- package/src/db/migrations/meta/0002_snapshot.json +2478 -368
- package/src/db/migrations/meta/_journal.json +6 -279
- package/src/db/schema/agents.ts +57 -0
- package/src/db/schema/ai.ts +174 -0
- package/src/db/schema/auth.ts +183 -0
- package/src/db/schema/billing.ts +209 -0
- package/src/db/schema/feature-flags.ts +20 -0
- package/src/db/schema/identity.ts +250 -0
- package/src/db/schema/index.ts +12 -0
- package/src/db/schema/notifications.ts +31 -0
- package/src/db/schema/usage.ts +269 -0
- package/src/db/validate.ts +63 -0
- package/src/db/workspace-queries.ts +64 -0
- package/src/documents/classifier.ts +61 -0
- package/src/documents/errors.ts +44 -0
- package/src/documents/index.ts +24 -0
- package/src/documents/service.ts +256 -0
- package/src/documents/types.ts +24 -0
- package/src/email/brand.ts +24 -0
- package/src/email/index.ts +19 -0
- package/src/email/provider.ts +278 -0
- package/src/email/send.ts +169 -0
- package/src/email/senders.ts +292 -0
- package/src/email/templates/base-layout.tsx +145 -0
- package/src/email/templates/index.ts +28 -0
- package/src/email/templates/password-reset.tsx +72 -0
- package/src/email/templates/payment-failed.tsx +77 -0
- package/src/email/templates/quota-warning.tsx +118 -0
- package/src/email/templates/subscription-confirmed.tsx +91 -0
- package/src/email/templates/trial-expiry.tsx +124 -0
- package/src/email/templates/trial-warning.tsx +104 -0
- package/src/email/templates/verify-email.tsx +72 -0
- package/src/email/templates/welcome.tsx +64 -0
- package/src/email/templates/workspace-invitation.tsx +105 -0
- package/src/env.ts +180 -0
- package/src/identity/audit.ts +65 -0
- package/src/identity/errors.ts +44 -0
- package/src/identity/index.ts +26 -0
- package/src/identity/service.ts +202 -0
- package/src/identity/types.ts +28 -0
- package/src/index.ts +4 -0
- package/src/logger.ts +198 -0
- package/src/money.ts +270 -0
- package/src/notifications/index.ts +108 -0
- package/src/notifications/triggers.ts +158 -0
- package/src/notifications/types.ts +18 -0
- package/src/prompt.ts +142 -0
- package/src/registry.ts +75 -0
- package/src/request-context.ts +137 -0
- package/src/storage/index.ts +153 -0
- package/dist/db/schema/rag.d.ts +0 -171
- package/dist/db/schema/rag.d.ts.map +0 -1
- package/dist/db/schema/rag.js +0 -42
- package/dist/db/schema/rag.js.map +0 -1
- package/src/db/migrations/0000_sharp_night_nurse.sql +0 -52
- package/src/db/migrations/0001_hesitant_vapor.sql +0 -40
- package/src/db/migrations/0002_numerous_lady_vermin.sql +0 -235
- package/src/db/migrations/0003_mighty_jean_grey.sql +0 -37
- package/src/db/migrations/0004_cynical_grandmaster.sql +0 -2
- package/src/db/migrations/0005_careful_switch.sql +0 -8
- package/src/db/migrations/0006_tense_blue_marvel.sql +0 -16
- package/src/db/migrations/0007_next_squadron_sinister.sql +0 -1
- package/src/db/migrations/0008_powerful_scream.sql +0 -39
- package/src/db/migrations/0009_sad_ink.sql +0 -1
- package/src/db/migrations/0010_shocking_omega_flight.sql +0 -2
- package/src/db/migrations/0011_ambiguous_otto_octavius.sql +0 -151
- package/src/db/migrations/0011_hnsw_vector_index.sql +0 -17
- package/src/db/migrations/0012_user_profiles.sql +0 -30
- package/src/db/migrations/0013_trending_seasonal_columns.sql +0 -9
- package/src/db/migrations/0014_add_product_type_to_agents.sql +0 -6
- package/src/db/migrations/0015_new_v1_tables.sql +0 -149
- package/src/db/migrations/0016_document_types.sql +0 -30
- package/src/db/migrations/0017_document_metadata.sql +0 -2
- package/src/db/migrations/0018_identity_graph.sql +0 -200
- package/src/db/migrations/0019_db_driven_prompts.sql +0 -20
- package/src/db/migrations/0020_pending_extractions.sql +0 -35
- package/src/db/migrations/0021_rag_documents.sql +0 -39
- package/src/db/migrations/0022_user_quotas_usage_jsonb.sql +0 -70
- package/src/db/migrations/0023_user_facts_user_scoped_unique.sql +0 -17
- package/src/db/migrations/0024_user_memories_hnsw.sql +0 -23
- package/src/db/migrations/0025_audit_trigger_errcode.sql +0 -21
- package/src/db/migrations/0026_user_profile_snapshots_composite_pk.sql +0 -17
- package/src/db/migrations/0027_billing_settings_mnt_columns.sql +0 -54
- package/src/db/migrations/0028_trial_credits_normalized_email.sql +0 -37
- package/src/db/migrations/0029_usage_records_conversation_idx.sql +0 -13
- package/src/db/migrations/0030_pending_extractions_composite_idx.sql +0 -11
- package/src/db/migrations/0031_rag_documents_hnsw.sql +0 -16
- package/src/db/migrations/0032_rate_limit_unique_idx.sql +0 -16
- package/src/db/migrations/0033_pending_extractions_partial_idx.sql +0 -18
- package/src/db/migrations/0034_rate_limit_count.sql +0 -9
- package/src/db/migrations/0035_credit_reservations.sql +0 -24
- package/src/db/migrations/0036_executions_audit_jobs.sql +0 -96
- package/src/db/migrations/0037_rate_limit_minute_bucket.sql +0 -46
- package/src/db/migrations/0038_user_quotas_generic_usage.sql +0 -25
- package/src/db/migrations/0039_platform_admin_impersonation.sql +0 -27
- package/src/db/migrations/0040_ee_audit_chain.sql +0 -23
- package/src/db/migrations/meta/0001_snapshot.json +0 -631
- package/src/db/migrations/meta/0003_snapshot.json +0 -2488
- package/src/db/migrations/meta/0004_snapshot.json +0 -2500
- package/src/db/migrations/meta/0005_snapshot.json +0 -2548
- package/src/db/migrations/meta/0006_snapshot.json +0 -2650
- package/src/db/migrations/meta/0007_snapshot.json +0 -2657
- package/src/db/migrations/meta/0008_snapshot.json +0 -2719
- package/src/db/migrations/meta/0009_snapshot.json +0 -2726
- package/src/db/migrations/meta/0010_snapshot.json +0 -2739
- package/src/db/migrations/meta/0011_snapshot.json +0 -3886
package/dist/money.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money, in one unit, with the currency attached. Imports nothing:
|
|
3
|
+
* `@intelligo-dev/executions/pricing` is a zero-import leaf that client
|
|
4
|
+
* bundles reach, and it needs these types.
|
|
5
|
+
*
|
|
6
|
+
* **Micros, not minor units.** A chat turn on a cheap model costs a
|
|
7
|
+
* fraction of a cent; rounding it to whole cents overcharges by more than a
|
|
8
|
+
* third on every request.
|
|
9
|
+
*
|
|
10
|
+
* **Currency on the amount, not in a global.** A bare `number` lets one
|
|
11
|
+
* unit be sold as another; here the currency travels with the amount and
|
|
12
|
+
* `add` refuses to mix two.
|
|
13
|
+
*/
|
|
14
|
+
/** Thrown for every misuse in this module, so callers can catch one type. */
|
|
15
|
+
export class MoneyError extends Error {
|
|
16
|
+
constructor(code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.name = "MoneyError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const MICROS_PER_UNIT = 1000000;
|
|
23
|
+
/**
|
|
24
|
+
* Currencies whose minor unit is not the usual hundredth; everything else
|
|
25
|
+
* is 2. Getting this wrong charges ¥100,000 instead of ¥1,000, because
|
|
26
|
+
* Stripe takes zero-decimal currencies in whole units.
|
|
27
|
+
*/
|
|
28
|
+
const MINOR_EXPONENT = {
|
|
29
|
+
BIF: 0,
|
|
30
|
+
CLP: 0,
|
|
31
|
+
DJF: 0,
|
|
32
|
+
GNF: 0,
|
|
33
|
+
ISK: 0,
|
|
34
|
+
JPY: 0,
|
|
35
|
+
KMF: 0,
|
|
36
|
+
KRW: 0,
|
|
37
|
+
MGA: 0,
|
|
38
|
+
MNT: 0,
|
|
39
|
+
PYG: 0,
|
|
40
|
+
RWF: 0,
|
|
41
|
+
UGX: 0,
|
|
42
|
+
UYI: 0,
|
|
43
|
+
VND: 0,
|
|
44
|
+
VUV: 0,
|
|
45
|
+
XAF: 0,
|
|
46
|
+
XOF: 0,
|
|
47
|
+
XPF: 0,
|
|
48
|
+
BHD: 3,
|
|
49
|
+
IQD: 3,
|
|
50
|
+
JOD: 3,
|
|
51
|
+
KWD: 3,
|
|
52
|
+
LYD: 3,
|
|
53
|
+
OMR: 3,
|
|
54
|
+
TND: 3,
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Validates and normalises an ISO-4217 code. Shape only: a list would go
|
|
58
|
+
* stale and block a deployment selling in a code it does not know.
|
|
59
|
+
* `minorExponent` gives an unknown code its default.
|
|
60
|
+
*/
|
|
61
|
+
export function currency(code) {
|
|
62
|
+
const upper = code.toUpperCase();
|
|
63
|
+
if (!/^[A-Z]{3}$/.test(upper)) {
|
|
64
|
+
throw new MoneyError("invalid_currency", `"${code}" is not an ISO-4217 alphabetic code (three letters).`);
|
|
65
|
+
}
|
|
66
|
+
return upper;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* An integer count of micros, up to `Number.MAX_SAFE_INTEGER` (about 9
|
|
70
|
+
* billion major units) — low enough to catch a value in the wrong unit.
|
|
71
|
+
*/
|
|
72
|
+
export function micros(value) {
|
|
73
|
+
if (!Number.isInteger(value)) {
|
|
74
|
+
throw new MoneyError("not_an_integer", `Micros must be a whole number; received ${value}. Convert with fromMajor/fromMinor rather than multiplying by hand.`);
|
|
75
|
+
}
|
|
76
|
+
if (!Number.isSafeInteger(value)) {
|
|
77
|
+
throw new MoneyError("out_of_range", `${value} micros exceeds the safe integer range.`);
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
export function money(amount, code) {
|
|
82
|
+
return { amount: micros(amount), currency: currency(code) };
|
|
83
|
+
}
|
|
84
|
+
/** How many decimal places this currency's minor unit has. */
|
|
85
|
+
export function minorExponent(code) {
|
|
86
|
+
return MINOR_EXPONENT[code] ?? 2;
|
|
87
|
+
}
|
|
88
|
+
/** `12.34` USD → `12_340_000` micros. */
|
|
89
|
+
export function fromMajor(value, code) {
|
|
90
|
+
return {
|
|
91
|
+
amount: micros(Math.round(value * MICROS_PER_UNIT)),
|
|
92
|
+
currency: currency(code),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** `1234` cents → `12_340_000` micros. */
|
|
96
|
+
export function fromMinor(value, code) {
|
|
97
|
+
const c = currency(code);
|
|
98
|
+
const scale = MICROS_PER_UNIT / 10 ** minorExponent(c);
|
|
99
|
+
return { amount: micros(Math.round(value * scale)), currency: c };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The integer a payment provider wants: whole units for a zero-decimal
|
|
103
|
+
* currency, hundredths for most, thousandths for a few.
|
|
104
|
+
*
|
|
105
|
+
* Rounds up. A fraction of a minor unit is money the deployment has
|
|
106
|
+
* already spent, and rounding it away means eating the difference on
|
|
107
|
+
* every transaction.
|
|
108
|
+
*/
|
|
109
|
+
export function toMinor(value) {
|
|
110
|
+
const scale = MICROS_PER_UNIT / 10 ** minorExponent(value.currency);
|
|
111
|
+
return Math.ceil(value.amount / scale);
|
|
112
|
+
}
|
|
113
|
+
/** The amount as a plain decimal number, for display and for charts. */
|
|
114
|
+
export function toMajor(value) {
|
|
115
|
+
return value.amount / MICROS_PER_UNIT;
|
|
116
|
+
}
|
|
117
|
+
export const zero = (code) => ({
|
|
118
|
+
amount: 0,
|
|
119
|
+
currency: currency(code),
|
|
120
|
+
});
|
|
121
|
+
function sameCurrency(a, b, op) {
|
|
122
|
+
if (a.currency !== b.currency) {
|
|
123
|
+
throw new MoneyError("currency_mismatch", `Cannot ${op} ${a.currency} and ${b.currency}. Convert one first — an exchange rate is a decision, not a coercion.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export function add(a, b) {
|
|
127
|
+
sameCurrency(a, b, "add");
|
|
128
|
+
return { amount: micros(a.amount + b.amount), currency: a.currency };
|
|
129
|
+
}
|
|
130
|
+
export function subtract(a, b) {
|
|
131
|
+
sameCurrency(a, b, "subtract");
|
|
132
|
+
return { amount: micros(a.amount - b.amount), currency: a.currency };
|
|
133
|
+
}
|
|
134
|
+
/** Scale by a plain factor — a margin multiplier, a percentage, a count. */
|
|
135
|
+
export function multiply(value, factor) {
|
|
136
|
+
if (!Number.isFinite(factor)) {
|
|
137
|
+
throw new MoneyError("invalid_factor", `Factor must be finite.`);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
amount: micros(Math.ceil(value.amount * factor)),
|
|
141
|
+
currency: value.currency,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export function compare(a, b) {
|
|
145
|
+
sameCurrency(a, b, "compare");
|
|
146
|
+
return a.amount === b.amount ? 0 : a.amount < b.amount ? -1 : 1;
|
|
147
|
+
}
|
|
148
|
+
export const isZero = (value) => value.amount === 0;
|
|
149
|
+
export const isNegative = (value) => value.amount < 0;
|
|
150
|
+
/**
|
|
151
|
+
* Convert at an explicit rate, expressed in micros so the rate itself
|
|
152
|
+
* carries no float error: `3_450_000_000` is 3450.0.
|
|
153
|
+
*
|
|
154
|
+
* There is no ambient rate and no default. A framework that guesses an
|
|
155
|
+
* exchange rate is inventing money.
|
|
156
|
+
*/
|
|
157
|
+
export function convert(value, to, rateMicros) {
|
|
158
|
+
const target = currency(to);
|
|
159
|
+
if (!Number.isInteger(rateMicros) || rateMicros <= 0) {
|
|
160
|
+
throw new MoneyError("invalid_rate", `Rate must be a positive integer number of micros; received ${rateMicros}.`);
|
|
161
|
+
}
|
|
162
|
+
if (target === value.currency) {
|
|
163
|
+
if (rateMicros !== MICROS_PER_UNIT) {
|
|
164
|
+
throw new MoneyError("invalid_rate", `Converting ${value.currency} to itself at ${rateMicros / MICROS_PER_UNIT} is not a conversion.`);
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
amount: micros(Math.ceil((value.amount * rateMicros) / MICROS_PER_UNIT)),
|
|
170
|
+
currency: target,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** Two significant digits is enough to read a sub-cent amount by. */
|
|
174
|
+
const SIGNIFICANT_DIGITS = 2;
|
|
175
|
+
/**
|
|
176
|
+
* How many decimals an amount needs to be legible.
|
|
177
|
+
*
|
|
178
|
+
* The currency's own minor unit, except when that would render a real
|
|
179
|
+
* charge as nothing: one chat turn costs a fraction of a cent, and a
|
|
180
|
+
* usage page that says every request cost `$0.00` is worse than one
|
|
181
|
+
* that says `$0.0048`. Below the minor unit the amount is shown to two
|
|
182
|
+
* significant digits, capped at the six decimals micros can hold.
|
|
183
|
+
*/
|
|
184
|
+
export function displayFractionDigits(value) {
|
|
185
|
+
const digits = minorExponent(value.currency);
|
|
186
|
+
const major = Math.abs(toMajor(value));
|
|
187
|
+
// Zero, and anything that still rounds to a visible minor unit, reads
|
|
188
|
+
// the way the currency is normally written.
|
|
189
|
+
if (major === 0 || major >= 10 ** -digits / 2)
|
|
190
|
+
return digits;
|
|
191
|
+
const leadingZeros = Math.floor(-Math.log10(major));
|
|
192
|
+
return Math.min(6, leadingZeros + SIGNIFICANT_DIGITS);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Formats for a human, in their locale. `Intl` owns the symbol, its
|
|
196
|
+
* position and the separators.
|
|
197
|
+
*/
|
|
198
|
+
export function formatMoney(value, locale, options = {}) {
|
|
199
|
+
const digits = displayFractionDigits(value);
|
|
200
|
+
return new Intl.NumberFormat(locale, {
|
|
201
|
+
style: "currency",
|
|
202
|
+
currency: value.currency,
|
|
203
|
+
minimumFractionDigits: digits,
|
|
204
|
+
maximumFractionDigits: digits,
|
|
205
|
+
...options,
|
|
206
|
+
}).format(toMajor(value));
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* CRUD operations for user notifications. These are used by server actions
|
|
5
|
-
* in the consumer application to power the notification bell, list, and
|
|
6
|
-
* mark-as-read functionality.
|
|
7
|
-
*
|
|
8
|
-
* Notifications are user-scoped (not workspace-scoped) so a user can see
|
|
9
|
-
* all their notifications across workspaces.
|
|
10
|
-
*/
|
|
11
|
-
import type { CreateNotificationParams } from "./types";
|
|
12
|
-
export type { NotificationType, CreateNotificationParams } from "./types";
|
|
13
|
-
export { triggerQuotaNotification, triggerTrialNotification, triggerPaymentFailedNotification, triggerTeamMemberJoinedNotification, } from "./triggers";
|
|
14
|
-
/**
|
|
15
|
-
* Create a new notification for a user
|
|
16
|
-
*
|
|
17
|
-
* Serializes metadata to JSON string if provided.
|
|
18
|
-
* Returns the created notification.
|
|
2
|
+
* User notifications. User-scoped, not workspace-scoped, so a user sees
|
|
3
|
+
* their notifications from every workspace.
|
|
19
4
|
*/
|
|
5
|
+
import type { CreateNotificationParams } from "./types.js";
|
|
6
|
+
export type { NotificationType, CreateNotificationParams } from "./types.js";
|
|
7
|
+
export { triggerQuotaNotification, triggerTrialNotification, triggerPaymentFailedNotification, triggerTeamMemberJoinedNotification, } from "./triggers.js";
|
|
8
|
+
/** Creates a notification; `metadata` is stored as a JSON string. */
|
|
20
9
|
export declare function createNotification(params: CreateNotificationParams): Promise<{
|
|
21
10
|
id: string;
|
|
22
11
|
createdAt: Date;
|
|
@@ -28,12 +17,7 @@ export declare function createNotification(params: CreateNotificationParams): Pr
|
|
|
28
17
|
message: string;
|
|
29
18
|
isRead: boolean;
|
|
30
19
|
} | undefined>;
|
|
31
|
-
/**
|
|
32
|
-
* Get unread notifications for a user
|
|
33
|
-
*
|
|
34
|
-
* Returns notifications where isRead is false, ordered by newest first.
|
|
35
|
-
* Parses metadata JSON string back to object.
|
|
36
|
-
*/
|
|
20
|
+
/** The user's unread notifications, newest first. */
|
|
37
21
|
export declare function getUnreadNotifications(userId: string, limit?: number): Promise<({
|
|
38
22
|
id: string;
|
|
39
23
|
userId: string;
|
|
@@ -47,12 +31,7 @@ export declare function getUnreadNotifications(userId: string, limit?: number):
|
|
|
47
31
|
} & {
|
|
48
32
|
metadata: Record<string, unknown> | null;
|
|
49
33
|
})[]>;
|
|
50
|
-
/**
|
|
51
|
-
* Get all notifications for a user (read and unread)
|
|
52
|
-
*
|
|
53
|
-
* Returns notifications ordered by newest first.
|
|
54
|
-
* Parses metadata JSON string back to object.
|
|
55
|
-
*/
|
|
34
|
+
/** All of the user's notifications, newest first. */
|
|
56
35
|
export declare function getNotifications(userId: string, limit?: number): Promise<({
|
|
57
36
|
id: string;
|
|
58
37
|
userId: string;
|
|
@@ -66,21 +45,8 @@ export declare function getNotifications(userId: string, limit?: number): Promis
|
|
|
66
45
|
} & {
|
|
67
46
|
metadata: Record<string, unknown> | null;
|
|
68
47
|
})[]>;
|
|
69
|
-
/**
|
|
70
|
-
* Get count of unread notifications for a user
|
|
71
|
-
*
|
|
72
|
-
* Used for the notification bell badge number.
|
|
73
|
-
*/
|
|
74
48
|
export declare function getNotificationCount(userId: string): Promise<number>;
|
|
75
|
-
/**
|
|
76
|
-
* Mark a single notification as read
|
|
77
|
-
*
|
|
78
|
-
* Security: requires both notificationId and userId to ensure
|
|
79
|
-
* users can only mark their own notifications.
|
|
80
|
-
*/
|
|
49
|
+
/** Filters on userId as well, so users can only mark their own. */
|
|
81
50
|
export declare function markAsRead(notificationId: string, userId: string): Promise<void>;
|
|
82
|
-
/**
|
|
83
|
-
* Mark all unread notifications as read for a user
|
|
84
|
-
*/
|
|
85
51
|
export declare function markAllAsRead(userId: string): Promise<void>;
|
|
86
52
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/notifications/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/notifications/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAExD,YAAY,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAE1E,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,gCAAgC,EAChC,mCAAmC,GACpC,MAAM,YAAY,CAAC;AAEpB,qEAAqE;AACrE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB;;;;;;;;;;eAcxE;AAED,qDAAqD;AACrD,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAW;;;;;;;;;;;cA6DD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;MAjDhD;AAED,qDAAqD;AACrD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW;;;;;;;;;;;cA8CtD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;MArChD;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS1E;AAED,mEAAmE;AACnE,wBAAsB,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAUtE;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,iBAOjD"}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* CRUD operations for user notifications. These are used by server actions
|
|
5
|
-
* in the consumer application to power the notification bell, list, and
|
|
6
|
-
* mark-as-read functionality.
|
|
7
|
-
*
|
|
8
|
-
* Notifications are user-scoped (not workspace-scoped) so a user can see
|
|
9
|
-
* all their notifications across workspaces.
|
|
2
|
+
* User notifications. User-scoped, not workspace-scoped, so a user sees
|
|
3
|
+
* their notifications from every workspace.
|
|
10
4
|
*/
|
|
11
|
-
import { db } from "../db";
|
|
12
|
-
import { notifications } from "../db/schema";
|
|
5
|
+
import { db } from "../db/index.js";
|
|
6
|
+
import { notifications } from "../db/schema/index.js";
|
|
13
7
|
import { eq, and, desc, count } from "drizzle-orm";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Create a new notification for a user
|
|
18
|
-
*
|
|
19
|
-
* Serializes metadata to JSON string if provided.
|
|
20
|
-
* Returns the created notification.
|
|
21
|
-
*/
|
|
8
|
+
export { triggerQuotaNotification, triggerTrialNotification, triggerPaymentFailedNotification, triggerTeamMemberJoinedNotification, } from "./triggers.js";
|
|
9
|
+
/** Creates a notification; `metadata` is stored as a JSON string. */
|
|
22
10
|
export async function createNotification(params) {
|
|
23
11
|
const [notification] = await db
|
|
24
12
|
.insert(notifications)
|
|
@@ -33,12 +21,7 @@ export async function createNotification(params) {
|
|
|
33
21
|
.returning();
|
|
34
22
|
return notification;
|
|
35
23
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Get unread notifications for a user
|
|
38
|
-
*
|
|
39
|
-
* Returns notifications where isRead is false, ordered by newest first.
|
|
40
|
-
* Parses metadata JSON string back to object.
|
|
41
|
-
*/
|
|
24
|
+
/** The user's unread notifications, newest first. */
|
|
42
25
|
export async function getUnreadNotifications(userId, limit = 20) {
|
|
43
26
|
const rows = await db
|
|
44
27
|
.select()
|
|
@@ -48,12 +31,7 @@ export async function getUnreadNotifications(userId, limit = 20) {
|
|
|
48
31
|
.limit(limit);
|
|
49
32
|
return rows.map(parseMetadata);
|
|
50
33
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Get all notifications for a user (read and unread)
|
|
53
|
-
*
|
|
54
|
-
* Returns notifications ordered by newest first.
|
|
55
|
-
* Parses metadata JSON string back to object.
|
|
56
|
-
*/
|
|
34
|
+
/** All of the user's notifications, newest first. */
|
|
57
35
|
export async function getNotifications(userId, limit = 50) {
|
|
58
36
|
const rows = await db
|
|
59
37
|
.select()
|
|
@@ -63,11 +41,6 @@ export async function getNotifications(userId, limit = 50) {
|
|
|
63
41
|
.limit(limit);
|
|
64
42
|
return rows.map(parseMetadata);
|
|
65
43
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Get count of unread notifications for a user
|
|
68
|
-
*
|
|
69
|
-
* Used for the notification bell badge number.
|
|
70
|
-
*/
|
|
71
44
|
export async function getNotificationCount(userId) {
|
|
72
45
|
const [result] = await db
|
|
73
46
|
.select({ count: count() })
|
|
@@ -75,32 +48,19 @@ export async function getNotificationCount(userId) {
|
|
|
75
48
|
.where(and(eq(notifications.userId, userId), eq(notifications.isRead, false)));
|
|
76
49
|
return result?.count ?? 0;
|
|
77
50
|
}
|
|
78
|
-
/**
|
|
79
|
-
* Mark a single notification as read
|
|
80
|
-
*
|
|
81
|
-
* Security: requires both notificationId and userId to ensure
|
|
82
|
-
* users can only mark their own notifications.
|
|
83
|
-
*/
|
|
51
|
+
/** Filters on userId as well, so users can only mark their own. */
|
|
84
52
|
export async function markAsRead(notificationId, userId) {
|
|
85
53
|
await db
|
|
86
54
|
.update(notifications)
|
|
87
55
|
.set({ isRead: true })
|
|
88
56
|
.where(and(eq(notifications.id, notificationId), eq(notifications.userId, userId)));
|
|
89
57
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Mark all unread notifications as read for a user
|
|
92
|
-
*/
|
|
93
58
|
export async function markAllAsRead(userId) {
|
|
94
59
|
await db
|
|
95
60
|
.update(notifications)
|
|
96
61
|
.set({ isRead: true })
|
|
97
62
|
.where(and(eq(notifications.userId, userId), eq(notifications.isRead, false)));
|
|
98
63
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Parse metadata JSON string back to object
|
|
101
|
-
*
|
|
102
|
-
* Returns notification with metadata as parsed object (or null if no metadata).
|
|
103
|
-
*/
|
|
104
64
|
function parseMetadata(notification) {
|
|
105
65
|
return {
|
|
106
66
|
...notification,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/notifications/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/notifications/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* These functions create BOTH in-app notifications (via createNotification)
|
|
5
|
-
* AND send emails (via email senders). They are the integration points that
|
|
6
|
-
* connect application events to the notification + email infrastructure.
|
|
7
|
-
*
|
|
8
|
-
* Design:
|
|
9
|
-
* - In-app notifications are awaited (fast DB inserts)
|
|
10
|
-
* - Email sends are fire-and-forget (non-blocking, errors logged)
|
|
11
|
-
* - These do NOT handle deduplication -- that's handled by notification_history
|
|
12
|
-
* table from Phase 12's checkNotificationTriggers()
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* - Quota/trial triggers are called by Phase 12 notification system
|
|
16
|
-
* - Payment failed trigger is called by billing email triggers
|
|
17
|
-
* - Team member joined trigger is called by server actions
|
|
2
|
+
* Each trigger creates an in-app notification (awaited) and sends an email
|
|
3
|
+
* (fire-and-forget, errors logged). Deduplication is the caller's job.
|
|
18
4
|
*/
|
|
5
|
+
import { type Money } from "../money.js";
|
|
19
6
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
7
|
+
* The plan allowance is money, not tokens: `used` and `allowance` are
|
|
8
|
+
* amounts in the deployment's billing currency, and the copy prints
|
|
9
|
+
* them as such.
|
|
22
10
|
*/
|
|
23
11
|
export declare function triggerQuotaNotification(params: {
|
|
24
12
|
userId: string;
|
|
@@ -26,14 +14,10 @@ export declare function triggerQuotaNotification(params: {
|
|
|
26
14
|
workspaceId: string;
|
|
27
15
|
workspaceName: string;
|
|
28
16
|
percentageUsed: number;
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
used: Money;
|
|
18
|
+
allowance: Money;
|
|
31
19
|
isExceeded: boolean;
|
|
32
20
|
}): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Trigger a trial credits warning or depleted notification.
|
|
35
|
-
* Creates an in-app notification and sends an email.
|
|
36
|
-
*/
|
|
37
21
|
export declare function triggerTrialNotification(params: {
|
|
38
22
|
userId: string;
|
|
39
23
|
userEmail: string;
|
|
@@ -44,10 +28,6 @@ export declare function triggerTrialNotification(params: {
|
|
|
44
28
|
percentageRemaining: number;
|
|
45
29
|
isDepleted: boolean;
|
|
46
30
|
}): Promise<void>;
|
|
47
|
-
/**
|
|
48
|
-
* Trigger a payment failed notification.
|
|
49
|
-
* Creates an in-app notification and sends an email.
|
|
50
|
-
*/
|
|
51
31
|
export declare function triggerPaymentFailedNotification(params: {
|
|
52
32
|
userId: string;
|
|
53
33
|
userEmail: string;
|
|
@@ -55,10 +35,7 @@ export declare function triggerPaymentFailedNotification(params: {
|
|
|
55
35
|
workspaceName: string;
|
|
56
36
|
amount: string;
|
|
57
37
|
}): Promise<void>;
|
|
58
|
-
/**
|
|
59
|
-
* Trigger a team member joined notification.
|
|
60
|
-
* Creates an in-app notification ONLY (no email for this event).
|
|
61
|
-
*/
|
|
38
|
+
/** In-app only: this event sends no email. */
|
|
62
39
|
export declare function triggerTeamMemberJoinedNotification(params: {
|
|
63
40
|
userId: string;
|
|
64
41
|
workspaceId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../../src/notifications/triggers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../../src/notifications/triggers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAKnD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,KAAK,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiChB;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB;AAED,wBAAsB,gCAAgC,CAAC,MAAM,EAAE;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AAED,8CAA8C;AAC9C,wBAAsB,mCAAmC,CAAC,MAAM,EAAE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB"}
|
|
@@ -1,69 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* These functions create BOTH in-app notifications (via createNotification)
|
|
5
|
-
* AND send emails (via email senders). They are the integration points that
|
|
6
|
-
* connect application events to the notification + email infrastructure.
|
|
7
|
-
*
|
|
8
|
-
* Design:
|
|
9
|
-
* - In-app notifications are awaited (fast DB inserts)
|
|
10
|
-
* - Email sends are fire-and-forget (non-blocking, errors logged)
|
|
11
|
-
* - These do NOT handle deduplication -- that's handled by notification_history
|
|
12
|
-
* table from Phase 12's checkNotificationTriggers()
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* - Quota/trial triggers are called by Phase 12 notification system
|
|
16
|
-
* - Payment failed trigger is called by billing email triggers
|
|
17
|
-
* - Team member joined trigger is called by server actions
|
|
2
|
+
* Each trigger creates an in-app notification (awaited) and sends an email
|
|
3
|
+
* (fire-and-forget, errors logged). Deduplication is the caller's job.
|
|
18
4
|
*/
|
|
19
|
-
import { createNotification } from "./index";
|
|
20
|
-
import { sendQuotaWarningEmail, sendTrialWarningEmail, sendPaymentFailedEmail, } from "../email/senders";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
import { createNotification } from "./index.js";
|
|
6
|
+
import { sendQuotaWarningEmail, sendTrialWarningEmail, sendPaymentFailedEmail, } from "../email/senders.js";
|
|
7
|
+
import { formatMoney } from "../money.js";
|
|
8
|
+
/** The locale the framework's own notification and email copy is written in. */
|
|
9
|
+
const COPY_LOCALE = "en";
|
|
24
10
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
11
|
+
* The plan allowance is money, not tokens: `used` and `allowance` are
|
|
12
|
+
* amounts in the deployment's billing currency, and the copy prints
|
|
13
|
+
* them as such.
|
|
27
14
|
*/
|
|
28
15
|
export async function triggerQuotaNotification(params) {
|
|
29
16
|
const upgradeUrl = `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"}/settings/billing`;
|
|
30
|
-
|
|
17
|
+
const used = formatMoney(params.used, COPY_LOCALE);
|
|
18
|
+
const allowance = formatMoney(params.allowance, COPY_LOCALE);
|
|
31
19
|
await createNotification({
|
|
32
20
|
userId: params.userId,
|
|
33
21
|
workspaceId: params.workspaceId,
|
|
34
22
|
type: params.isExceeded ? "quota_warning_100" : "quota_warning_80",
|
|
35
|
-
title: params.isExceeded ? "
|
|
23
|
+
title: params.isExceeded ? "Allowance Used Up" : "Allowance Warning",
|
|
36
24
|
message: params.isExceeded
|
|
37
|
-
? `Your workspace "${params.workspaceName}" has used all ${
|
|
38
|
-
: `Your workspace "${params.workspaceName}" has used ${
|
|
25
|
+
? `Your workspace "${params.workspaceName}" has used all of its ${allowance} monthly allowance. Upgrade your plan for more.`
|
|
26
|
+
: `Your workspace "${params.workspaceName}" has used ${used} of its ${allowance} monthly allowance (${params.percentageUsed}%).`,
|
|
39
27
|
metadata: {
|
|
40
28
|
workspaceName: params.workspaceName,
|
|
41
29
|
percentageUsed: params.percentageUsed,
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
usedMicros: params.used.amount,
|
|
31
|
+
allowanceMicros: params.allowance.amount,
|
|
32
|
+
currency: params.allowance.currency,
|
|
44
33
|
},
|
|
45
34
|
});
|
|
46
|
-
// Send email (fire-and-forget -- non-blocking)
|
|
47
35
|
sendQuotaWarningEmail({
|
|
48
36
|
to: params.userEmail,
|
|
49
37
|
workspaceName: params.workspaceName,
|
|
50
38
|
percentageUsed: params.percentageUsed,
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
used: params.used,
|
|
40
|
+
allowance: params.allowance,
|
|
53
41
|
upgradeUrl,
|
|
54
42
|
isExceeded: params.isExceeded,
|
|
55
43
|
}).catch((err) => console.error("[Email] Failed to send quota warning:", err));
|
|
56
44
|
}
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Trial Notifications (TRIAL-04, TRIAL-05)
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
/**
|
|
61
|
-
* Trigger a trial credits warning or depleted notification.
|
|
62
|
-
* Creates an in-app notification and sends an email.
|
|
63
|
-
*/
|
|
64
45
|
export async function triggerTrialNotification(params) {
|
|
65
46
|
const upgradeUrl = `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"}/settings/billing`;
|
|
66
|
-
// Create in-app notification (awaited -- fast DB insert)
|
|
67
47
|
await createNotification({
|
|
68
48
|
userId: params.userId,
|
|
69
49
|
workspaceId: params.workspaceId,
|
|
@@ -81,7 +61,6 @@ export async function triggerTrialNotification(params) {
|
|
|
81
61
|
percentageRemaining: params.percentageRemaining,
|
|
82
62
|
},
|
|
83
63
|
});
|
|
84
|
-
// Send email (fire-and-forget -- non-blocking)
|
|
85
64
|
sendTrialWarningEmail({
|
|
86
65
|
to: params.userEmail,
|
|
87
66
|
workspaceName: params.workspaceName,
|
|
@@ -92,16 +71,8 @@ export async function triggerTrialNotification(params) {
|
|
|
92
71
|
isDepleted: params.isDepleted,
|
|
93
72
|
}).catch((err) => console.error("[Email] Failed to send trial warning:", err));
|
|
94
73
|
}
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
// Payment Failed Notification
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
/**
|
|
99
|
-
* Trigger a payment failed notification.
|
|
100
|
-
* Creates an in-app notification and sends an email.
|
|
101
|
-
*/
|
|
102
74
|
export async function triggerPaymentFailedNotification(params) {
|
|
103
75
|
const updatePaymentUrl = `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"}/settings/billing`;
|
|
104
|
-
// Create in-app notification (awaited -- fast DB insert)
|
|
105
76
|
await createNotification({
|
|
106
77
|
userId: params.userId,
|
|
107
78
|
workspaceId: params.workspaceId,
|
|
@@ -113,7 +84,6 @@ export async function triggerPaymentFailedNotification(params) {
|
|
|
113
84
|
amount: params.amount,
|
|
114
85
|
},
|
|
115
86
|
});
|
|
116
|
-
// Send email (fire-and-forget -- non-blocking)
|
|
117
87
|
sendPaymentFailedEmail({
|
|
118
88
|
to: params.userEmail,
|
|
119
89
|
workspaceName: params.workspaceName,
|
|
@@ -121,15 +91,8 @@ export async function triggerPaymentFailedNotification(params) {
|
|
|
121
91
|
updatePaymentUrl,
|
|
122
92
|
}).catch((err) => console.error("[Email] Failed to send payment failed:", err));
|
|
123
93
|
}
|
|
124
|
-
|
|
125
|
-
// Team Member Joined Notification
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
/**
|
|
128
|
-
* Trigger a team member joined notification.
|
|
129
|
-
* Creates an in-app notification ONLY (no email for this event).
|
|
130
|
-
*/
|
|
94
|
+
/** In-app only: this event sends no email. */
|
|
131
95
|
export async function triggerTeamMemberJoinedNotification(params) {
|
|
132
|
-
// Create in-app notification only (no email for team member joined events)
|
|
133
96
|
await createNotification({
|
|
134
97
|
userId: params.userId,
|
|
135
98
|
workspaceId: params.workspaceId,
|