@manablox/db 0.3.0 → 0.4.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.
@@ -1,9 +1,9 @@
1
- import { R as contents, S as webhooks, _ as index_d_exports, a as ContentRow, c as MembershipRow, d as PushSubscriptionRow, f as RoleRow, g as WorkflowRunRow, h as WorkflowRow, l as MenuItemRow, m as UserRow, p as SpaceRow, r as AssetVariantRow, t as AssetRow, u as MenuRow, x as webhookDeliveries, z as publishedContents } from "./index-rZ24t-Ln.js";
1
+ import { T as webhooks, U as contents, W as publishedContents, _ as UserRow, a as ContentApprovalRow, b as index_d_exports, d as MenuItemRow, f as MenuRow, g as SpaceRow, h as RoleRow, i as AuditEntryRow, l as ContentVersionRow, m as PushSubscriptionRow, p as NotificationRow, r as AssetVariantRow, s as ContentRow, t as AssetRow, u as MembershipRow, v as WorkflowRow, w as webhookDeliveries, y as WorkflowRunRow } from "./index-DrNMGM9N.js";
2
2
  import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
3
  import postgres from "postgres";
4
4
  import { SQL, SQL as SQL$1 } from "drizzle-orm";
5
5
  import { PgColumn, PgColumn as PgColumn$1, PgTable } from "drizzle-orm/pg-core";
6
- import { ContentStatus, ContentTypeDefinition, ContentTypeInput, ContentTypeRegistry, DatabaseConfig, FilterOperator, Loose, WorkflowCursor, WorkflowRunContext, WorkflowRunStatus, WorkflowSelection, WorkflowStep, WorkflowStepLog, WorkflowTrigger } from "@manablox/core";
6
+ import { ApprovalStatus, AuditAction, AuditActor, AuditActorKind, AuditChange, AuditSortField, AuditTargetKind, ContentStatus, ContentTypeDefinition, ContentTypeInput, ContentTypeRegistry, DatabaseConfig, FilterOperator, Loose, NotificationKind, NotificationPreferences, WorkflowCursor, WorkflowRunContext, WorkflowRunStatus, WorkflowSelection, WorkflowStep, WorkflowStepLog, WorkflowTrigger } from "@manablox/core";
7
7
  //#region src/client.d.ts
8
8
  type Database = PostgresJsDatabase<typeof index_d_exports>;
9
9
  /** What `db.transaction((tx) => …)` hands its callback. */
@@ -138,6 +138,8 @@ declare class AssetRepository {
138
138
  create(data: AssetWriteData): Promise<AssetRow>;
139
139
  update(id: string, data: Loose<Pick<AssetWriteData, 'name' | 'alt' | 'title' | 'meta'>>): Promise<AssetRow>;
140
140
  delete(id: string): Promise<AssetRow | null>;
141
+ /** Timestamps an import carries over; `create()` can only stamp the moment of import. */
142
+ restoreTimestamps(id: string, createdAt: Date, updatedAt: Date): Promise<void>;
141
143
  variants(assetIds: string[]): Promise<AssetVariantRow[]>;
142
144
  findVariant(assetId: string, preset: string, format: string): Promise<AssetVariantRow | null>;
143
145
  /** Drops every variant row; the caller removes the files. */
@@ -206,6 +208,92 @@ declare class AssetUsageRepository {
206
208
  }>>;
207
209
  }
208
210
  //#endregion
