@managemint-solutions/sdk 0.8.0 → 0.16.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.
Files changed (74) hide show
  1. package/README.md +73 -1
  2. package/dist/additional-details/index.d.ts +3 -1
  3. package/dist/additional-details/index.js +15 -28
  4. package/dist/additional-details/index.js.map +1 -1
  5. package/dist/audit/dto.d.ts +13 -0
  6. package/dist/audit/dto.js +90 -0
  7. package/dist/audit/dto.js.map +1 -0
  8. package/dist/audit/index.d.ts +104 -0
  9. package/dist/audit/index.js +308 -0
  10. package/dist/audit/index.js.map +1 -0
  11. package/dist/billing/dto.d.ts +33 -0
  12. package/dist/billing/dto.js +71 -0
  13. package/dist/billing/dto.js.map +1 -0
  14. package/dist/billing/index.d.ts +35 -0
  15. package/dist/billing/index.js +115 -0
  16. package/dist/billing/index.js.map +1 -0
  17. package/dist/client.d.ts +12 -0
  18. package/dist/client.js +23 -7
  19. package/dist/client.js.map +1 -1
  20. package/dist/clients/index.d.ts +3 -1
  21. package/dist/clients/index.js +25 -62
  22. package/dist/clients/index.js.map +1 -1
  23. package/dist/index.d.ts +8 -0
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/nest/index.d.ts +22 -0
  27. package/dist/nest/index.js +28 -1
  28. package/dist/nest/index.js.map +1 -1
  29. package/dist/notes/index.d.ts +3 -1
  30. package/dist/notes/index.js +16 -27
  31. package/dist/notes/index.js.map +1 -1
  32. package/dist/notifications/dto.d.ts +10 -0
  33. package/dist/notifications/dto.js +63 -0
  34. package/dist/notifications/dto.js.map +1 -0
  35. package/dist/notifications/index.d.ts +139 -0
  36. package/dist/notifications/index.js +354 -0
  37. package/dist/notifications/index.js.map +1 -0
  38. package/dist/organizations/dto.d.ts +62 -0
  39. package/dist/organizations/dto.js +445 -0
  40. package/dist/organizations/dto.js.map +1 -0
  41. package/dist/organizations/index.d.ts +72 -0
  42. package/dist/organizations/index.js +221 -0
  43. package/dist/organizations/index.js.map +1 -0
  44. package/dist/permissions/dto.d.ts +27 -0
  45. package/dist/permissions/dto.js +215 -0
  46. package/dist/permissions/dto.js.map +1 -0
  47. package/dist/permissions/index.d.ts +36 -0
  48. package/dist/permissions/index.js +110 -0
  49. package/dist/permissions/index.js.map +1 -0
  50. package/dist/projects/index.d.ts +3 -1
  51. package/dist/projects/index.js +25 -62
  52. package/dist/projects/index.js.map +1 -1
  53. package/dist/service-client/index.d.ts +72 -0
  54. package/dist/service-client/index.js +119 -0
  55. package/dist/service-client/index.js.map +1 -0
  56. package/dist/statuses/index.d.ts +3 -1
  57. package/dist/statuses/index.js +17 -28
  58. package/dist/statuses/index.js.map +1 -1
  59. package/dist/supporting-files/index.d.ts +3 -1
  60. package/dist/supporting-files/index.js +14 -20
  61. package/dist/supporting-files/index.js.map +1 -1
  62. package/dist/tasks/index.d.ts +3 -1
  63. package/dist/tasks/index.js +21 -50
  64. package/dist/tasks/index.js.map +1 -1
  65. package/dist/users/dto.d.ts +48 -0
  66. package/dist/users/dto.js +267 -0
  67. package/dist/users/dto.js.map +1 -0
  68. package/dist/users/index.d.ts +38 -0
  69. package/dist/users/index.js +144 -0
  70. package/dist/users/index.js.map +1 -0
  71. package/dist/webhook-events/index.d.ts +65 -0
  72. package/dist/webhook-events/index.js +109 -0
  73. package/dist/webhook-events/index.js.map +1 -0
  74. package/package.json +2 -2
