@heavybit/shared-lib 1.3.2 → 2.7.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/README.md +261 -0
- package/dist/cjs/access/index.js +9 -1
- package/dist/cjs/access/index.js.map +1 -1
- package/dist/cjs/approvals/index.js +137 -0
- package/dist/cjs/approvals/index.js.map +1 -0
- package/dist/cjs/audit/index.js +74 -0
- package/dist/cjs/audit/index.js.map +1 -0
- package/dist/cjs/cache/index.js +90 -16
- package/dist/cjs/cache/index.js.map +1 -1
- package/dist/cjs/cache/keyspace.js +101 -0
- package/dist/cjs/cache/keyspace.js.map +1 -0
- package/dist/cjs/common/circuit-breaker.js +74 -0
- package/dist/cjs/common/circuit-breaker.js.map +1 -0
- package/dist/cjs/common/errors.js +44 -5
- package/dist/cjs/common/errors.js.map +1 -1
- package/dist/cjs/common/index.js +4 -0
- package/dist/cjs/common/index.js.map +1 -1
- package/dist/cjs/common/internal-client.js +177 -0
- package/dist/cjs/common/internal-client.js.map +1 -0
- package/dist/cjs/common/internal-constants.js +12 -0
- package/dist/cjs/common/internal-constants.js.map +1 -0
- package/dist/cjs/common/internal-service-auth.js +160 -0
- package/dist/cjs/common/internal-service-auth.js.map +1 -0
- package/dist/cjs/common/money.js +215 -0
- package/dist/cjs/common/money.js.map +1 -0
- package/dist/cjs/common/phone.js +63 -11
- package/dist/cjs/common/phone.js.map +1 -1
- package/dist/cjs/common/response.js +78 -5
- package/dist/cjs/common/response.js.map +1 -1
- package/dist/cjs/common/time.js +28 -2
- package/dist/cjs/common/time.js.map +1 -1
- package/dist/cjs/config/index.js +140 -20
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/context/index.js +49 -0
- package/dist/cjs/context/index.js.map +1 -0
- package/dist/cjs/contracts/index.js +0 -2
- package/dist/cjs/contracts/index.js.map +1 -1
- package/dist/cjs/contracts/notification-dispatch.js +87 -63
- package/dist/cjs/contracts/notification-dispatch.js.map +1 -1
- package/dist/cjs/database/db.js +37 -0
- package/dist/cjs/database/db.js.map +1 -0
- package/dist/cjs/database/index.js +22 -51
- package/dist/cjs/database/index.js.map +1 -1
- package/dist/cjs/database/pagination.js +88 -0
- package/dist/cjs/database/pagination.js.map +1 -0
- package/dist/cjs/database/pool.js +60 -0
- package/dist/cjs/database/pool.js.map +1 -0
- package/dist/cjs/database/scoped-repository.js +132 -0
- package/dist/cjs/database/scoped-repository.js.map +1 -0
- package/dist/cjs/database/scopes.js +166 -0
- package/dist/cjs/database/scopes.js.map +1 -0
- package/dist/cjs/database/table-names.js +14 -0
- package/dist/cjs/database/table-names.js.map +1 -0
- package/dist/cjs/database/tables.js +71 -0
- package/dist/cjs/database/tables.js.map +1 -0
- package/dist/cjs/express/auth.js +88 -41
- package/dist/cjs/express/auth.js.map +1 -1
- package/dist/cjs/express/index.js +3 -0
- package/dist/cjs/express/index.js.map +1 -1
- package/dist/cjs/express/internal-caller.js +52 -0
- package/dist/cjs/express/internal-caller.js.map +1 -0
- package/dist/cjs/express/middleware.js +154 -34
- package/dist/cjs/express/middleware.js.map +1 -1
- package/dist/cjs/express/respond.js +47 -0
- package/dist/cjs/express/respond.js.map +1 -0
- package/dist/cjs/express/step-up.js +41 -0
- package/dist/cjs/express/step-up.js.map +1 -0
- package/dist/cjs/http/index.js +3 -71
- package/dist/cjs/http/index.js.map +1 -1
- package/dist/cjs/identity/index.js +269 -67
- package/dist/cjs/identity/index.js.map +1 -1
- package/dist/cjs/identity/step-up.js +52 -0
- package/dist/cjs/identity/step-up.js.map +1 -0
- package/dist/cjs/index.js +7 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/knex/columns.js +36 -0
- package/dist/cjs/knex/columns.js.map +1 -0
- package/dist/cjs/knex/index.js +26 -43
- package/dist/cjs/knex/index.js.map +1 -1
- package/dist/cjs/knex/migration-helpers.js +57 -0
- package/dist/cjs/knex/migration-helpers.js.map +1 -0
- package/dist/cjs/logging/index.js +76 -40
- package/dist/cjs/logging/index.js.map +1 -1
- package/dist/cjs/logging/redact.js +112 -0
- package/dist/cjs/logging/redact.js.map +1 -0
- package/dist/cjs/messaging/base-consumer.js +36 -0
- package/dist/cjs/messaging/base-consumer.js.map +1 -0
- package/dist/cjs/messaging/consumer.js +142 -73
- package/dist/cjs/messaging/consumer.js.map +1 -1
- package/dist/cjs/messaging/envelope.js +53 -10
- package/dist/cjs/messaging/envelope.js.map +1 -1
- package/dist/cjs/messaging/idempotency.js +95 -8
- package/dist/cjs/messaging/idempotency.js.map +1 -1
- package/dist/cjs/messaging/index.js +3 -1
- package/dist/cjs/messaging/index.js.map +1 -1
- package/dist/cjs/messaging/outbox.js +97 -55
- package/dist/cjs/messaging/outbox.js.map +1 -1
- package/dist/cjs/messaging/publisher.js +86 -38
- package/dist/cjs/messaging/publisher.js.map +1 -1
- package/dist/cjs/messaging/staff-notifier.js +36 -44
- package/dist/cjs/messaging/staff-notifier.js.map +1 -1
- package/dist/cjs/messaging/staff-recipients.js +57 -0
- package/dist/cjs/messaging/staff-recipients.js.map +1 -0
- package/dist/cjs/messaging/topology.js +41 -5
- package/dist/cjs/messaging/topology.js.map +1 -1
- package/dist/cjs/rate-limit/index.js +57 -6
- package/dist/cjs/rate-limit/index.js.map +1 -1
- package/dist/cjs/rate-limit/keys.js +47 -0
- package/dist/cjs/rate-limit/keys.js.map +1 -0
- package/dist/cjs/socket/index.js +127 -16
- package/dist/cjs/socket/index.js.map +1 -1
- package/dist/cjs/socket/ticket.js +124 -0
- package/dist/cjs/socket/ticket.js.map +1 -0
- package/dist/cjs/testing/index.js +23 -14
- package/dist/cjs/testing/index.js.map +1 -1
- package/dist/cjs/validation/index.js +27 -3
- package/dist/cjs/validation/index.js.map +1 -1
- package/dist/esm/access/index.js +9 -1
- package/dist/esm/access/index.js.map +1 -1
- package/dist/esm/approvals/index.js +127 -0
- package/dist/esm/approvals/index.js.map +1 -0
- package/dist/esm/audit/index.js +70 -0
- package/dist/esm/audit/index.js.map +1 -0
- package/dist/esm/cache/index.js +70 -13
- package/dist/esm/cache/index.js.map +1 -1
- package/dist/esm/cache/keyspace.js +95 -0
- package/dist/esm/cache/keyspace.js.map +1 -0
- package/dist/esm/common/circuit-breaker.js +70 -0
- package/dist/esm/common/circuit-breaker.js.map +1 -0
- package/dist/esm/common/errors.js +42 -4
- package/dist/esm/common/errors.js.map +1 -1
- package/dist/esm/common/index.js +4 -0
- package/dist/esm/common/index.js.map +1 -1
- package/dist/esm/common/internal-client.js +173 -0
- package/dist/esm/common/internal-client.js.map +1 -0
- package/dist/esm/common/internal-constants.js +9 -0
- package/dist/esm/common/internal-constants.js.map +1 -0
- package/dist/esm/common/internal-service-auth.js +150 -0
- package/dist/esm/common/internal-service-auth.js.map +1 -0
- package/dist/esm/common/money.js +207 -0
- package/dist/esm/common/money.js.map +1 -0
- package/dist/esm/common/phone.js +63 -11
- package/dist/esm/common/phone.js.map +1 -1
- package/dist/esm/common/response.js +75 -5
- package/dist/esm/common/response.js.map +1 -1
- package/dist/esm/common/time.js +27 -2
- package/dist/esm/common/time.js.map +1 -1
- package/dist/esm/config/index.js +136 -19
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/context/index.js +44 -0
- package/dist/esm/context/index.js.map +1 -0
- package/dist/esm/contracts/index.js +0 -2
- package/dist/esm/contracts/index.js.map +1 -1
- package/dist/esm/contracts/notification-dispatch.js +85 -62
- package/dist/esm/contracts/notification-dispatch.js.map +1 -1
- package/dist/esm/database/db.js +32 -0
- package/dist/esm/database/db.js.map +1 -0
- package/dist/esm/database/index.js +9 -45
- package/dist/esm/database/index.js.map +1 -1
- package/dist/esm/database/pagination.js +81 -0
- package/dist/esm/database/pagination.js.map +1 -0
- package/dist/esm/database/pool.js +51 -0
- package/dist/esm/database/pool.js.map +1 -0
- package/dist/esm/database/scoped-repository.js +128 -0
- package/dist/esm/database/scoped-repository.js.map +1 -0
- package/dist/esm/database/scopes.js +157 -0
- package/dist/esm/database/scopes.js.map +1 -0
- package/dist/esm/database/table-names.js +11 -0
- package/dist/esm/database/table-names.js.map +1 -0
- package/dist/esm/database/tables.js +61 -0
- package/dist/esm/database/tables.js.map +1 -0
- package/dist/esm/express/auth.js +87 -40
- package/dist/esm/express/auth.js.map +1 -1
- package/dist/esm/express/index.js +3 -0
- package/dist/esm/express/index.js.map +1 -1
- package/dist/esm/express/internal-caller.js +49 -0
- package/dist/esm/express/internal-caller.js.map +1 -0
- package/dist/esm/express/middleware.js +146 -27
- package/dist/esm/express/middleware.js.map +1 -1
- package/dist/esm/express/respond.js +42 -0
- package/dist/esm/express/respond.js.map +1 -0
- package/dist/esm/express/step-up.js +38 -0
- package/dist/esm/express/step-up.js.map +1 -0
- package/dist/esm/http/index.js +3 -70
- package/dist/esm/http/index.js.map +1 -1
- package/dist/esm/identity/index.js +246 -64
- package/dist/esm/identity/index.js.map +1 -1
- package/dist/esm/identity/step-up.js +47 -0
- package/dist/esm/identity/step-up.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/knex/columns.js +28 -0
- package/dist/esm/knex/columns.js.map +1 -0
- package/dist/esm/knex/index.js +8 -34
- package/dist/esm/knex/index.js.map +1 -1
- package/dist/esm/knex/migration-helpers.js +51 -0
- package/dist/esm/knex/migration-helpers.js.map +1 -0
- package/dist/esm/logging/index.js +68 -39
- package/dist/esm/logging/index.js.map +1 -1
- package/dist/esm/logging/redact.js +106 -0
- package/dist/esm/logging/redact.js.map +1 -0
- package/dist/esm/messaging/base-consumer.js +32 -0
- package/dist/esm/messaging/base-consumer.js.map +1 -0
- package/dist/esm/messaging/consumer.js +139 -71
- package/dist/esm/messaging/consumer.js.map +1 -1
- package/dist/esm/messaging/envelope.js +50 -10
- package/dist/esm/messaging/envelope.js.map +1 -1
- package/dist/esm/messaging/idempotency.js +91 -7
- package/dist/esm/messaging/idempotency.js.map +1 -1
- package/dist/esm/messaging/index.js +3 -1
- package/dist/esm/messaging/index.js.map +1 -1
- package/dist/esm/messaging/outbox.js +97 -54
- package/dist/esm/messaging/outbox.js.map +1 -1
- package/dist/esm/messaging/publisher.js +86 -38
- package/dist/esm/messaging/publisher.js.map +1 -1
- package/dist/esm/messaging/staff-notifier.js +36 -43
- package/dist/esm/messaging/staff-notifier.js.map +1 -1
- package/dist/esm/messaging/staff-recipients.js +53 -0
- package/dist/esm/messaging/staff-recipients.js.map +1 -0
- package/dist/esm/messaging/topology.js +39 -4
- package/dist/esm/messaging/topology.js.map +1 -1
- package/dist/esm/rate-limit/index.js +41 -6
- package/dist/esm/rate-limit/index.js.map +1 -1
- package/dist/esm/rate-limit/keys.js +43 -0
- package/dist/esm/rate-limit/keys.js.map +1 -0
- package/dist/esm/socket/index.js +112 -17
- package/dist/esm/socket/index.js.map +1 -1
- package/dist/esm/socket/ticket.js +117 -0
- package/dist/esm/socket/ticket.js.map +1 -0
- package/dist/esm/testing/index.js +24 -15
- package/dist/esm/testing/index.js.map +1 -1
- package/dist/esm/validation/index.js +27 -2
- package/dist/esm/validation/index.js.map +1 -1
- package/dist/types/access/index.d.ts +9 -3
- package/dist/types/access/index.d.ts.map +1 -1
- package/dist/types/alerting/index.d.ts +1 -1
- package/dist/types/alerting/index.d.ts.map +1 -1
- package/dist/types/approvals/index.d.ts +99 -0
- package/dist/types/approvals/index.d.ts.map +1 -0
- package/dist/types/audit/index.d.ts +82 -0
- package/dist/types/audit/index.d.ts.map +1 -0
- package/dist/types/cache/index.d.ts +64 -14
- package/dist/types/cache/index.d.ts.map +1 -1
- package/dist/types/cache/keyspace.d.ts +84 -0
- package/dist/types/cache/keyspace.d.ts.map +1 -0
- package/dist/types/common/circuit-breaker.d.ts +51 -0
- package/dist/types/common/circuit-breaker.d.ts.map +1 -0
- package/dist/types/common/errors.d.ts +48 -7
- package/dist/types/common/errors.d.ts.map +1 -1
- package/dist/types/common/index.d.ts +4 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/internal-client.d.ts +116 -0
- package/dist/types/common/internal-client.d.ts.map +1 -0
- package/dist/types/common/internal-constants.d.ts +9 -0
- package/dist/types/common/internal-constants.d.ts.map +1 -0
- package/dist/types/common/internal-service-auth.d.ts +102 -0
- package/dist/types/common/internal-service-auth.d.ts.map +1 -0
- package/dist/types/common/money.d.ts +104 -0
- package/dist/types/common/money.d.ts.map +1 -0
- package/dist/types/common/phone.d.ts +45 -6
- package/dist/types/common/phone.d.ts.map +1 -1
- package/dist/types/common/response.d.ts +66 -5
- package/dist/types/common/response.d.ts.map +1 -1
- package/dist/types/common/time.d.ts +20 -2
- package/dist/types/common/time.d.ts.map +1 -1
- package/dist/types/config/index.d.ts +252 -28
- package/dist/types/config/index.d.ts.map +1 -1
- package/dist/types/context/index.d.ts +39 -0
- package/dist/types/context/index.d.ts.map +1 -0
- package/dist/types/contracts/index.d.ts +0 -2
- package/dist/types/contracts/index.d.ts.map +1 -1
- package/dist/types/contracts/notification-dispatch.d.ts +204 -86
- package/dist/types/contracts/notification-dispatch.d.ts.map +1 -1
- package/dist/types/database/db.d.ts +46 -0
- package/dist/types/database/db.d.ts.map +1 -0
- package/dist/types/database/index.d.ts +9 -22
- package/dist/types/database/index.d.ts.map +1 -1
- package/dist/types/database/pagination.d.ts +68 -0
- package/dist/types/database/pagination.d.ts.map +1 -0
- package/dist/types/database/pool.d.ts +39 -0
- package/dist/types/database/pool.d.ts.map +1 -0
- package/dist/types/database/scoped-repository.d.ts +92 -0
- package/dist/types/database/scoped-repository.d.ts.map +1 -0
- package/dist/types/database/scopes.d.ts +159 -0
- package/dist/types/database/scopes.d.ts.map +1 -0
- package/dist/types/database/table-names.d.ts +13 -0
- package/dist/types/database/table-names.d.ts.map +1 -0
- package/dist/types/database/tables.d.ts +748 -0
- package/dist/types/database/tables.d.ts.map +1 -0
- package/dist/types/express/auth.d.ts +63 -11
- package/dist/types/express/auth.d.ts.map +1 -1
- package/dist/types/express/index.d.ts +3 -0
- package/dist/types/express/index.d.ts.map +1 -1
- package/dist/types/express/internal-caller.d.ts +53 -0
- package/dist/types/express/internal-caller.d.ts.map +1 -0
- package/dist/types/express/middleware.d.ts +86 -7
- package/dist/types/express/middleware.d.ts.map +1 -1
- package/dist/types/express/respond.d.ts +41 -0
- package/dist/types/express/respond.d.ts.map +1 -0
- package/dist/types/express/step-up.d.ts +38 -0
- package/dist/types/express/step-up.d.ts.map +1 -0
- package/dist/types/http/index.d.ts +3 -31
- package/dist/types/http/index.d.ts.map +1 -1
- package/dist/types/identity/index.d.ts +159 -21
- package/dist/types/identity/index.d.ts.map +1 -1
- package/dist/types/identity/step-up.d.ts +41 -0
- package/dist/types/identity/step-up.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/knex/columns.d.ts +15 -0
- package/dist/types/knex/columns.d.ts.map +1 -0
- package/dist/types/knex/index.d.ts +3 -11
- package/dist/types/knex/index.d.ts.map +1 -1
- package/dist/types/knex/migration-helpers.d.ts +28 -0
- package/dist/types/knex/migration-helpers.d.ts.map +1 -0
- package/dist/types/logging/index.d.ts +30 -4
- package/dist/types/logging/index.d.ts.map +1 -1
- package/dist/types/logging/redact.d.ts +54 -0
- package/dist/types/logging/redact.d.ts.map +1 -0
- package/dist/types/messaging/base-consumer.d.ts +33 -0
- package/dist/types/messaging/base-consumer.d.ts.map +1 -0
- package/dist/types/messaging/consumer.d.ts +59 -21
- package/dist/types/messaging/consumer.d.ts.map +1 -1
- package/dist/types/messaging/envelope.d.ts +50 -11
- package/dist/types/messaging/envelope.d.ts.map +1 -1
- package/dist/types/messaging/idempotency.d.ts +60 -3
- package/dist/types/messaging/idempotency.d.ts.map +1 -1
- package/dist/types/messaging/index.d.ts +8 -0
- package/dist/types/messaging/index.d.ts.map +1 -1
- package/dist/types/messaging/outbox.d.ts +62 -22
- package/dist/types/messaging/outbox.d.ts.map +1 -1
- package/dist/types/messaging/publisher.d.ts +54 -21
- package/dist/types/messaging/publisher.d.ts.map +1 -1
- package/dist/types/messaging/staff-notifier.d.ts +69 -38
- package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
- package/dist/types/messaging/staff-recipients.d.ts +50 -0
- package/dist/types/messaging/staff-recipients.d.ts.map +1 -0
- package/dist/types/messaging/topology.d.ts +40 -8
- package/dist/types/messaging/topology.d.ts.map +1 -1
- package/dist/types/rate-limit/index.d.ts +40 -4
- package/dist/types/rate-limit/index.d.ts.map +1 -1
- package/dist/types/rate-limit/keys.d.ts +52 -0
- package/dist/types/rate-limit/keys.d.ts.map +1 -0
- package/dist/types/socket/index.d.ts +86 -2
- package/dist/types/socket/index.d.ts.map +1 -1
- package/dist/types/socket/ticket.d.ts +90 -0
- package/dist/types/socket/ticket.d.ts.map +1 -0
- package/dist/types/testing/index.d.ts +31 -10
- package/dist/types/testing/index.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +62 -15
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/validation/index.d.ts +33 -2
- package/dist/types/validation/index.d.ts.map +1 -1
- package/package.json +3 -1
- package/dist/cjs/contracts/editorial-draft.js +0 -120
- package/dist/cjs/contracts/editorial-draft.js.map +0 -1
- package/dist/cjs/contracts/upload-import.js +0 -54
- package/dist/cjs/contracts/upload-import.js.map +0 -1
- package/dist/esm/contracts/editorial-draft.js +0 -117
- package/dist/esm/contracts/editorial-draft.js.map +0 -1
- package/dist/esm/contracts/upload-import.js +0 -51
- package/dist/esm/contracts/upload-import.js.map +0 -1
- package/dist/types/contracts/editorial-draft.d.ts +0 -610
- package/dist/types/contracts/editorial-draft.d.ts.map +0 -1
- package/dist/types/contracts/upload-import.d.ts +0 -96
- package/dist/types/contracts/upload-import.d.ts.map +0 -1
|
@@ -1,92 +1,116 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.dispatchDataKeySchema = exports.dispatchRecipientSchema = exports.dispatchValueSchema = exports.DISPATCH_LIMITS = exports.NOTIFICATION_DISPATCH = void 0;
|
|
4
|
+
exports.createNotificationDispatchSchema = createNotificationDispatchSchema;
|
|
5
|
+
/**
|
|
6
|
+
* Notification dispatch: a generic command contract (shared plumbing, no app vocabulary).
|
|
7
|
+
*
|
|
8
|
+
* Services never send email, SMS or push themselves. They stage one dispatch command (default
|
|
9
|
+
* `notification:dispatch`) through their outbox, and the app's notification-service renders a seeded
|
|
10
|
+
* template and delivers it. Both sides build their schema from {@link createNotificationDispatchSchema},
|
|
11
|
+
* so producers and the consumer cannot drift.
|
|
12
|
+
*
|
|
13
|
+
* App-specific parts are parameters: the app passes its own categories (e.g. `security`, `order`) and
|
|
14
|
+
* channels (e.g. `email`, `sms`, `in_app`). Template slugs and their variables also belong to the app
|
|
15
|
+
* (keep a table next to the notification-service seeds and test both sides against it).
|
|
16
|
+
*/
|
|
4
17
|
const zod_1 = require("zod");
|
|
5
18
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* and notification-service validates against it, so the two cannot drift.
|
|
19
|
+
* Default command name for a dispatch. An app may use another name and pass it to its producers.
|
|
20
|
+
*
|
|
21
|
+
* @example await outbox.command(tx, NOTIFICATION_DISPATCH, dispatch, { correlationId });
|
|
10
22
|
*/
|
|
11
23
|
exports.NOTIFICATION_DISPATCH = 'notification:dispatch';
|
|
12
|
-
/** Channels a dispatch may ask for. `in_app` needs a recipient `userId` (the bell feed is per user). */
|
|
13
|
-
exports.DISPATCH_CHANNELS = ['email', 'in_app'];
|
|
14
24
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
25
|
+
* Size limits that keep a dispatch small enough for a queue message and safe to render.
|
|
26
|
+
*
|
|
27
|
+
* @example z.string().max(DISPATCH_LIMITS.template)
|
|
17
28
|
*/
|
|
18
|
-
exports.NOTIFICATION_CATEGORIES = ['security', 'account', 'team', 'marketing', 'staff'];
|
|
19
29
|
exports.DISPATCH_LIMITS = {
|
|
20
30
|
template: 100,
|
|
21
31
|
email: 254,
|
|
22
32
|
name: 200,
|
|
33
|
+
userId: 36,
|
|
23
34
|
dataKeys: 40,
|
|
24
35
|
dataKey: 64,
|
|
25
36
|
dataValue: 2000,
|
|
26
37
|
secretKeys: 10,
|
|
27
38
|
permission: 100,
|
|
28
39
|
};
|
|
29
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Template variables are flat scalars: they are printed into the copy, never serialised objects.
|
|
42
|
+
*
|
|
43
|
+
* @example dispatchValueSchema.parse('1250.00')
|
|
44
|
+
*/
|
|
30
45
|
exports.dispatchValueSchema = zod_1.z.union([zod_1.z.string().max(exports.DISPATCH_LIMITS.dataValue), zod_1.z.number().finite(), zod_1.z.boolean(), zod_1.z.null()]);
|
|
46
|
+
/**
|
|
47
|
+
* Who receives a dispatch. `userId` is needed for per-user channels such as an in-app feed;
|
|
48
|
+
* addresses with no account behind them carry only `email`.
|
|
49
|
+
*
|
|
50
|
+
* @example { email: 'ada@example.com', name: 'Ada', userId: '01J…', timezone: 'Europe/Lisbon' }
|
|
51
|
+
*/
|
|
31
52
|
exports.dispatchRecipientSchema = zod_1.z.object({
|
|
32
53
|
email: zod_1.z.string().trim().email().max(exports.DISPATCH_LIMITS.email),
|
|
33
54
|
name: zod_1.z.string().trim().max(exports.DISPATCH_LIMITS.name).optional(),
|
|
34
|
-
userId: zod_1.z.string().max(
|
|
55
|
+
userId: zod_1.z.string().max(exports.DISPATCH_LIMITS.userId).nullish(),
|
|
35
56
|
locale: zod_1.z.string().max(20).nullish(),
|
|
36
57
|
/** IANA zone; time variables are rendered in it. */
|
|
37
58
|
timezone: zod_1.z.string().max(64).nullish(),
|
|
38
59
|
});
|
|
39
|
-
const dataKey = zod_1.z.string().regex(/^[A-Za-z][A-Za-z0-9_]*$/, 'Use a template variable name').max(exports.DISPATCH_LIMITS.dataKey);
|
|
40
|
-
exports.notificationDispatchSchema = zod_1.z
|
|
41
|
-
.object({
|
|
42
|
-
/** Seeded `notification_templates.slug`. */
|
|
43
|
-
template: zod_1.z.string().trim().min(1).max(exports.DISPATCH_LIMITS.template),
|
|
44
|
-
category: zod_1.z.enum(exports.NOTIFICATION_CATEGORIES),
|
|
45
|
-
recipient: exports.dispatchRecipientSchema,
|
|
46
|
-
channels: zod_1.z.array(zod_1.z.enum(exports.DISPATCH_CHANNELS)).min(1).max(exports.DISPATCH_CHANNELS.length),
|
|
47
|
-
data: zod_1.z
|
|
48
|
-
.record(dataKey, exports.dispatchValueSchema)
|
|
49
|
-
.default({})
|
|
50
|
-
.refine((data) => Object.keys(data).length <= exports.DISPATCH_LIMITS.dataKeys, `At most ${exports.DISPATCH_LIMITS.dataKeys} variables`),
|
|
51
|
-
/**
|
|
52
|
-
* Keys of `data` holding a one-time secret (a sign-in code, a reset or confirm link). They are used to
|
|
53
|
-
* render the email and nothing else: notification-service never stores them and never puts them in the
|
|
54
|
-
* in-app feed, and email-service masks them in what it keeps.
|
|
55
|
-
*/
|
|
56
|
-
secretKeys: zod_1.z.array(dataKey).max(exports.DISPATCH_LIMITS.secretKeys).default([]),
|
|
57
|
-
/** Security and staff alerts are always delivered, whatever the recipient's preferences. */
|
|
58
|
-
bypassPreferences: zod_1.z.boolean().optional(),
|
|
59
|
-
/** The permission staff recipients hold; informational until recipients are resolved by permission. */
|
|
60
|
-
targetPermission: zod_1.z.string().max(exports.DISPATCH_LIMITS.permission).optional(),
|
|
61
|
-
})
|
|
62
|
-
.superRefine((dispatch, ctx) => {
|
|
63
|
-
for (const key of dispatch.secretKeys) {
|
|
64
|
-
if (!(key in dispatch.data)) {
|
|
65
|
-
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['secretKeys'], message: `Secret key "${key}" is not in data` });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
60
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
61
|
+
* A template variable name the renderer can match.
|
|
62
|
+
*
|
|
63
|
+
* @example dispatchDataKeySchema.parse('expiresInMinutes')
|
|
73
64
|
*/
|
|
74
|
-
exports.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
};
|
|
65
|
+
exports.dispatchDataKeySchema = zod_1.z.string().regex(/^[A-Za-z][A-Za-z0-9_]*$/, 'Use a template variable name').max(exports.DISPATCH_LIMITS.dataKey);
|
|
66
|
+
/**
|
|
67
|
+
* Build the dispatch schema for an app's categories and channels. Producers type their payload with it;
|
|
68
|
+
* the notification-service validates incoming commands with the same schema.
|
|
69
|
+
*
|
|
70
|
+
* Rules every app gets: a seeded `template` slug; one or more distinct channels; at most 40 flat-scalar `data`
|
|
71
|
+
* variables with renderer-safe names; `secretKeys` (one-time codes, reset links) must be keys of `data`;
|
|
72
|
+
* the consumer must use them only to render, never store them or show them in a feed.
|
|
73
|
+
*
|
|
74
|
+
* @typeParam C The app's categories (inferred as literals).
|
|
75
|
+
* @typeParam Ch The app's channels (inferred as literals).
|
|
76
|
+
* @param vocabulary The app's categories and channels.
|
|
77
|
+
* @returns A Zod schema; `data` defaults to `{}` and `secretKeys` to `[]`.
|
|
78
|
+
* @example
|
|
79
|
+
* export const dispatchSchema = createNotificationDispatchSchema({
|
|
80
|
+
* categories: ['security', 'account', 'order', 'staff'],
|
|
81
|
+
* channels: ['email', 'sms'],
|
|
82
|
+
* });
|
|
83
|
+
* export type Dispatch = z.input<typeof dispatchSchema>;
|
|
84
|
+
*/
|
|
85
|
+
function createNotificationDispatchSchema(vocabulary) {
|
|
86
|
+
return zod_1.z
|
|
87
|
+
.object({
|
|
88
|
+
/** Seeded template slug in the notification-service. */
|
|
89
|
+
template: zod_1.z.string().trim().min(1).max(exports.DISPATCH_LIMITS.template),
|
|
90
|
+
category: zod_1.z.enum([...vocabulary.categories]),
|
|
91
|
+
recipient: exports.dispatchRecipientSchema,
|
|
92
|
+
channels: zod_1.z
|
|
93
|
+
.array(zod_1.z.enum([...vocabulary.channels]))
|
|
94
|
+
.min(1)
|
|
95
|
+
.max(vocabulary.channels.length)
|
|
96
|
+
.refine((channels) => new Set(channels).size === channels.length, 'Each channel at most once'),
|
|
97
|
+
data: zod_1.z
|
|
98
|
+
.record(exports.dispatchDataKeySchema, exports.dispatchValueSchema)
|
|
99
|
+
.default({})
|
|
100
|
+
.refine((data) => Object.keys(data).length <= exports.DISPATCH_LIMITS.dataKeys, `At most ${exports.DISPATCH_LIMITS.dataKeys} variables`),
|
|
101
|
+
/** Keys of `data` holding a one-time secret: used to render, never stored or shown in a feed. */
|
|
102
|
+
secretKeys: zod_1.z.array(exports.dispatchDataKeySchema).max(exports.DISPATCH_LIMITS.secretKeys).default([]),
|
|
103
|
+
/** Deliver whatever the recipient's notification preferences say (for mail the recipient cannot opt out of). */
|
|
104
|
+
bypassPreferences: zod_1.z.boolean().optional(),
|
|
105
|
+
/** The permission the recipients were resolved by, when they were (traceability only). */
|
|
106
|
+
targetPermission: zod_1.z.string().max(exports.DISPATCH_LIMITS.permission).optional(),
|
|
107
|
+
})
|
|
108
|
+
.superRefine((dispatch, ctx) => {
|
|
109
|
+
for (const key of dispatch.secretKeys) {
|
|
110
|
+
if (!(key in dispatch.data)) {
|
|
111
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['secretKeys'], message: `Secret key "${key}" is not in data` });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
92
116
|
//# sourceMappingURL=notification-dispatch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-dispatch.js","sourceRoot":"","sources":["../../../src/contracts/notification-dispatch.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"notification-dispatch.js","sourceRoot":"","sources":["../../../src/contracts/notification-dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDb = createDb;
|
|
4
|
+
exports.knexConfig = knexConfig;
|
|
5
|
+
exports.connectionOptions = connectionOptions;
|
|
6
|
+
const mysql2_1 = require("drizzle-orm/mysql2");
|
|
7
|
+
const pool_js_1 = require("./pool.js");
|
|
8
|
+
/**
|
|
9
|
+
* Creates the service's Drizzle database. There is no module-level singleton: call it once in
|
|
10
|
+
* `container.ts` and inject `db` into repositories.
|
|
11
|
+
*
|
|
12
|
+
* @example const { db, ping, close } = createDb({ env: config, schema });
|
|
13
|
+
*/
|
|
14
|
+
function createDb(options) {
|
|
15
|
+
const { env, schema } = options;
|
|
16
|
+
const pool = options.pool ?? (0, pool_js_1.createMysqlPool)(connectionOptions(env));
|
|
17
|
+
const db = (0, mysql2_1.drizzle)(pool, { schema, mode: 'default' });
|
|
18
|
+
return {
|
|
19
|
+
db,
|
|
20
|
+
ping: () => (0, pool_js_1.pingMysql)(pool),
|
|
21
|
+
close: () => pool.end(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Knex config for a service's `knexfile.js`. Knex runs **migrations and seeds only** (`.cjs` files
|
|
26
|
+
* in `./migrations` and `./seeds`); repositories use Drizzle.
|
|
27
|
+
*
|
|
28
|
+
* @example module.exports = knexConfig(config);
|
|
29
|
+
*/
|
|
30
|
+
function knexConfig(env) {
|
|
31
|
+
return (0, pool_js_1.createKnexConfig)(connectionOptions(env));
|
|
32
|
+
}
|
|
33
|
+
/** Maps the validated `DB_*` env to connection options. */
|
|
34
|
+
function connectionOptions(env) {
|
|
35
|
+
return { host: env.DB_HOST, port: env.DB_PORT, user: env.DB_USER, password: env.DB_PASSWORD, database: env.DB_NAME, poolMax: env.DB_POOL_MAX };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/database/db.ts"],"names":[],"mappings":";;AAyCA,4BASC;AAQD,gCAEC;AAGD,8CAEC;AAhED,+CAAkE;AAIlE,uCAAsG;AA8BtG;;;;;GAKG;AACH,SAAgB,QAAQ,CAA0C,OAAiC;IACjG,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAChC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAA,yBAAe,EAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,MAAM,EAAE,GAAG,IAAA,gBAAO,EAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAsB,CAAC;IAC3E,OAAO;QACL,EAAE;QACF,IAAI,EAAE,GAAG,EAAE,CAAC,IAAA,mBAAS,EAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;KACxB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,UAAU,CAAC,GAAgB;IACzC,OAAO,IAAA,0BAAgB,EAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,2DAA2D;AAC3D,SAAgB,iBAAiB,CAAC,GAAgB;IAChD,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;AACjJ,CAAC"}
|
|
@@ -1,57 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
4
15
|
};
|
|
5
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createMysqlPool = createMysqlPool;
|
|
7
|
-
exports.createKnexConfig = createKnexConfig;
|
|
8
|
-
exports.pingMysql = pingMysql;
|
|
9
|
-
exports.escapeLike = escapeLike;
|
|
10
|
-
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
11
17
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
18
|
+
* `@heavybit/shared-lib/database`: Drizzle data access for services. Repositories use Drizzle
|
|
19
|
+
* through {@link ScopedRepository} and the scopes in `scopes.ts`; raw scoped queries and Knex in
|
|
20
|
+
* `src/` are forbidden. Knex is for migrations and seeds only (`knexConfig`, `@heavybit/shared-lib/knex`).
|
|
14
21
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
database: options.database,
|
|
22
|
-
connectionLimit: options.poolMax ?? 10,
|
|
23
|
-
waitForConnections: true,
|
|
24
|
-
enableKeepAlive: true,
|
|
25
|
-
timezone: 'Z',
|
|
26
|
-
charset: 'utf8mb4_unicode_ci',
|
|
27
|
-
dateStrings: false,
|
|
28
|
-
supportBigNumbers: true,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
/** Knex config for migrations/seeds and query-builder use (same session settings as the pool). */
|
|
32
|
-
function createKnexConfig(options) {
|
|
33
|
-
return {
|
|
34
|
-
client: 'mysql2',
|
|
35
|
-
connection: {
|
|
36
|
-
host: options.host,
|
|
37
|
-
port: options.port,
|
|
38
|
-
user: options.user,
|
|
39
|
-
password: options.password,
|
|
40
|
-
database: options.database,
|
|
41
|
-
timezone: 'Z',
|
|
42
|
-
charset: 'utf8mb4',
|
|
43
|
-
},
|
|
44
|
-
pool: { min: 0, max: options.poolMax ?? 10 },
|
|
45
|
-
migrations: { directory: options.migrationsDir ?? './migrations', tableName: 'knex_migrations', loadExtensions: ['.cjs', '.js'] },
|
|
46
|
-
seeds: { directory: options.seedsDir ?? './seeds', loadExtensions: ['.cjs', '.js'] },
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
/** Readiness probe helper. */
|
|
50
|
-
async function pingMysql(pool) {
|
|
51
|
-
await pool.query('SELECT 1');
|
|
52
|
-
}
|
|
53
|
-
/** Escapes `%`, `_` and `\` for use inside a LIKE pattern. */
|
|
54
|
-
function escapeLike(value) {
|
|
55
|
-
return value.replace(/[\\%_]/g, (ch) => `\\${ch}`);
|
|
56
|
-
}
|
|
22
|
+
__exportStar(require("./pool.js"), exports);
|
|
23
|
+
__exportStar(require("./db.js"), exports);
|
|
24
|
+
__exportStar(require("./scopes.js"), exports);
|
|
25
|
+
__exportStar(require("./scoped-repository.js"), exports);
|
|
26
|
+
__exportStar(require("./pagination.js"), exports);
|
|
27
|
+
__exportStar(require("./tables.js"), exports);
|
|
57
28
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/database/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/database/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;GAIG;AACH,4CAA0B;AAC1B,0CAAwB;AACxB,8CAA4B;AAC5B,yDAAuC;AACvC,kDAAgC;AAChC,8CAA4B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cursorQuerySchema = exports.DEFAULT_PAGE_LIMIT = exports.MAX_PAGE_LIMIT = void 0;
|
|
4
|
+
exports.parseCursorQuery = parseCursorQuery;
|
|
5
|
+
exports.encodeCursor = encodeCursor;
|
|
6
|
+
exports.decodeCursor = decodeCursor;
|
|
7
|
+
exports.buildCursorPage = buildCursorPage;
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const errors_js_1 = require("../common/errors.js");
|
|
10
|
+
const response_js_1 = require("../common/response.js");
|
|
11
|
+
const index_js_1 = require("../validation/index.js");
|
|
12
|
+
/**
|
|
13
|
+
* Cursor (keyset) pagination (D6). Clients send `?cursor=&limit=`; the cursor is an opaque
|
|
14
|
+
* base64url token of `{ sortKey, id }` (the last row's sort value plus an id tie-breaker).
|
|
15
|
+
* Dependency-light: usable from controllers without the database subpath.
|
|
16
|
+
*/
|
|
17
|
+
/** Largest page size a client may ask for. */
|
|
18
|
+
exports.MAX_PAGE_LIMIT = 100;
|
|
19
|
+
/** Page size when the client sends none. */
|
|
20
|
+
exports.DEFAULT_PAGE_LIMIT = 20;
|
|
21
|
+
const MAX_CURSOR_LENGTH = 512;
|
|
22
|
+
/** `{ cursor?, limit }` query schema: limit 1–100, default 20; unknown keys are stripped. */
|
|
23
|
+
exports.cursorQuerySchema = zod_1.z.object({
|
|
24
|
+
cursor: zod_1.z.string().trim().min(1).max(MAX_CURSOR_LENGTH).optional(),
|
|
25
|
+
limit: zod_1.z.coerce.number().int().min(1).max(exports.MAX_PAGE_LIMIT).default(exports.DEFAULT_PAGE_LIMIT),
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Validates `{ cursor, limit }` from a query string.
|
|
29
|
+
*
|
|
30
|
+
* @throws ValidationError (422) with field errors when invalid.
|
|
31
|
+
* @example const { cursor, limit } = parseCursorQuery(req.query);
|
|
32
|
+
*/
|
|
33
|
+
function parseCursorQuery(input) {
|
|
34
|
+
const result = exports.cursorQuerySchema.safeParse({ cursor: emptyToUndefined(input?.cursor), limit: emptyToUndefined(input?.limit) });
|
|
35
|
+
if (result.success)
|
|
36
|
+
return result.data;
|
|
37
|
+
throw new errors_js_1.ValidationError((0, index_js_1.zodIssuesToErrors)(result.error), 'Invalid pagination parameters');
|
|
38
|
+
}
|
|
39
|
+
function emptyToUndefined(value) {
|
|
40
|
+
return value === '' || value === null ? undefined : value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Encodes a cursor. Date sort keys become ISO strings.
|
|
44
|
+
*
|
|
45
|
+
* @example encodeCursor({ sortKey: row.createdAt, id: row.id })
|
|
46
|
+
*/
|
|
47
|
+
function encodeCursor(cursor) {
|
|
48
|
+
const sortKey = cursor.sortKey instanceof Date ? cursor.sortKey.toISOString() : cursor.sortKey;
|
|
49
|
+
return Buffer.from(JSON.stringify({ k: sortKey, i: cursor.id }), 'utf8').toString('base64url');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Decodes a cursor produced by {@link encodeCursor}.
|
|
53
|
+
*
|
|
54
|
+
* @throws ValidationError (422, field `cursor`) for anything malformed or tampered with.
|
|
55
|
+
*/
|
|
56
|
+
function decodeCursor(token) {
|
|
57
|
+
const invalid = () => new errors_js_1.ValidationError({ cursor: ['Invalid cursor'] }, 'Invalid cursor');
|
|
58
|
+
if (typeof token !== 'string' || token.length === 0 || token.length > MAX_CURSOR_LENGTH || !/^[A-Za-z0-9_-]+$/.test(token))
|
|
59
|
+
throw invalid();
|
|
60
|
+
let parsed;
|
|
61
|
+
try {
|
|
62
|
+
parsed = JSON.parse(Buffer.from(token, 'base64url').toString('utf8'));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw invalid();
|
|
66
|
+
}
|
|
67
|
+
if (!parsed || typeof parsed !== 'object')
|
|
68
|
+
throw invalid();
|
|
69
|
+
const { k, i } = parsed;
|
|
70
|
+
const validKey = k === null || typeof k === 'string' || (typeof k === 'number' && Number.isFinite(k));
|
|
71
|
+
if (!validKey || typeof i !== 'string' || i.length === 0 || i.length > 64)
|
|
72
|
+
throw invalid();
|
|
73
|
+
return { sortKey: k, id: i };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Builds a page from `limit + 1` fetched rows: the extra row only signals that more exist.
|
|
77
|
+
*
|
|
78
|
+
* @param rows Rows fetched with `limit + 1`.
|
|
79
|
+
* @param limit The page size.
|
|
80
|
+
* @param cursorOf Extracts `{ sortKey, id }` from a row for the next cursor.
|
|
81
|
+
*/
|
|
82
|
+
function buildCursorPage(rows, limit, cursorOf) {
|
|
83
|
+
const data = rows.slice(0, limit);
|
|
84
|
+
const hasMore = rows.length > limit && data.length > 0;
|
|
85
|
+
const nextCursor = hasMore ? encodeCursor(cursorOf(data[data.length - 1])) : null;
|
|
86
|
+
return { data, meta: (0, response_js_1.buildCursorPageMeta)(limit, nextCursor) };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../src/database/pagination.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createMysqlPool = createMysqlPool;
|
|
7
|
+
exports.createKnexConfig = createKnexConfig;
|
|
8
|
+
exports.pingMysql = pingMysql;
|
|
9
|
+
exports.escapeLike = escapeLike;
|
|
10
|
+
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
11
|
+
/**
|
|
12
|
+
* mysql2 pool for Drizzle. Sessions run in UTC and utf8mb4 so timestamps and emoji round-trip
|
|
13
|
+
* unchanged regardless of server defaults.
|
|
14
|
+
*/
|
|
15
|
+
function createMysqlPool(options) {
|
|
16
|
+
return promise_1.default.createPool({
|
|
17
|
+
host: options.host,
|
|
18
|
+
port: options.port,
|
|
19
|
+
user: options.user,
|
|
20
|
+
password: options.password,
|
|
21
|
+
database: options.database,
|
|
22
|
+
connectionLimit: options.poolMax ?? 10,
|
|
23
|
+
waitForConnections: true,
|
|
24
|
+
enableKeepAlive: true,
|
|
25
|
+
timezone: '+00:00',
|
|
26
|
+
charset: 'utf8mb4_unicode_ci',
|
|
27
|
+
dateStrings: false,
|
|
28
|
+
supportBigNumbers: true,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Knex config for migrations and seeds: mysql2, utf8mb4, UTC, `.cjs` files only. Services call it
|
|
33
|
+
* through `knexConfig(env)` in their `knexfile.js`.
|
|
34
|
+
*/
|
|
35
|
+
function createKnexConfig(options) {
|
|
36
|
+
return {
|
|
37
|
+
client: 'mysql2',
|
|
38
|
+
connection: {
|
|
39
|
+
host: options.host,
|
|
40
|
+
port: options.port,
|
|
41
|
+
user: options.user,
|
|
42
|
+
password: options.password,
|
|
43
|
+
database: options.database,
|
|
44
|
+
timezone: '+00:00',
|
|
45
|
+
charset: 'utf8mb4',
|
|
46
|
+
},
|
|
47
|
+
pool: { min: 0, max: options.poolMax ?? 10 },
|
|
48
|
+
migrations: { directory: options.migrationsDir ?? './migrations', tableName: 'knex_migrations', extension: 'cjs', loadExtensions: ['.cjs'] },
|
|
49
|
+
seeds: { directory: options.seedsDir ?? './seeds', extension: 'cjs', loadExtensions: ['.cjs'] },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Readiness probe helper. */
|
|
53
|
+
async function pingMysql(pool) {
|
|
54
|
+
await pool.query('SELECT 1');
|
|
55
|
+
}
|
|
56
|
+
/** Escapes `%`, `_` and `\` for use inside a LIKE pattern. */
|
|
57
|
+
function escapeLike(value) {
|
|
58
|
+
return value.replace(/[\\%_]/g, (ch) => `\\${ch}`);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../src/database/pool.ts"],"names":[],"mappings":";;;;;AA+BA,0CAeC;AAMD,4CAgBC;AAGD,8BAEC;AAGD,gCAEC;AA7ED,6DAAmC;AA0BnC;;;GAGG;AACH,SAAgB,eAAe,CAAC,OAA+B;IAC7D,OAAO,iBAAK,CAAC,UAAU,CAAC;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,eAAe,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;QACtC,kBAAkB,EAAE,IAAI;QACxB,eAAe,EAAE,IAAI;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,oBAAoB;QAC7B,WAAW,EAAE,KAAK;QAClB,iBAAiB,EAAE,IAAI;KACxB,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,OAA0B;IACzD,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE;YACV,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,QAAQ;YAClB,OAAO,EAAE,SAAS;SACnB;QACD,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE;QAC5C,UAAU,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,aAAa,IAAI,cAAc,EAAE,SAAS,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,MAAM,CAAC,EAAE;QAC5I,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,MAAM,CAAC,EAAE;KAChG,CAAC;AACJ,CAAC;AAED,8BAA8B;AACvB,KAAK,UAAU,SAAS,CAAC,IAAgB;IAC9C,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC/B,CAAC;AAED,8DAA8D;AAC9D,SAAgB,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScopedRepository = void 0;
|
|
4
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
5
|
+
const errors_js_1 = require("../common/errors.js");
|
|
6
|
+
const pagination_js_1 = require("./pagination.js");
|
|
7
|
+
const scopes_js_1 = require("./scopes.js");
|
|
8
|
+
/**
|
|
9
|
+
* Base for every repository over scoped data (Drizzle). Every read, count, update and delete is
|
|
10
|
+
* AND-ed with the scope's condition, and every insert is stamped with the scope value; there is no
|
|
11
|
+
* method that returns an unscoped query. Subclasses add queries only through {@link scoped} and
|
|
12
|
+
* {@link executor}.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* class NoteRepository extends ScopedRepository<typeof notes, ScopeContext<'workspaceId'>> {
|
|
16
|
+
* findByTitle(ctx: ScopeContext<'workspaceId'>, title: string) {
|
|
17
|
+
* return this.executor().select().from(this.table).where(this.scoped(ctx, eq(this.table.title, title)));
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* const repo = new NoteRepository(db, notes, workspaceScope);
|
|
21
|
+
*/
|
|
22
|
+
class ScopedRepository {
|
|
23
|
+
#db;
|
|
24
|
+
table;
|
|
25
|
+
scope;
|
|
26
|
+
idColumn;
|
|
27
|
+
constructor(db, table, scope, options = {}) {
|
|
28
|
+
this.#db = db;
|
|
29
|
+
this.table = table;
|
|
30
|
+
this.scope = scope;
|
|
31
|
+
this.idColumn = (0, scopes_js_1.resolveColumn)(table, options.idColumn ?? 'id').column;
|
|
32
|
+
(0, scopes_js_1.resolveColumn)(table, scope.column); // fail fast when the table lacks the scope column
|
|
33
|
+
}
|
|
34
|
+
/** The row with `id` inside the caller's scope, or null (absent and out-of-scope look the same). */
|
|
35
|
+
async findById(ctx, id, options = {}) {
|
|
36
|
+
const rows = await this.executor(options.tx).select().from(this.table)
|
|
37
|
+
.where(this.scoped(ctx, (0, drizzle_orm_1.eq)(this.idColumn, id))).limit(1);
|
|
38
|
+
return rows[0] ?? null;
|
|
39
|
+
}
|
|
40
|
+
/** Rows inside the caller's scope, keyset-paginated by `orderBy` then id. */
|
|
41
|
+
async findMany(ctx, options = {}) {
|
|
42
|
+
return this.selectPage(ctx, options, clampLimit(options.limit));
|
|
43
|
+
}
|
|
44
|
+
/** Like {@link findMany}, returning `{ data, meta: { limit, nextCursor, hasMore } }`. */
|
|
45
|
+
async findPage(ctx, options = {}) {
|
|
46
|
+
const limit = clampLimit(options.limit);
|
|
47
|
+
const rows = await this.selectPage(ctx, options, limit + 1);
|
|
48
|
+
const { key: sortKey } = (0, scopes_js_1.resolveColumn)(this.table, options.orderBy?.column ?? this.idColumn.name);
|
|
49
|
+
const { key: idKey } = (0, scopes_js_1.resolveColumn)(this.table, this.idColumn.name);
|
|
50
|
+
return (0, pagination_js_1.buildCursorPage)(rows, limit, (row) => {
|
|
51
|
+
const record = row;
|
|
52
|
+
return { sortKey: record[sortKey], id: String(record[idKey]) };
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Number of rows inside the caller's scope matching `where`. */
|
|
56
|
+
async count(ctx, where, options = {}) {
|
|
57
|
+
const rows = await this.executor(options.tx).select({ n: (0, drizzle_orm_1.count)() }).from(this.table).where(this.scoped(ctx, where));
|
|
58
|
+
return Number(rows[0]?.n ?? 0);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Inserts rows stamped with the caller's scope value.
|
|
62
|
+
*
|
|
63
|
+
* @throws ForbiddenError (`SCOPE_MISMATCH`) when a row names another scope.
|
|
64
|
+
* @returns The stamped rows as written.
|
|
65
|
+
*/
|
|
66
|
+
async insert(ctx, rows, options = {}) {
|
|
67
|
+
const list = (Array.isArray(rows) ? rows : [rows]).map((row) => this.scope.stamp(this.table, row, ctx));
|
|
68
|
+
if (list.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
await this.executor(options.tx).insert(this.table).values(list);
|
|
71
|
+
return list;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Updates rows inside the caller's scope. Rows never move between scopes.
|
|
75
|
+
*
|
|
76
|
+
* @throws ForbiddenError (`SCOPE_IMMUTABLE`) when `values` sets the scope column.
|
|
77
|
+
* @returns Number of rows changed.
|
|
78
|
+
*/
|
|
79
|
+
async update(ctx, where, values, options = {}) {
|
|
80
|
+
if ((0, scopes_js_1.resolveColumn)(this.table, this.scope.column).key in values) {
|
|
81
|
+
throw new errors_js_1.ForbiddenError(`The ${this.scope.name} of a row cannot be changed`, { code: 'SCOPE_IMMUTABLE' });
|
|
82
|
+
}
|
|
83
|
+
const result = await this.executor(options.tx).update(this.table).set(values).where(this.scoped(ctx, where));
|
|
84
|
+
return affectedRows(result);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Deletes rows inside the caller's scope.
|
|
88
|
+
*
|
|
89
|
+
* @returns Number of rows deleted.
|
|
90
|
+
*/
|
|
91
|
+
async delete(ctx, where, options = {}) {
|
|
92
|
+
const result = await this.executor(options.tx).delete(this.table).where(this.scoped(ctx, where));
|
|
93
|
+
return affectedRows(result);
|
|
94
|
+
}
|
|
95
|
+
/** The scope condition AND-ed with `conditions`. The only way subclasses may build a WHERE. */
|
|
96
|
+
scoped(ctx, ...conditions) {
|
|
97
|
+
return (0, drizzle_orm_1.and)(this.scope.condition(this.table, ctx), ...conditions);
|
|
98
|
+
}
|
|
99
|
+
/** The executor for a call: `tx` when given, otherwise the pool. Always pair with {@link scoped}. */
|
|
100
|
+
executor(tx) {
|
|
101
|
+
return tx ?? this.#db;
|
|
102
|
+
}
|
|
103
|
+
async selectPage(ctx, options, take) {
|
|
104
|
+
const sort = (0, scopes_js_1.resolveColumn)(this.table, options.orderBy?.column ?? this.idColumn.name).column;
|
|
105
|
+
const descending = (options.orderBy?.direction ?? 'desc') === 'desc';
|
|
106
|
+
const after = options.cursor ? this.afterCursor(options.cursor, sort, descending) : undefined;
|
|
107
|
+
const order = descending ? [(0, drizzle_orm_1.desc)(sort), (0, drizzle_orm_1.desc)(this.idColumn)] : [(0, drizzle_orm_1.asc)(sort), (0, drizzle_orm_1.asc)(this.idColumn)];
|
|
108
|
+
const rows = await this.executor(options.tx).select().from(this.table)
|
|
109
|
+
.where(this.scoped(ctx, options.where, after)).orderBy(...order).limit(take);
|
|
110
|
+
return rows;
|
|
111
|
+
}
|
|
112
|
+
/** Keyset condition: rows strictly after the cursor's (sortKey, id) in the sort direction. */
|
|
113
|
+
afterCursor(token, sort, descending) {
|
|
114
|
+
const { sortKey, id } = (0, pagination_js_1.decodeCursor)(token);
|
|
115
|
+
const past = descending ? drizzle_orm_1.lt : drizzle_orm_1.gt;
|
|
116
|
+
if (sort === this.idColumn)
|
|
117
|
+
return past(this.idColumn, id);
|
|
118
|
+
const value = sort.dataType === 'date' && typeof sortKey === 'string' ? new Date(sortKey) : sortKey;
|
|
119
|
+
return (0, drizzle_orm_1.or)(past(sort, value), (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(sort, value), past(this.idColumn, id)));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.ScopedRepository = ScopedRepository;
|
|
123
|
+
function clampLimit(limit) {
|
|
124
|
+
if (limit === undefined || !Number.isFinite(limit))
|
|
125
|
+
return pagination_js_1.DEFAULT_PAGE_LIMIT;
|
|
126
|
+
return Math.min(pagination_js_1.MAX_PAGE_LIMIT, Math.max(1, Math.trunc(limit)));
|
|
127
|
+
}
|
|
128
|
+
function affectedRows(result) {
|
|
129
|
+
const header = (Array.isArray(result) ? result[0] : result);
|
|
130
|
+
return Number(header?.affectedRows ?? 0);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=scoped-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-repository.js","sourceRoot":"","sources":["../../../src/database/scoped-repository.ts"],"names":[],"mappings":"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"}
|