211
+ //#region src/repositories/audit.d.ts
212
+ interface AuditAppendInput {
213
+ /** The space the action concerns; omit for an instance-wide action. */
214
+ spaceId?: string | null | undefined;
215
+ /** Who did it; the current actor (see `runAsActor`) when omitted. */
216
+ actor?: AuditActor | undefined;
217
+ action: AuditAction;
218
+ targetKind: AuditTargetKind;
219
+ targetId?: string | null | undefined;
220
+ targetLabel?: string | null | undefined;
221
+ changes?: AuditChange[] | undefined;
222
+ meta?: Record<string, unknown> | null | undefined;
223
+ at?: Date | undefined;
224
+ }
225
+ interface AuditFilter {
226
+ /** A space's entries; `null` for instance-wide entries only; absent for every entry. */
227
+ spaceId?: string | null | undefined;
228
+ actorKind?: AuditActorKind | undefined;
229
+ actorId?: string | undefined;
230
+ actions?: AuditAction[] | undefined;
231
+ targetKind?: AuditTargetKind | undefined;
232
+ targetId?: string | undefined;
233
+ from?: Date | undefined;
234
+ to?: Date | undefined;
235
+ /** Matched against the actor's label, the target's label and the action. */
236
+ search?: string | undefined;
237
+ }
238
+ interface AuditSort {
239
+ by: AuditSortField;
240
+ direction: 'asc' | 'desc';
241
+ }
242
+ /** The outcome of walking the chain: where it broke, if it did. */
243
+ interface AuditVerification {
244
+ ok: boolean;
245
+ checked: number;
246
+ /** The first entry whose hash does not match, or whose `prevHash` is not its predecessor's. */
247
+ brokenAt: {
248
+ seq: number;
249
+ id: string;
250
+ reason: 'hash' | 'link';
251
+ } | null;
252
+ }
253
+ interface AuditRepositoryOptions {
254
+ /** Told when `record` could not write; the action itself is not undone. */
255
+ onError?: ((error: unknown, input: AuditAppendInput) => void) | undefined;
256
+ /**
257
+ * Told after every entry has been committed: the realtime feed listens here, because
258
+ * an audit entry is written for every change and only after it has happened. A
259
+ * listener that throws is reported to `onError`; the entry stays written.
260
+ */
261
+ onAppended?: ((row: AuditEntryRow) => void) | undefined;
262
+ }
263
+ /**
264
+ * Appends to and reads the audit log. There is no update and no delete: the table
265
+ * refuses both, so this class offers neither.
266
+ */
267
+ declare class AuditRepository {
268
+ private readonly db;
269
+ private readonly options;
270
+ constructor(db: Database, options?: AuditRepositoryOptions);
271
+ /**
272
+ * Writes one entry, chained to the last. Writers are serialised on a transaction-level
273
+ * advisory lock, so two entries can never both claim the same predecessor; the lock
274
+ * is held for one read and one insert.
275
+ */
276
+ append(input: AuditAppendInput): Promise<AuditEntryRow>;
277
+ private write;
278
+ /**
279
+ * `append` that never throws. The write it describes has already happened; failing
280
+ * the request now would report an error for something that succeeded, so the failure
281
+ * is reported to `onError` (the host logs it) and the caller carries on.
282
+ */
283
+ record(input: AuditAppendInput): Promise<AuditEntryRow | null>;
284
+ findById(id: string): Promise<AuditEntryRow | null>;
285
+ list(filter: AuditFilter, sort?: AuditSort, pagination?: Pagination): Promise<Paginated<AuditEntryRow>>;
286
+ /** The entries about one thing, oldest first: a document's history. */
287
+ forTarget(targetKind: AuditTargetKind, targetId: string, limit?: number): Promise<AuditEntryRow[]>;
288
+ /**
289
+ * Recomputes every hash from the first entry on and checks each link to its
290
+ * predecessor. The chain is one across the instance, so this walks all of it, in
291
+ * batches; on a large log it takes a while and that is the point.
292
+ */
293
+ verify(batchSize?: number): Promise<AuditVerification>;
294
+ count(filter?: AuditFilter): Promise<number>;
295
+ }
296
+ //#endregion
209
297
  //#region src/repositories/content.d.ts