@@ -0,0 +1,308 @@
1
+ "use strict";
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);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.auditedTable = exports.AuditResource = exports.snapshotChanges = exports.diffChanges = exports.AUDIT_EXCLUDED_COLUMNS = void 0;
18
+ const enum_1 = require("@managemint-solutions/entities/audit-trail/enum");
19
+ const errors_1 = require("../errors");
20
+ __exportStar(require("./dto"), exports);
21
+ /**
22
+ * Bookkeeping columns a diff never reports: identity, tenancy and the who/when stamps of the
23
+ * transition itself (the action already says what happened), plus the `entity_type`/`entity_id`
24
+ * pair, which becomes `related_entity_*` on the audit row instead.
25
+ */
26
+ exports.AUDIT_EXCLUDED_COLUMNS = new Set([
27
+ 'id',
28
+ 'mms_id',
29
+ 'organization_id',
30
+ 'created_at',
31
+ 'created_by',
32
+ 'user_id',
33
+ 'updated_at',
34
+ 'last_updated_by',
35
+ 'archived_at',
36
+ 'archived_by',
37
+ 'unarchived_at',
38
+ 'unarchived_by',
39
+ 'deleted_at',
40
+ 'deleted_by',
41
+ 'restored_at',
42
+ 'restored_by',
43
+ 'entity_type',
44
+ 'entity_id',
45
+ ]);
46
+ /** `undefined` and `null` are the same absent value as far as a row diff is concerned. */
47
+ const normalize = (value) => value ?? null;
48
+ /**
49
+ * The fields that differ between two flat rows, as `{ field: { old, new } }`. Values stay real
50
+ * JSON (booleans stay booleans, jsonb stays an object) and are compared by serialized value, so
51
+ * arrays and objects are equal when their contents are.
52
+ */
53
+ const diffChanges = (before, after) => {
54
+ const changes = {};
55
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
56
+ if (exports.AUDIT_EXCLUDED_COLUMNS.has(key))
57
+ continue;
58
+ const oldValue = normalize(before[key]);
59
+ const newValue = normalize(after[key]);
60
+ if (JSON.stringify(oldValue) === JSON.stringify(newValue))
61
+ continue;
62
+ changes[key] = { old: oldValue, new: newValue };
63
+ }
64
+ return changes;
65
+ };
66
+ exports.diffChanges = diffChanges;
67
+ /**
68
+ * A whole row as a change set — `'new'` for an insert, `'old'` for a hard delete — so the trail
69
+ * stays self-contained once the row is gone. Columns that hold nothing are left out.
70
+ */
71
+ const snapshotChanges = (row, side) => {
72
+ const changes = {};
73
+ for (const [key, value] of Object.entries(row)) {
74
+ if (exports.AUDIT_EXCLUDED_COLUMNS.has(key) || value === null || value === undefined)
75
+ continue;
76
+ changes[key] = side === 'new' ? { old: null, new: value } : { old: value, new: null };
77
+ }
78
+ return changes;
79
+ };
80
+ exports.snapshotChanges = snapshotChanges;
81
+ const AUDIT_TRAIL_SELECT = `
82
+ mms_id,
83
+ created_at,
84
+ action,
85
+ entity_type,
86
+ entity_id,
87
+ created_by:user_profiles!created_by (*),
88
+ related_entity_type,
89
+ related_entity_id,
90
+ changes
91
+ `;
92
+ /**
93
+ * The `audit_logs` table. Reads are the audit trail endpoint; writes go through `record`, which
94
+ * every audited write calls for itself — no feature ever writes an audit line by hand.
95
+ */
96
+ class AuditResource {
97
+ supabase;
98
+ actor;
99
+ onError;
100
+ constructor(supabase, actor, onError) {
101
+ this.supabase = supabase;
102
+ this.actor = actor;
103
+ this.onError = onError;
104
+ }
105
+ /**
106
+ * Writes the audit row(s) — one insert, however many entries. **Never throws**: a logging
107
+ * problem must not fail the business write, so a failure goes to `onAuditError`
108
+ * (default `console.error`) instead.
109
+ */
110
+ async record(entry) {
111
+ const entries = Array.isArray(entry) ? entry : [entry];
112
+ if (entries.length === 0)
113
+ return;
114
+ try {
115
+ const { error } = await this.supabase.from('audit_logs').insert(entries.map((item) => ({
116
+ created_by: this.actor.mms_id,
117
+ organization_id: this.actor.organization_id,
118
+ action: item.action,
119
+ entity_type: item.entityType,
120
+ entity_id: item.entityId,
121
+ related_entity_type: item.relatedEntityType ?? null,
122
+ related_entity_id: item.relatedEntityId ?? null,
123
+ changes: item.changes,
124
+ })));
125
+ if (error)
126
+ throw (0, errors_1.mapPostgrestError)(error);
127
+ }
128
+ catch (error) {
129
+ const handle = this.onError ?? ((thrown, failed) => console.error(thrown, failed));
130
+ handle(error, entries);
131
+ }
132
+ }
133
+ async list(query) {
134
+ const page = query.page;
135
+ const pageSize = query.page_size;
136
+ const offset = (page - 1) * pageSize;
137
+ // `order`/`range` return `this`, so the builder stays a filter builder and the conditional
138
+ // filters below can be reassigned onto it without a cast.
139
+ let auditTrailQuery = this.supabase
140
+ .from('audit_logs')
141
+ .select(AUDIT_TRAIL_SELECT, { count: 'estimated' })
142
+ .eq('organization_id', this.actor.organization_id)
143
+ .order('created_at', { ascending: false })
144
+ .range(offset, offset + pageSize - 1);
145
+ if (query.created_after) {
146
+ auditTrailQuery = auditTrailQuery.gte('created_at', query.created_after);
147
+ }
148
+ if (query.created_before) {
149
+ auditTrailQuery = auditTrailQuery.lte('created_at', query.created_before);
150
+ }
151
+ if (query.created_by) {
152
+ auditTrailQuery = auditTrailQuery.eq('created_by', query.created_by);
153
+ }
154
+ if (query.action) {
155
+ auditTrailQuery = auditTrailQuery.eq('action', query.action);
156
+ }
157
+ if (query.entity_type) {
158
+ auditTrailQuery = auditTrailQuery.eq('entity_type', query.entity_type);
159
+ }
160
+ if (query.entity_id) {
161
+ auditTrailQuery = auditTrailQuery.eq('entity_id', query.entity_id);
162
+ }
163
+ const { data, error, count } = await auditTrailQuery;
164
+ if (error)
165
+ throw (0, errors_1.mapPostgrestError)(error);
166
+ const totalEstimated = count ?? 0;
167
+ const totalPages = totalEstimated > 0 ? Math.ceil(totalEstimated / pageSize) : 0;
168
+ return {
169
+ results: (data ?? []),
170
+ page,
171
+ per_page: pageSize,
172
+ count: totalEstimated,
173
+ total_pages: totalPages,
174
+ has_next_page: page < totalPages,
175
+ has_previous_page: page > 1,
176
+ };
177
+ }
178
+ }
179
+ exports.AuditResource = AuditResource;
180
+ /** The keyed update every audited write builds, before any caller-supplied guard. */
181
+ const buildUpdate = (supabase, table, id, payload, organizationId) => {
182
+ // `eq` returns `this`, so the org filter can be reassigned onto the builder without a cast.
183
+ let query = supabase.from(table).update(payload).eq('mms_id', id);
184
+ if (organizationId !== null)
185
+ query = query.eq('organization_id', organizationId);
186
+ return query;
187
+ };
188
+ /**
189
+ * The audited write helper every resource writes through: it does the pre-read, the write, the
190
+ * diff and the `audit_logs` insert itself, so no feature carries audit code. The write fails
191
+ * loudly (`SupabaseClientError`); only the audit insert is swallowed.
192
+ */
193
+ const auditedTable = (supabase, actor, audit, options) => {
194
+ const scoped = options.scopeByOrganization !== false;
195
+ // Tables that hang off another entity (notes, additional details, supporting files) carry the
196
+ // owner in `entity_type`/`entity_id`; the audit row records it as the related entity.
197
+ const relatedOf = (row) => ({
198
+ relatedEntityType: row.entity_type ?? null,
199
+ relatedEntityId: row.entity_id ?? null,
200
+ });
201
+ const read = async (id) => {
202
+ // `eq` returns `this`, so the org filter can be reassigned onto the builder without a cast.
203
+ let query = supabase.from(options.table).select('*').eq('mms_id', id);
204
+ if (scoped)
205
+ query = query.eq('organization_id', actor.organization_id);
206
+ const { data, error } = await query.single();
207
+ if (error)
208
+ throw (0, errors_1.mapPostgrestError)(error);
209
+ return data;
210
+ };
211
+ const organizationFilter = scoped ? actor.organization_id : null;
212
+ const updateOf = (id, before, payload) => buildUpdate(supabase, options.table, id, typeof payload === 'function' ? payload(before) : payload, organizationFilter);
213
+ const recordWrite = (id, action, before, after) => audit.record({
214
+ action,
215
+ entityType: options.entityType,
216
+ entityId: id,
217
+ changes: (0, exports.diffChanges)(before, after),
218
+ ...relatedOf(before),
219
+ });
220
+ const write = async (id, action, payload) => {
221
+ const before = await read(id);
222
+ const { data, error } = await updateOf(id, before, payload).select('*').single();
223
+ if (error)
224
+ throw (0, errors_1.mapPostgrestError)(error);
225
+ const after = data;
226
+ await recordWrite(id, action, before, after);
227
+ return after;
228
+ };
229
+ const writeWhere = async (id, guard, payload) => {
230
+ const before = await read(id);
231
+ // `maybeSingle`, not `single`: matching nothing is the guard doing its job, not an error.
232
+ const { data, error } = await guard(updateOf(id, before, payload))
233
+ .select('*')
234
+ .maybeSingle();
235
+ if (error)
236
+ throw (0, errors_1.mapPostgrestError)(error);
237
+ // The guard lost the race — the row is untouched, so there is nothing to audit.
238
+ if (!data)
239
+ return null;
240
+ const after = data;
241
+ await recordWrite(id, enum_1.AuditTrailAction.UPDATE, before, after);
242
+ return after;
243
+ };
244
+ return {
245
+ async insert(payload) {
246
+ const { data, error } = await supabase
247
+ .from(options.table)
248
+ .insert(payload)
249
+ .select('*')
250
+ .single();
251
+ if (error)
252
+ throw (0, errors_1.mapPostgrestError)(error);
253
+ const row = data;
254
+ await audit.record({
255
+ action: enum_1.AuditTrailAction.INSERT,
256
+ entityType: options.entityType,
257
+ entityId: row.mms_id,
258
+ changes: (0, exports.snapshotChanges)(row, 'new'),
259
+ ...relatedOf(row),
260
+ });
261
+ return row;
262
+ },
263
+ async insertMany(payloads) {
264
+ if (payloads.length === 0)
265
+ return [];
266
+ const { data, error } = await supabase.from(options.table).insert(payloads).select('*');
267
+ if (error)
268
+ throw (0, errors_1.mapPostgrestError)(error);
269
+ const rows = (data ?? []);
270
+ await audit.record(rows.map((row) => ({
271
+ action: enum_1.AuditTrailAction.INSERT,
272
+ entityType: options.entityType,
273
+ entityId: row.mms_id,
274
+ changes: (0, exports.snapshotChanges)(row, 'new'),
275
+ ...relatedOf(row),
276
+ })));
277
+ return rows;
278
+ },
279
+ update(id, payload) {
280
+ return write(id, enum_1.AuditTrailAction.UPDATE, payload);
281
+ },
282
+ updateWhere(id, guard, payload) {
283
+ return writeWhere(id, guard, payload);
284
+ },
285
+ transition(id, action, payload) {
286
+ return write(id, action, payload);
287
+ },
288
+ async remove(id) {
289
+ const before = await read(id);
290
+ let query = supabase.from(options.table).delete().eq('mms_id', id);
291
+ if (scoped)
292
+ query = query.eq('organization_id', actor.organization_id);
293
+ const { error } = await query;
294
+ if (error)
295
+ throw (0, errors_1.mapPostgrestError)(error);
296
+ await audit.record({
297
+ action: enum_1.AuditTrailAction.DELETE,
298
+ entityType: options.entityType,
299
+ entityId: id,
300
+ changes: (0, exports.snapshotChanges)(before, 'old'),
301
+ ...relatedOf(before),
302
+ });
303
+ return before;
304
+ },
305
+ };
306
+ };
307
+ exports.auditedTable = auditedTable;
308
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The client supplies the idempotency key for every money operation
3
+ * (docs/billing/00-overview.md principle 1). A uuid v4 keeps it unique per
4
+ * (organization, key) without the API having to hand one out first.
5
+ */
6
+ export declare class InitialPaymentDto {
7
+ readonly idempotency_key: string;
8
+ }
9
+ /**
10
+ * Cancellation moves no money, but it is a subscription-level operation and
11
+ * carries a key like every other one, so a client retry after a dropped
12
+ * connection replays the original answer (docs/billing/09-cancellation.md).
13
+ */
14
+ export declare class CancelSubscriptionDto {
15
+ readonly idempotency_key: string;
16
+ }
17
+ /**
18
+ * A reinstatement may or may not move money - the caller cannot know which
19
+ * branch it will get - so the key is always required
20
+ * (docs/billing/10-reinstatement.md).
21
+ */
22
+ export declare class ReinstateSubscriptionDto {
23
+ readonly idempotency_key: string;
24
+ }
25
+ /**
26
+ * A purchase names the module by *id*, which is checked against the active
27
+ * pricebook - never a module key from a hardcoded enum
28
+ * (docs/billing/07-modules-and-pricebooks.md section 2).
29
+ */
30
+ export declare class PurchaseModuleDto {
31
+ readonly module_id: string;
32
+ readonly idempotency_key: string;
33
+ }
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PurchaseModuleDto = exports.ReinstateSubscriptionDto = exports.CancelSubscriptionDto = exports.InitialPaymentDto = void 0;
13
+ const class_validator_1 = require("class-validator");
14
+ /**
15
+ * The client supplies the idempotency key for every money operation
16
+ * (docs/billing/00-overview.md principle 1). A uuid v4 keeps it unique per
17
+ * (organization, key) without the API having to hand one out first.
18
+ */
19
+ class InitialPaymentDto {
20
+ idempotency_key;
21
+ }
22
+ exports.InitialPaymentDto = InitialPaymentDto;
23
+ __decorate([
24
+ (0, class_validator_1.IsUUID)('4', { message: 'An idempotency key is required and must be a v4 uuid' }),
25
+ __metadata("design:type", String)
26
+ ], InitialPaymentDto.prototype, "idempotency_key", void 0);
27
+ /**
28
+ * Cancellation moves no money, but it is a subscription-level operation and
29
+ * carries a key like every other one, so a client retry after a dropped
30
+ * connection replays the original answer (docs/billing/09-cancellation.md).
31
+ */
32
+ class CancelSubscriptionDto {
33
+ idempotency_key;
34
+ }
35
+ exports.CancelSubscriptionDto = CancelSubscriptionDto;
36
+ __decorate([
37
+ (0, class_validator_1.IsUUID)('4', { message: 'An idempotency key is required and must be a v4 uuid' }),
38
+ __metadata("design:type", String)
39
+ ], CancelSubscriptionDto.prototype, "idempotency_key", void 0);
40
+ /**
41
+ * A reinstatement may or may not move money - the caller cannot know which
42
+ * branch it will get - so the key is always required
43
+ * (docs/billing/10-reinstatement.md).
44
+ */
45
+ class ReinstateSubscriptionDto {
46
+ idempotency_key;
47
+ }
48
+ exports.ReinstateSubscriptionDto = ReinstateSubscriptionDto;
49
+ __decorate([
50
+ (0, class_validator_1.IsUUID)('4', { message: 'An idempotency key is required and must be a v4 uuid' }),
51
+ __metadata("design:type", String)
52
+ ], ReinstateSubscriptionDto.prototype, "idempotency_key", void 0);
53
+ /**
54
+ * A purchase names the module by *id*, which is checked against the active
55
+ * pricebook - never a module key from a hardcoded enum
56
+ * (docs/billing/07-modules-and-pricebooks.md section 2).
57
+ */
58
+ class PurchaseModuleDto {
59
+ module_id;
60
+ idempotency_key;
61
+ }
62
+ exports.PurchaseModuleDto = PurchaseModuleDto;
63
+ __decorate([
64
+ (0, class_validator_1.IsUUID)('4', { message: 'A module id is required and must be a v4 uuid' }),
65
+ __metadata("design:type", String)
66
+ ], PurchaseModuleDto.prototype, "module_id", void 0);
67
+ __decorate([
68
+ (0, class_validator_1.IsUUID)('4', { message: 'An idempotency key is required and must be a v4 uuid' }),
69
+ __metadata("design:type", String)
70
+ ], PurchaseModuleDto.prototype, "idempotency_key", void 0);
71
+ //# sourceMappingURL=dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto.js","sourceRoot":"","sources":["../../src/billing/dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAyC;AAEzC;;;;GAIG;AACH,MAAa,iBAAiB;IAEnB,eAAe,CAAU;CACnC;AAHD,8CAGC;AADU;IADR,IAAA,wBAAM,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;;0DAC/C;AAGpC;;;;GAIG;AACH,MAAa,qBAAqB;IAEvB,eAAe,CAAU;CACnC;AAHD,sDAGC;AADU;IADR,IAAA,wBAAM,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;;8DAC/C;AAGpC;;;;GAIG;AACH,MAAa,wBAAwB;IAE1B,eAAe,CAAU;CACnC;AAHD,4DAGC;AADU;IADR,IAAA,wBAAM,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;;iEAC/C;AAGpC;;;;GAIG;AACH,MAAa,iBAAiB;IAEnB,SAAS,CAAU;IAGnB,eAAe,CAAU;CACnC;AAND,8CAMC;AAJU;IADR,IAAA,wBAAM,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC;;oDAC9C;AAGnB;IADR,IAAA,wBAAM,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;;0DAC/C"}
@@ -0,0 +1,35 @@
1
+ import type { BillingTransactionRow, CreateBillingEventDto } from '@managemint-solutions/entities/billing';
2
+ import { type AuditActor, type AuditResource, type AuditRow } from '../audit';
3
+ import type { TypedSupabaseClient } from '../client';
4
+ export * from './dto';
5
+ /**
6
+ * The ledger and its narrative log. Every policy here is `false` for `authenticated` except
7
+ * READ, so this only ever runs on the service client — the ledger is written by the billing
8
+ * service and by webhooks, never by a caller.
9
+ */
10
+ export declare class BillingResource {
11
+ private readonly supabase;
12
+ private readonly actor;
13
+ private readonly transactions;
14
+ constructor(supabase: TypedSupabaseClient, actor: AuditActor, audit: AuditResource);
15
+ createTransaction(payload: AuditRow): Promise<BillingTransactionRow>;
16
+ updateTransaction(transactionId: string, patch: AuditRow): Promise<AuditRow>;
17
+ /**
18
+ * Claims a transaction for processing, but only while it is not already paid. This is the
19
+ * send-exactly-once gate on the webhook path: a redelivered `charge.success` must not settle
20
+ * the same row twice. Resolves to null when the guard finds it already settled.
21
+ */
22
+ claimTransactionForProcessing(transactionId: string, patch: AuditRow): Promise<AuditRow | null>;
23
+ getTransaction(transactionId: string): Promise<BillingTransactionRow | null>;
24
+ /** The one open transaction, if the organization has one. */
25
+ openTransaction(openStatuses: string[]): Promise<BillingTransactionRow | null>;
26
+ /** The replay lookup: the same key must never raise a second charge. */
27
+ transactionByIdempotencyKey(idempotencyKey: string): Promise<BillingTransactionRow | null>;
28
+ /**
29
+ * Appends to `billing_events`. Deliberately unaudited: the table *is* an append-only
30
+ * narrative of the billing state machine, so an audit row per event would log the log.
31
+ */
32
+ recordEvent(event: CreateBillingEventDto): Promise<void>;
33
+ /** Whether a catalog module is still on sale. */
34
+ isModuleActive(moduleId: string): Promise<boolean>;
35
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
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);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.BillingResource = void 0;
18
+ const enum_1 = require("@managemint-solutions/entities/audit-trail/enum");
19
+ const enum_2 = require("@managemint-solutions/entities/billing/enum");
20
+ const audit_1 = require("../audit");
21
+ const errors_1 = require("../errors");
22
+ __exportStar(require("./dto"), exports);
23
+ const BILLING_TRANSACTIONS_TABLE = {
24
+ table: 'billing_transactions',
25
+ entityType: enum_1.AuditTrailEntityType.BILLING_TRANSACTIONS,
26
+ };
27
+ /**
28
+ * The ledger and its narrative log. Every policy here is `false` for `authenticated` except
29
+ * READ, so this only ever runs on the service client — the ledger is written by the billing
30
+ * service and by webhooks, never by a caller.
31
+ */
32
+ class BillingResource {
33
+ supabase;
34
+ actor;
35
+ transactions;
36
+ constructor(supabase, actor, audit) {
37
+ this.supabase = supabase;
38
+ this.actor = actor;
39
+ this.transactions = (0, audit_1.auditedTable)(supabase, actor, audit, BILLING_TRANSACTIONS_TABLE);
40
+ }
41
+ async createTransaction(payload) {
42
+ const row = await this.transactions.insert(payload);
43
+ return row;
44
+ }
45
+ async updateTransaction(transactionId, patch) {
46
+ return this.transactions.update(transactionId, {
47
+ ...patch,
48
+ updated_at: new Date().toISOString(),
49
+ });
50
+ }
51
+ /**
52
+ * Claims a transaction for processing, but only while it is not already paid. This is the
53
+ * send-exactly-once gate on the webhook path: a redelivered `charge.success` must not settle
54
+ * the same row twice. Resolves to null when the guard finds it already settled.
55
+ */
56
+ async claimTransactionForProcessing(transactionId, patch) {
57
+ return this.transactions.updateWhere(transactionId, (query) => query.neq('transaction_status', enum_2.BillingTransactionStatus.PAID), { ...patch, updated_at: new Date().toISOString() });
58
+ }
59
+ async getTransaction(transactionId) {
60
+ const { data, error } = await this.supabase
61
+ .from('billing_transactions')
62
+ .select('*')
63
+ .eq('mms_id', transactionId)
64
+ .maybeSingle();
65
+ if (error)
66
+ throw (0, errors_1.mapPostgrestError)(error);
67
+ return (data ?? null);
68
+ }
69
+ /** The one open transaction, if the organization has one. */
70
+ async openTransaction(openStatuses) {
71
+ const { data, error } = await this.supabase
72
+ .from('billing_transactions')
73
+ .select('*')
74
+ .eq('organization_id', this.actor.organization_id)
75
+ .in('transaction_status', openStatuses)
76
+ .maybeSingle();
77
+ if (error)
78
+ throw (0, errors_1.mapPostgrestError)(error);
79
+ return (data ?? null);
80
+ }
81
+ /** The replay lookup: the same key must never raise a second charge. */
82
+ async transactionByIdempotencyKey(idempotencyKey) {
83
+ const { data, error } = await this.supabase
84
+ .from('billing_transactions')
85
+ .select('*')
86
+ .eq('organization_id', this.actor.organization_id)
87
+ .eq('idempotency_key', idempotencyKey)
88
+ .maybeSingle();
89
+ if (error)
90
+ throw (0, errors_1.mapPostgrestError)(error);
91
+ return (data ?? null);
92
+ }
93
+ /**
94
+ * Appends to `billing_events`. Deliberately unaudited: the table *is* an append-only
95
+ * narrative of the billing state machine, so an audit row per event would log the log.
96
+ */
97
+ async recordEvent(event) {
98
+ const { error } = await this.supabase.from('billing_events').insert(event);
99
+ if (error)
100
+ throw (0, errors_1.mapPostgrestError)(error);
101
+ }
102
+ /** Whether a catalog module is still on sale. */
103
+ async isModuleActive(moduleId) {
104
+ const { data, error } = await this.supabase
105
+ .from('billing_modules')
106
+ .select('active')
107
+ .eq('mms_id', moduleId)
108
+ .maybeSingle();
109
+ if (error)
110
+ throw (0, errors_1.mapPostgrestError)(error);
111
+ return data?.active === true;
112
+ }
113
+ }
114
+ exports.BillingResource = BillingResource;
115
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -1,24 +1,36 @@
1
1
  import { type SupabaseClient as SupabaseJsClient } from '@supabase/supabase-js';
