@pipeline-builder/pipeline-data 3.4.113 → 3.4.115
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/lib/api/crud-service.d.ts +79 -0
- package/lib/api/crud-service.js +183 -3
- package/lib/api/soft-delete-sweep.d.ts +65 -0
- package/lib/api/soft-delete-sweep.js +114 -0
- package/lib/database/drizzle-schema.d.ts +153 -0
- package/lib/database/schema/alert.d.ts +34 -0
- package/lib/database/schema/alert.js +11 -1
- package/lib/database/schema/compliance.d.ts +34 -0
- package/lib/database/schema/compliance.js +9 -1
- package/lib/database/schema/dashboard.d.ts +17 -0
- package/lib/database/schema/dashboard.js +11 -2
- package/lib/database/schema/message.d.ts +17 -0
- package/lib/database/schema/message.js +7 -1
- package/lib/database/schema/pipeline-template.d.ts +17 -0
- package/lib/database/schema/pipeline-template.js +7 -1
- package/lib/database/schema/pipeline.d.ts +17 -0
- package/lib/database/schema/pipeline.js +7 -1
- package/lib/database/schema/plugin.d.ts +17 -0
- package/lib/database/schema/plugin.js +6 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -1
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SQL } from 'drizzle-orm';
|
|
2
2
|
import type { AnyColumn } from 'drizzle-orm/column';
|
|
3
3
|
import type { PgTable } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { withTenantTx } from '../database/tenancy.js';
|
|
4
5
|
/**
|
|
5
6
|
* Cast Drizzle query results to a typed array.
|
|
6
7
|
* Drizzle's generic return type (`PgSelectBase<...>`) doesn't narrow to our
|
|
@@ -12,6 +13,10 @@ export declare function drizzleRows<T>(rows: unknown): T[];
|
|
|
12
13
|
export declare function drizzleCount(rows: unknown): [{
|
|
13
14
|
count: number;
|
|
14
15
|
}];
|
|
16
|
+
/** The Drizzle transaction object handed to `withTenantTx` callbacks. Passed to
|
|
17
|
+
* `onBeforePurge` so a subclass's dependent teardown runs in the SAME purge
|
|
18
|
+
* transaction (atomic with the parent DELETE), not a fresh connection. */
|
|
19
|
+
export type CrudTx = Parameters<Parameters<typeof withTenantTx>[0]>[0];
|
|
15
20
|
/**
|
|
16
21
|
* Base interface for entities with common fields
|
|
17
22
|
*/
|
|
@@ -53,6 +58,11 @@ export interface PaginatedResult<T> {
|
|
|
53
58
|
/** Cursor pointing to the last item, for cursor-based pagination. */
|
|
54
59
|
nextCursor?: string;
|
|
55
60
|
}
|
|
61
|
+
/** The shared soft-delete retention window in ms (`SOFT_DELETE_RETENTION_DAYS`,
|
|
62
|
+
* default 30d; 0 disables purge-deadline stamping). Exposed so non-CrudService
|
|
63
|
+
* soft-delete paths (e.g. platform's hand-rolled dashboard/alert services)
|
|
64
|
+
* stamp `purge_after` with the SAME window the CrudService entities use. */
|
|
65
|
+
export declare function softDeleteRetentionMs(): number;
|
|
56
66
|
export declare abstract class CrudService<TEntity extends BaseEntity, TFilter, TInsert, TUpdate> {
|
|
57
67
|
/** Drizzle schema table for this entity */
|
|
58
68
|
protected abstract get schema(): PgTable;
|
|
@@ -110,6 +120,27 @@ export declare abstract class CrudService<TEntity extends BaseEntity, TFilter, T
|
|
|
110
120
|
protected onAfterUpdate(_id: string, _entity: TEntity, _userId: string): Promise<void>;
|
|
111
121
|
/** Called after an entity is soft-deleted */
|
|
112
122
|
protected onAfterDelete(_id: string, _entity: TEntity, _userId: string): Promise<void>;
|
|
123
|
+
/** Called after a soft-deleted entity is restored (undo the delete). Override
|
|
124
|
+
* for entities whose restore has side-effects (e.g. re-notify). Best-effort. */
|
|
125
|
+
protected onAfterRestore(_id: string, _entity: TEntity, _userId: string): Promise<void>;
|
|
126
|
+
/** Called with the ids about to be hard-purged, BEFORE the delete, inside the
|
|
127
|
+
* purge transaction (`tx`) — a subclass tearing down dependents that lack ON
|
|
128
|
+
* DELETE CASCADE MUST use `tx` so the teardown is atomic with the parent
|
|
129
|
+
* DELETE (and can't self-deadlock against its row locks). Throwing aborts that
|
|
130
|
+
* batch's purge (rows stay tombstoned, retried next tick). */
|
|
131
|
+
protected onBeforePurge(_ids: string[], _tx: CrudTx): Promise<void>;
|
|
132
|
+
/** Called after rows are hard-purged, for external side-effects (e.g. plugin
|
|
133
|
+
* image GC). Best-effort: errors are logged, never block the sweep. */
|
|
134
|
+
protected onAfterPurge(_ids: string[]): Promise<void>;
|
|
135
|
+
/** Retention before a soft-deleted row becomes purge-eligible. Defaults to the
|
|
136
|
+
* shared `SOFT_DELETE_RETENTION_DAYS`; override per entity for a bespoke window. */
|
|
137
|
+
protected get softDeleteRetentionMs(): number;
|
|
138
|
+
/** The `purge_after` value to stamp on a soft-delete: `now + retention`, or the
|
|
139
|
+
* spread-friendly empty object when the entity has no `purge_after` column.
|
|
140
|
+
* `protected` so subclasses with hand-rolled soft-delete paths (e.g.
|
|
141
|
+
* message-service's thread cascade / sysadmin moderation) stamp it too — an
|
|
142
|
+
* un-stamped tombstone has NULL `purge_after` and is never hard-purged. */
|
|
143
|
+
protected purgeAfterStamp(now: Date): Record<string, unknown>;
|
|
113
144
|
/**
|
|
114
145
|
* Org → team hierarchy: when a read is widened to a parent org (`parentOrgId`
|
|
115
146
|
* set), the parent's rows are outside the caller's RLS scope, so the read must
|
|
@@ -241,4 +272,52 @@ export declare abstract class CrudService<TEntity extends BaseEntity, TFilter, T
|
|
|
241
272
|
* (shared/sysadmin-managed) records. No-op for entities without an
|
|
242
273
|
* `accessModifier` column. Callers pass `!isSystemAdmin(req)`. */
|
|
243
274
|
restrictToPrivate?: boolean): Promise<TEntity[]>;
|
|
275
|
+
/**
|
|
276
|
+
* Restore a soft-deleted (tombstoned) entity: clear `isActive`/`deletedAt`/
|
|
277
|
+
* `deletedBy`/`purgeAfter` so it reappears in normal reads.
|
|
278
|
+
*
|
|
279
|
+
* Matches ONLY a genuine tombstone — `isActive = false` AND `deletedAt IS NOT
|
|
280
|
+
* NULL` — so a merely *deactivated* row (isActive=false, no deletedAt) is never
|
|
281
|
+
* silently "restored". Pinned to the caller's own org (orgId-less sysadmin
|
|
282
|
+
* context spans orgs, matching delete/update). Returns null when there is no
|
|
283
|
+
* such tombstone (already active, purged, or unknown) so the route can 404.
|
|
284
|
+
* A unique-key collision (a live row already holds the key) surfaces as the
|
|
285
|
+
* driver's unique-violation for the route to map to 409 — in practice the
|
|
286
|
+
* tombstone IS the key holder, so this is defensive.
|
|
287
|
+
*/
|
|
288
|
+
/** Conditions matching a GENUINE tombstone (`isActive=false` AND `deletedAt IS
|
|
289
|
+
* NOT NULL`), optionally pinned to `id` and/or `orgId`. Single source of truth
|
|
290
|
+
* for restore/findDeletedById/findDeleted so the "what is a tombstone" rule
|
|
291
|
+
* lives in one place. */
|
|
292
|
+
private tombstoneConditions;
|
|
293
|
+
restore(id: string, orgId: string, userId: string): Promise<TEntity | null>;
|
|
294
|
+
/**
|
|
295
|
+
* Read a soft-deleted entity by id (the inverse of the default `isActive=true`
|
|
296
|
+
* reads) — used by restore routes to load the tombstone for access-control
|
|
297
|
+
* gating before restoring. Returns null unless the row exists AND is a genuine
|
|
298
|
+
* tombstone (`isActive=false` + `deletedAt IS NOT NULL`).
|
|
299
|
+
*/
|
|
300
|
+
findDeletedById(id: string, orgId?: string): Promise<TEntity | null>;
|
|
301
|
+
/**
|
|
302
|
+
* List an org's soft-deleted tombstones (`isActive=false` + `deletedAt IS NOT
|
|
303
|
+
* NULL`), most-recently-deleted first — powers the "recently deleted" restore
|
|
304
|
+
* UI. Returns `[]` for entities without a soft-delete lifecycle. `limit` is
|
|
305
|
+
* clamped to [1, 200] to keep the response bounded (the retention sweep hard-
|
|
306
|
+
* deletes old tombstones anyway, so this is a short, self-limiting list).
|
|
307
|
+
*/
|
|
308
|
+
findDeleted(orgId: string, opts?: {
|
|
309
|
+
limit?: number;
|
|
310
|
+
offset?: number;
|
|
311
|
+
}): Promise<TEntity[]>;
|
|
312
|
+
/**
|
|
313
|
+
* Hard-delete a batch of tombstones whose purge deadline has passed
|
|
314
|
+
* (`deletedAt IS NOT NULL AND purge_after < now`). Batched (`limit`) so a
|
|
315
|
+
* sweep tick stays bounded; the per-service sweep loops until a tick returns
|
|
316
|
+
* `< limit`. Runs across ALL orgs, so callers MUST invoke it from a sysadmin
|
|
317
|
+
* scope (`runSoftDeletePurge` establishes one). `onBeforePurge(ids, tx)` tears
|
|
318
|
+
* down dependents lacking ON DELETE CASCADE inside the same tx; `onAfterPurge`
|
|
319
|
+
* handles external side-effects.
|
|
320
|
+
* Returns the number of rows purged. No-op (0) for entities without the columns.
|
|
321
|
+
*/
|
|
322
|
+
purgeExpired(now: Date, limit?: number): Promise<number>;
|
|
244
323
|
}
|