210
298
  interface ContentWriteData {
211
299
  id?: string | undefined;
@@ -328,6 +416,25 @@ declare class ContentRepository {
328
416
  }>>;
329
417
  /** The row exactly as it was at that version — `snapshot()` stores the whole row. */
330
418
  versionSnapshot(contentId: string, version: number): Promise<ContentRow | null>;
419
+ /** Every stored version of these documents, oldest first, for a space export. */
420
+ history(contentIds: string[]): Promise<ContentVersionRow[]>;
421
+ /**
422
+ * Puts an imported document back the way its source had it: the version counter and
423
+ * timestamps `create()` had to invent, and the version history in place of the single
424
+ * snapshot `create()` wrote. The row's own columns are left alone otherwise.
425
+ */
426
+ restoreHistory(id: string, data: {
427
+ version: number;
428
+ createdAt: Date;
429
+ updatedAt: Date;
430
+ versions: Array<{
431
+ version: number;
432
+ label: string | null;
433
+ snapshot: Record<string, unknown>;
434
+ createdAt: Date;
435
+ createdBy: string | null;
436
+ }>;
437
+ }): Promise<void>;
331
438
  private snapshot;
332
439
  private findRow;
333
440
  private lockRow;
@@ -340,6 +447,55 @@ declare function buildTree(rows: Array<ContentRow & {
340
447
  depth: number;
341
448
  }>, rootId: string | null): TreeNode[];
342
449
  //#endregion
450
+ //#region src/repositories/content-approval.d.ts
451
+ interface ApprovalRequestData {
452
+ spaceId: string;
453
+ contentId: string;
454
+ typeId: string;
455
+ requestedBy: string | null;
456
+ requestedByLabel: string;
457
+ requestNote?: string | null | undefined;
458
+ contentVersion?: number | null | undefined;
459
+ }
460
+ interface ApprovalDecisionData {
461
+ status: Exclude<ApprovalStatus, 'pending'>;
462
+ decidedBy: string | null;
463
+ decidedByLabel: string | null;
464
+ decisionNote?: string | null | undefined;
465
+ }
466
+ /** A pending request with the document it concerns, for the reviewers' queue. */
467
+ interface PendingApproval extends ContentApprovalRow {
468
+ content: {
469
+ id: string;
470
+ title: string;
471
+ locale: string;
472
+ typeId: string;
473
+ updatedAt: Date;
474
+ };
475
+ }
476
+ /**
477
+ * The approval history of documents. Rows are appended when someone asks and closed
478
+ * when someone decides; the "one pending per document" rule is the service's, this
479
+ * only offers the reads it needs to keep it.
480
+ */
481
+ declare class ContentApprovalRepository {
482
+ private readonly db;
483
+ constructor(db: Database);
484
+ findById(id: string): Promise<ContentApprovalRow | null>;
485
+ /** The open request on a document, if there is one. */
486
+ pendingFor(contentId: string): Promise<ContentApprovalRow | null>;
487
+ /** The most recent request on a document, whatever became of it. */
488
+ latestFor(contentId: string): Promise<ContentApprovalRow | null>;
489
+ historyFor(contentId: string, limit?: number): Promise<ContentApprovalRow[]>;
490
+ /**
491
+ * Every open request in a space, oldest first, with the document beside it. Narrowed
492
+ * to `typeIds` when the reader may only publish some types; `null` means every type.
493
+ */
494
+ pendingInSpace(spaceId: string, typeIds?: string[] | null): Promise<PendingApproval[]>;
495
+ request(data: ApprovalRequestData): Promise<ContentApprovalRow>;
496
+ decide(id: string, data: ApprovalDecisionData): Promise<ContentApprovalRow>;
497
+ }
498
+ //#endregion
343
499
  //#region src/repositories/content-type.d.ts
344
500
  /**
345
501
  * Persistence for *runtime-defined* content types only. Code-defined types come from
@@ -420,8 +576,49 @@ declare class MenuRepository {
420
576
  resolve(menu: MenuRow, locale: string, published?: boolean): Promise<ResolvedMenuItem[]>;
421
577
  }
422
578
  //#endregion
579
+ //#region src/repositories/notification.d.ts
580
+ interface NotificationInsert {
581
+ userId: string;
582
+ spaceId?: string | null | undefined;
583
+ kind: NotificationKind;
584
+ title: string;
585
+ body?: string | undefined;
586
+ url?: string | null | undefined;
587
+ targetKind?: string | null | undefined;
588
+ targetId?: string | null | undefined;
589
+ actorId?: string | null | undefined;
590
+ actorLabel?: string | null | undefined;
591
+ meta?: Record<string, unknown> | null | undefined;
592
+ }
593
+ interface NotificationFilter {
594
+ unreadOnly?: boolean | undefined;
595
+ kinds?: NotificationKind[] | undefined;
596
+ spaceId?: string | null | undefined;
597
+ }
598
+ /**
599
+ * One person's inbox. Every method is scoped to a `userId`, so a row id from another
600
+ * account is inert: there is no way to read or change what is not yours.
601
+ */
602
+ declare class NotificationRepository {
603
+ private readonly db;
604
+ constructor(db: Database);
605
+ /** Inserts one row per recipient in one statement; an empty list writes nothing. */
606
+ insertMany(rows: NotificationInsert[]): Promise<NotificationRow[]>;
607
+ list(userId: string, filter?: NotificationFilter, pagination?: Pagination): Promise<Paginated<NotificationRow>>;
608
+ findById(userId: string, id: string): Promise<NotificationRow | null>;
609
+ countUnread(userId: string): Promise<number>;
610
+ /** Marks the named rows read; rows already read keep their original time. Returns how many changed. */
611
+ markRead(userId: string, ids: string[]): Promise<number>;
612
+ markUnread(userId: string, ids: string[]): Promise<number>;
613
+ markAllRead(userId: string): Promise<number>;
614
+ delete(userId: string, ids: string[]): Promise<number>;
615
+ deleteRead(userId: string): Promise<number>;
616
+ }
617
+ //#endregion
423
618
  //#region src/repositories/role.d.ts
424
619
  interface RoleWriteData {
620
+ /** Kept when given, so an import can restore a role under the id its memberships name. */
621
+ id?: string | undefined;
425
622
  name: string;
426
623
  machineName: string;
427
624
  description?: string | null;
@@ -489,6 +686,8 @@ declare class UserRepository {
489
686
  constructor(db: Database);
490
687
  findById(id: string): Promise<UserRow | null>;
491
688
  findManyByIds(ids: string[]): Promise<UserRow[]>;
689
+ /** Every account holding an instance role, for a fan-out to the superadmins. */
690
+ findByRole(role: string): Promise<UserRow[]>;
492
691
  findByEmail(email: string): Promise<UserRow | null>;
493
692
  list(pagination: Pagination, search?: string): Promise<Paginated<UserRow>>;
494
693
  /**
@@ -515,6 +714,9 @@ declare class UserRepository {
515
714
  /** Signs the user out everywhere. */
516
715
  revokeSessions(userId: string): Promise<void>;
517
716
  countByRole(role: string): Promise<number>;
717
+ setNotificationPreferences(id: string, preferences: NotificationPreferences): Promise<UserRow>;
718
+ /** The stored hash of the password credential, or `null` for an account without one. */
719
+ passwordHash(userId: string): Promise<string | null>;
518
720
  setRole(id: string, role: string): Promise<UserRow>;
519
721
  /**
520
722
  * Authoritative role plus space memberships in one query.
@@ -548,6 +750,15 @@ declare class UserRepository {
548
750
  //#region src/repositories/webhook.d.ts
549
751
  type WebhookRow = typeof webhooks.$inferSelect;
550
752
  type WebhookDeliveryRow = typeof webhookDeliveries.$inferSelect;
753
+ interface WebhookWriteData {
754
+ id?: string | undefined;
755
+ spaceId: string;
756
+ name: string;
757
+ url: string;
758
+ secret?: string | null | undefined;
759
+ events: string[];
760
+ enabled?: boolean | undefined;
761
+ }
551
762
  interface WebhookDeliveryData {
552
763
  webhookId: string;
553
764
  event: string;
@@ -560,6 +771,8 @@ declare class WebhookRepository {
560
771
  private readonly db;
561
772
  constructor(db: Database);
562
773
  findById(id: string): Promise<WebhookRow | null>;
774
+ listBySpace(spaceId: string): Promise<WebhookRow[]>;
775
+ create(data: WebhookWriteData): Promise<WebhookRow>;
563
776
  /** The switched-on webhooks of a space, for fanning an event out. */
564
777
  findEnabled(spaceId: string): Promise<WebhookRow[]>;
565
778
  recordDelivery(data: WebhookDeliveryData): Promise<WebhookDeliveryRow>;
@@ -652,7 +865,13 @@ interface Repositories {
652
865
  roles: RoleRepository;
653
866
  workflows: WorkflowRepository;
654
867
  webhooks: WebhookRepository;
868
+ audit: AuditRepository;
869
+ notifications: NotificationRepository;
870
+ approvals: ContentApprovalRepository;
871
+ }
872
+ interface RepositoriesOptions {
873
+ audit?: AuditRepositoryOptions | undefined;
655
874
  }
656
- declare function createRepositories(db: Database, registry: ContentTypeRegistry): Repositories;
875
+ declare function createRepositories(db: Database, registry: ContentTypeRegistry, options?: RepositoriesOptions): Repositories;
657
876
  //#endregion
658
- export { AssetRepository as A, buildContentWhere as B, ContentRepository as C, buildTree as D, TreeNode as E, ContentSort as F, Executor as G, Database as H, ContentTable as I, createDatabase as J, Sql as K, FieldFilter as L, Paginated as M, paginate as N, AssetUsageRepository as O, ContentFilter as P, Pagination as R, ContentTypeRepository as S, SQL$1 as T, DatabaseHandle as U, buildOrderBy as V, DatabaseOptions as W, MenuItemInput as _, WorkflowRunCreateData as a, MenuWriteData as b, WebhookDeliveryRow as c, SpaceRole as d, UserCreateData as f, SpaceWriteData as g, SpaceRepository as h, WorkflowRepository as i, AssetWriteData as j, AssetFilter as k, WebhookRepository as l, UserUpdateData as m, createRepositories as n, WorkflowWriteData as o, UserRepository as p, Transaction as q, RUNS_KEPT_PER_WORKFLOW as r, WebhookDeliveryData as s, Repositories as t, WebhookRow as u, MenuItemNode as v, ContentWriteData as w, ResolvedMenuItem as x, MenuRepository as y, PgColumn$1 as z };
877
+ export { Pagination as $, ContentApprovalRepository as A, AuditRepositoryOptions as B, MenuItemNode as C, ContentTypeRepository as D, ResolvedMenuItem as E, TreeNode as F, AssetRepository as G, AuditVerification as H, buildTree as I, paginate as J, AssetWriteData as K, AuditAppendInput as L, ContentRepository as M, ContentWriteData as N, ApprovalDecisionData as O, SQL$1 as P, FieldFilter as Q, AuditFilter as R, MenuItemInput as S, MenuWriteData as T, AssetUsageRepository as U, AuditSort as V, AssetFilter as W, ContentSort as X, ContentFilter as Y, ContentTable as Z, SpaceRepository as _, WorkflowRepository as a, DatabaseOptions as at, NotificationInsert as b, WebhookDeliveryData as c, Transaction as ct, WebhookRow as d, PgColumn$1 as et, WebhookWriteData as f, UserUpdateData as g, UserRepository as h, RUNS_KEPT_PER_WORKFLOW as i, DatabaseHandle as it, PendingApproval as j, ApprovalRequestData as k, WebhookDeliveryRow as l, createDatabase as lt, UserCreateData as m, RepositoriesOptions as n, buildOrderBy as nt, WorkflowRunCreateData as o, Executor as ot, SpaceRole as p, Paginated as q, createRepositories as r, Database as rt, WorkflowWriteData as s, Sql as st, Repositories as t, buildContentWhere as tt, WebhookRepository as u, SpaceWriteData as v, MenuRepository as w, NotificationRepository as x, NotificationFilter as y, AuditRepository as z };