2
2
  import { type AuthContext } from './auth';
3
+ import { AuditResource, type AuditErrorHandler } from './audit';
3
4
  import { AdditionalDetailsResource } from './additional-details';
4
5
  import { ClientsResource } from './clients';
5
6
  import { NotesResource } from './notes';
7
+ import { NotificationsResource } from './notifications';
8
+ import { OrganizationsResource } from './organizations';
9
+ import { PermissionsResource } from './permissions';
6
10
  import { ProjectsResource } from './projects';
7
11
  import { StatusesResource } from './statuses';
8
12
  import { SupportingFilesResource } from './supporting-files';
9
13
  import { TasksResource } from './tasks';
14
+ import { UsersResource } from './users';
10
15
  export type TypedSupabaseClient = SupabaseJsClient;
11
16
  export type SupabaseClientConfig = {
12
17
  url: string;
13
18
  key: string;
14
19
  accessToken: string;
20
+ /** Where a failed audit insert is reported. Defaults to `console.error`. */
21
+ onAuditError?: AuditErrorHandler;
15
22
  };
16
23
  export declare class SupabaseClient {
17
24
  readonly supabase: TypedSupabaseClient;
18
25
  readonly auth: AuthContext;
26
+ readonly audit: AuditResource;
19
27
  readonly statuses: StatusesResource;
20
28
  readonly additionalDetails: AdditionalDetailsResource;
21
29
  readonly notes: NotesResource;
30
+ readonly notifications: NotificationsResource;
31
+ readonly organizations: OrganizationsResource;
32
+ readonly permissions: PermissionsResource;
33
+ readonly users: UsersResource;
22
34
  readonly supportingFiles: SupportingFilesResource;
23
35
  readonly clients: ClientsResource;
24
36
  readonly projects: ProjectsResource;