@ai-matrx/data 0.23.0 → 0.24.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 (73) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +34 -0
  3. package/bin/emit.mjs +32 -7
  4. package/bin/generate.mjs +37 -8
  5. package/bin/matrx-data.mjs +6 -4
  6. package/description/admin.json.gz +0 -0
  7. package/description/agent.json.gz +0 -0
  8. package/description/ai.json.gz +0 -0
  9. package/description/api.json.gz +0 -0
  10. package/description/app.json.gz +0 -0
  11. package/description/assignment.json.gz +0 -0
  12. package/description/audit.json.gz +0 -0
  13. package/description/batch.json.gz +0 -0
  14. package/description/billing.json.gz +0 -0
  15. package/description/browser.json.gz +0 -0
  16. package/description/canvas.json.gz +0 -0
  17. package/description/chat.json.gz +0 -0
  18. package/description/code.json.gz +0 -0
  19. package/description/commerce.json.gz +0 -0
  20. package/description/communication.json.gz +0 -0
  21. package/description/content.json.gz +0 -0
  22. package/description/content_ir.json.gz +0 -0
  23. package/description/context.json.gz +0 -0
  24. package/description/crm.json.gz +0 -0
  25. package/description/custom.json.gz +0 -0
  26. package/description/dictionary.json.gz +0 -0
  27. package/description/docproc.json.gz +0 -0
  28. package/description/education.json.gz +0 -0
  29. package/description/esign.json.gz +0 -0
  30. package/description/extend.json.gz +0 -0
  31. package/description/files.json.gz +0 -0
  32. package/description/growth.json.gz +0 -0
  33. package/description/hindsight.json.gz +0 -0
  34. package/description/history.json.gz +0 -0
  35. package/description/iam.json.gz +0 -0
  36. package/description/index.json.gz +0 -0
  37. package/description/interview.json.gz +0 -0
  38. package/description/knowledge.json.gz +0 -0
  39. package/description/legal.json.gz +0 -0
  40. package/description/mandate.json.gz +0 -0
  41. package/description/marketing.json.gz +0 -0
  42. package/description/media.json.gz +0 -0
  43. package/description/meta.json.gz +0 -0
  44. package/description/ops.json.gz +0 -0
  45. package/description/pdf.json.gz +0 -0
  46. package/description/plan.json.gz +0 -0
  47. package/description/platform.json.gz +0 -0
  48. package/description/podcast.json.gz +0 -0
  49. package/description/projects.json.gz +0 -0
  50. package/description/public.json.gz +0 -0
  51. package/description/rag.json.gz +0 -0
  52. package/description/research.json.gz +0 -0
  53. package/description/runtime.json.gz +0 -0
  54. package/description/scheduler.json.gz +0 -0
  55. package/description/scraper.json.gz +0 -0
  56. package/description/seo.json.gz +0 -0
  57. package/description/skill.json.gz +0 -0
  58. package/description/social.json.gz +0 -0
  59. package/description/tool.json.gz +0 -0
  60. package/description/transcripts.json.gz +0 -0
  61. package/description/ui.json.gz +0 -0
  62. package/description/users.json.gz +0 -0
  63. package/description/web.json.gz +0 -0
  64. package/description/work.json.gz +0 -0
  65. package/description/workbench.json.gz +0 -0
  66. package/description/workflow.json.gz +0 -0
  67. package/dist/db.cjs +62 -28
  68. package/dist/db.cjs.map +1 -1
  69. package/dist/db.d.cts +83 -33
  70. package/dist/db.d.ts +83 -33
  71. package/dist/db.js +62 -28
  72. package/dist/db.js.map +1 -1
  73. package/package.json +5 -4
package/dist/db.d.cts CHANGED
@@ -884,6 +884,7 @@ interface QueryLike extends PromiseLike<Resp<any>> {
884
884
  update(values: unknown): QueryLike;
885
885
  delete(): QueryLike;
886
886
  eq(column: string, value: unknown): QueryLike;
887
+ is(column: string, value: null | boolean): QueryLike;
887
888
  order(column: string, opts?: {
888
889
  ascending?: boolean;
889
890
  }): QueryLike;
@@ -958,74 +959,123 @@ declare class DatabaseError extends Error {
958
959
  type Op = "read" | "insert" | "update" | "remove" | "archive";
959
960
  /** Refuse before the wire when the descriptor's rules do not open this door. */
960
961
  declare function assertDoor(db: Db, table: TableDescriptor<any, any, any>, op: Op): void;
962
+ type Col<R> = Extract<keyof R, string>;
963
+ /** The typed filter a `Where` callback receives — column names and values checked against the row. */
964
+ interface Filter<R> {
965
+ eq<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
966
+ neq<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
967
+ gt<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
968
+ gte<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
969
+ lt<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
970
+ lte<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
971
+ like(column: Col<R>, pattern: string): Filter<R>;
972
+ ilike(column: Col<R>, pattern: string): Filter<R>;
973
+ in<K extends Col<R>>(column: K, values: readonly NonNullable<R[K]>[]): Filter<R>;
974
+ is(column: Col<R>, value: null | boolean): Filter<R>;
975
+ contains(column: Col<R>, value: unknown): Filter<R>;
976
+ overlaps(column: Col<R>, value: unknown): Filter<R>;
977
+ textSearch(column: Col<R>, query: string, opts?: {
978
+ type?: "plain" | "phrase" | "websearch";
979
+ config?: string;
980
+ }): Filter<R>;
981
+ /** PostgREST's raw `or` grammar (`"pinned.eq.true,starred.eq.true"`). */
982
+ or(filters: string, opts?: {
983
+ referencedTable?: string;
984
+ }): Filter<R>;
985
+ not(column: Col<R>, operator: string, value: unknown): Filter<R>;
986
+ }
987
+ /** A filter callback — the same builder `readAllRows` callers write today, typed to the row. */
988
+ type Where<R = any> = (q: Filter<R>) => Filter<R>;
989
+ interface ReadOptions<C> {
990
+ /** Only these columns (the result is typed to them). Default every column. */
991
+ columns?: readonly C[];
992
+ /** Include soft-deleted rows (default: excluded on tables with `deleted_at`). */
993
+ includeDeleted?: boolean;
994
+ }
961
995
  /** One row by primary key (a composite key takes `{ col: value }`), or null. */
962
- declare function read<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown, columns?: string): Promise<RowOf<T> | null>;
963
- /** A filter callback — the same builder `readAllRows` callers write today (`q => q.or(…).gte(…)`). */
964
- type Where = (q: any) => any;
965
- /** EVERY matching row, paged past the 1,000-row cap; throws on a short read. */
966
- declare function listAll<T extends TableDescriptor<any, any, any>>(db: Db, table: T, where?: Where, opts?: {
967
- columns?: string;
968
- orderBy?: string;
996
+ declare function read<R, I, U, C extends Col<R> = Col<R>>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, opts?: ReadOptions<C>): Promise<Pick<R, C> | null>;
997
+ /** EVERY matching row, paged past the 1,000-row cap (ordered by the full key); throws on a short read. */
998
+ declare function listAll<R, I, U, C extends Col<R> = Col<R>>(db: Db, table: TableDescriptor<R, I, U>, where?: Where<R>, opts?: ReadOptions<C> & {
999
+ orderBy?: Col<R>;
969
1000
  pageSize?: number;
970
1001
  maxRows?: number;
971
- }): Promise<RowOf<T>[]>;
1002
+ }): Promise<Pick<R, C>[]>;
972
1003
  /** One explicit window (`from`..`to`, inclusive) plus the exact total. */
973
- declare function page<T extends TableDescriptor<any, any, any>>(db: Db, table: T, window: {
1004
+ declare function page<R, I, U, C extends Col<R> = Col<R>>(db: Db, table: TableDescriptor<R, I, U>, window: ReadOptions<C> & {
974
1005
  from: number;
975
1006
  to: number;
976
- where?: Where;
977
- columns?: string;
978
- orderBy?: string;
1007
+ where?: Where<R>;
1008
+ orderBy?: Col<R>;
979
1009
  ascending?: boolean;
980
1010
  }): Promise<{
981
- rows: RowOf<T>[];
1011
+ rows: Pick<R, C>[];
982
1012
  total: number | null;
983
1013
  }>;
984
1014
  /** The exact count of matching rows (no rows transferred). */
985
- declare function count<T extends TableDescriptor<any, any, any>>(db: Db, table: T, where?: Where): Promise<number>;
1015
+ declare function count<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, where?: Where<R>, opts?: {
1016
+ includeDeleted?: boolean;
1017
+ }): Promise<number>;
986
1018
  /** Insert one row (or many) and return what the database stored. */
987
- declare function insert<T extends TableDescriptor<any, any, any>>(db: Db, table: T, values: InsertOf<T>): Promise<RowOf<T>>;
988
- declare function insert<T extends TableDescriptor<any, any, any>>(db: Db, table: T, values: InsertOf<T>[]): Promise<RowOf<T>[]>;
1019
+ declare function insert<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, values: I): Promise<R>;
1020
+ declare function insert<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, values: readonly I[]): Promise<R[]>;
1021
+ /** Every write on a versioned table is explicit: `{ expectedVersion }` (guarded) or `{ overwrite: true }`. */
989
1022
  type UpdateGuard = {
990
1023
  expectedVersion: number;
991
1024
  } | {
992
1025
  overwrite: true;
993
1026
  };
1027
+ type WriteResult<R> = {
1028
+ status: "saved";
1029
+ row: R;
1030
+ rebasedFrom?: {
1031
+ expectedVersion: number;
1032
+ currentVersion: number;
1033
+ };
1034
+ } | {
1035
+ status: "conflict";
1036
+ currentRow: R;
1037
+ currentVersion: number;
1038
+ } | {
1039
+ status: "not_found";
1040
+ };
994
1041
  /**
995
1042
  * Update one row by primary key. On a versioned table the guard is required:
996
1043
  * `{ expectedVersion }` (a conflict comes back, never a silent overwrite) or
997
- * `{ overwrite: true }`.
1044
+ * `{ overwrite: true }`; on an unversioned table `expectedVersion` is refused.
998
1045
  */
999
- declare function update<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown, patch: UpdateOf<T>, guard?: UpdateGuard): Promise<GuardedUpdateResult<RowOf<T> & {
1000
- version: number;
1001
- }> | {
1002
- status: "saved";
1003
- row: RowOf<T>;
1004
- } | {
1005
- status: "not_found";
1006
- }>;
1046
+ declare function update<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, patch: U, guard?: UpdateGuard): Promise<WriteResult<R>>;
1007
1047
  /**
1008
1048
  * Remove one row the way the table removes: soft delete (`deleted_at`) where it
1009
1049
  * has one; an archive-only table refuses (use `archive`); a hard delete only where
1010
- * the table has neither.
1050
+ * the table has neither. Guarded like `update` on a versioned table.
1011
1051
  */
1012
- declare function remove<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown): Promise<"soft_deleted" | "deleted" | "not_found">;
1013
- /** Archive (or, with `restore`, bring back) one row by the table's archive column. */
1014
- declare function archive<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown, opts?: {
1052
+ declare function remove<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, guard?: UpdateGuard): Promise<{
1053
+ status: "soft_deleted" | "deleted" | "not_found";
1054
+ } | {
1055
+ status: "conflict";
1056
+ currentVersion: number;
1057
+ }>;
1058
+ /** Archive (or, with `restore`, bring back) one row by the table's archive column. Guarded like `update`. */
1059
+ declare function archive<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, opts?: {
1015
1060
  restore?: boolean;
1016
- }): Promise<RowOf<T> | null>;
1061
+ guard?: UpdateGuard;
1062
+ }): Promise<WriteResult<R>>;
1017
1063
  /** Bring back an archived or soft-deleted row. */
1018
- declare const restore: <T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown) => Promise<RowOf<T> | null>;
1064
+ declare const restore: <R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, guard?: UpdateGuard) => Promise<WriteResult<R>>;
1019
1065
  /** Call a database function through its generated door (PostgREST chooses an overload by argument names). */
1020
1066
  declare function call<F extends FunctionDescriptor<any, any>>(db: Db, fn: F, args: ArgsOf<F>): Promise<ResultOf<F>>;
1021
- /** The compact flags index `matrx-data generate --flags-index` writes: "schema.table" → descriptor. */
1067
+ /** One schema's flags (`<schema>.flags.ts` from `matrx-data generate --flags-index`): "schema.table" → descriptor. */
1022
1068
  type FlagsIndex = Readonly<Record<string, TableDescriptor>>;
1069
+ /** Per-schema loaders (`flags-index.ts`): only the schema a runtime name names is loaded (pay-only). */
1070
+ type FlagLoaders = Readonly<Record<string, () => Promise<FlagsIndex>>>;
1023
1071
  /** The door for a table whose name arrives at runtime — loosely typed, the same rules. */
1024
1072
  declare function tableByName(index: FlagsIndex, name: string): TableDescriptor;
1073
+ /** `tableByName` over per-schema loaders: loads only the named schema's flags. */
1074
+ declare function loadTableByName(loaders: FlagLoaders, name: string): Promise<TableDescriptor>;
1025
1075
  /** The `postgres_changes` filter for a table — only published tables have one (an unpublished table delivers nothing). */
1026
1076
  declare function changesOf(table: TableDescriptor<any, any, any>): {
1027
1077
  schema: string;
1028
1078
  table: string;
1029
1079
  };
1030
1080
 
1031
- export { type ActiveOrgCookie, type ActiveOrgCookieOptions, type ActiveOrgSelection, type ArgsOf, type AuthRetryableResult, BulkWriteError, type CreateActiveOrgCookieArgs, type CreateDbOptions, type CreateOAuthRefreshFetchArgs, type CreateSessionRetryOptions, DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE, DEFAULT_ACTIVE_ORG_COOKIE_NAME, DatabaseError, type Db, DoorRefusedError, type FlagsIndex, type FunctionDescriptor, type GuardedUpdateArgs, type GuardedUpdateResult, IncompleteReadError, type InsertOf, type JsonObject, type MergeJsonColumnArgs, type MergeJsonColumnResult, type OAuthRefreshStorage, type OrgRule, type PagedReadQuery, type PagedReadResult, type QueryLike, type ReadAllRowsOptions, type ReadAllRowsRestOptions, RecordUnavailableError, type RecordUnavailableReason, type RecordUnavailableResolution, type ResultOf, type RowOf, type SessionSource, SessionUnavailableError, type SupabaseLike, type TableDescriptor, type TableType, type TryWriteOneResult, type UpdateGuard, type UpdateOf, type UserCredentialsPort, type VersionedRow, type Where, WriteDidNotLandError, type WriteDidNotLandReason, type WriteFailureWords, type WriteOneAction, type WriteOneOptions, type WriteOneRowResult, WriteRefusedError, archive, asJsonObject, assertDoor, assertWriteLanded, call, changesOf, count, createActiveOrgCookie, createDb, createOAuthRefreshFetch, createServiceDb, createSessionRetry, decodeActiveOrgSelection, decodeOAuthRefreshClaims, describeWriteFailure, encodeActiveOrgSelection, guardedUpdate, insert, isMissingSessionError, isRecordUnavailableError, listAll, mergeJsonColumn, page, personSentence, read, readAllRows, readAllRowsRest, recordUnavailableMessage, remove, restore, serverMessageFromBody, tableByName, tryReadAllRows, tryReadAllRowsRest, tryWriteOne, update, writeFailureStatus, writeOne, writeOneRow };
1081
+ export { type ActiveOrgCookie, type ActiveOrgCookieOptions, type ActiveOrgSelection, type ArgsOf, type AuthRetryableResult, BulkWriteError, type CreateActiveOrgCookieArgs, type CreateDbOptions, type CreateOAuthRefreshFetchArgs, type CreateSessionRetryOptions, DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE, DEFAULT_ACTIVE_ORG_COOKIE_NAME, DatabaseError, type Db, DoorRefusedError, type Filter, type FlagLoaders, type FlagsIndex, type FunctionDescriptor, type GuardedUpdateArgs, type GuardedUpdateResult, IncompleteReadError, type InsertOf, type JsonObject, type MergeJsonColumnArgs, type MergeJsonColumnResult, type OAuthRefreshStorage, type OrgRule, type PagedReadQuery, type PagedReadResult, type QueryLike, type ReadAllRowsOptions, type ReadAllRowsRestOptions, type ReadOptions, RecordUnavailableError, type RecordUnavailableReason, type RecordUnavailableResolution, type ResultOf, type RowOf, type SessionSource, SessionUnavailableError, type SupabaseLike, type TableDescriptor, type TableType, type TryWriteOneResult, type UpdateGuard, type UpdateOf, type UserCredentialsPort, type VersionedRow, type Where, WriteDidNotLandError, type WriteDidNotLandReason, type WriteFailureWords, type WriteOneAction, type WriteOneOptions, type WriteOneRowResult, WriteRefusedError, type WriteResult, archive, asJsonObject, assertDoor, assertWriteLanded, call, changesOf, count, createActiveOrgCookie, createDb, createOAuthRefreshFetch, createServiceDb, createSessionRetry, decodeActiveOrgSelection, decodeOAuthRefreshClaims, describeWriteFailure, encodeActiveOrgSelection, guardedUpdate, insert, isMissingSessionError, isRecordUnavailableError, listAll, loadTableByName, mergeJsonColumn, page, personSentence, read, readAllRows, readAllRowsRest, recordUnavailableMessage, remove, restore, serverMessageFromBody, tableByName, tryReadAllRows, tryReadAllRowsRest, tryWriteOne, update, writeFailureStatus, writeOne, writeOneRow };
package/dist/db.d.ts CHANGED
@@ -884,6 +884,7 @@ interface QueryLike extends PromiseLike<Resp<any>> {
884
884
  update(values: unknown): QueryLike;
885
885
  delete(): QueryLike;
886
886
  eq(column: string, value: unknown): QueryLike;
887
+ is(column: string, value: null | boolean): QueryLike;
887
888
  order(column: string, opts?: {
888
889
  ascending?: boolean;
889
890
  }): QueryLike;
@@ -958,74 +959,123 @@ declare class DatabaseError extends Error {
958
959
  type Op = "read" | "insert" | "update" | "remove" | "archive";
959
960
  /** Refuse before the wire when the descriptor's rules do not open this door. */
960
961
  declare function assertDoor(db: Db, table: TableDescriptor<any, any, any>, op: Op): void;
962
+ type Col<R> = Extract<keyof R, string>;
963
+ /** The typed filter a `Where` callback receives — column names and values checked against the row. */
964
+ interface Filter<R> {
965
+ eq<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
966
+ neq<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
967
+ gt<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
968
+ gte<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
969
+ lt<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
970
+ lte<K extends Col<R>>(column: K, value: NonNullable<R[K]>): Filter<R>;
971
+ like(column: Col<R>, pattern: string): Filter<R>;
972
+ ilike(column: Col<R>, pattern: string): Filter<R>;
973
+ in<K extends Col<R>>(column: K, values: readonly NonNullable<R[K]>[]): Filter<R>;
974
+ is(column: Col<R>, value: null | boolean): Filter<R>;
975
+ contains(column: Col<R>, value: unknown): Filter<R>;
976
+ overlaps(column: Col<R>, value: unknown): Filter<R>;
977
+ textSearch(column: Col<R>, query: string, opts?: {
978
+ type?: "plain" | "phrase" | "websearch";
979
+ config?: string;
980
+ }): Filter<R>;
981
+ /** PostgREST's raw `or` grammar (`"pinned.eq.true,starred.eq.true"`). */
982
+ or(filters: string, opts?: {
983
+ referencedTable?: string;
984
+ }): Filter<R>;
985
+ not(column: Col<R>, operator: string, value: unknown): Filter<R>;
986
+ }
987
+ /** A filter callback — the same builder `readAllRows` callers write today, typed to the row. */
988
+ type Where<R = any> = (q: Filter<R>) => Filter<R>;
989
+ interface ReadOptions<C> {
990
+ /** Only these columns (the result is typed to them). Default every column. */
991
+ columns?: readonly C[];
992
+ /** Include soft-deleted rows (default: excluded on tables with `deleted_at`). */
993
+ includeDeleted?: boolean;
994
+ }
961
995
  /** One row by primary key (a composite key takes `{ col: value }`), or null. */
962
- declare function read<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown, columns?: string): Promise<RowOf<T> | null>;
963
- /** A filter callback — the same builder `readAllRows` callers write today (`q => q.or(…).gte(…)`). */
964
- type Where = (q: any) => any;
965
- /** EVERY matching row, paged past the 1,000-row cap; throws on a short read. */
966
- declare function listAll<T extends TableDescriptor<any, any, any>>(db: Db, table: T, where?: Where, opts?: {
967
- columns?: string;
968
- orderBy?: string;
996
+ declare function read<R, I, U, C extends Col<R> = Col<R>>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, opts?: ReadOptions<C>): Promise<Pick<R, C> | null>;
997
+ /** EVERY matching row, paged past the 1,000-row cap (ordered by the full key); throws on a short read. */
998
+ declare function listAll<R, I, U, C extends Col<R> = Col<R>>(db: Db, table: TableDescriptor<R, I, U>, where?: Where<R>, opts?: ReadOptions<C> & {
999
+ orderBy?: Col<R>;
969
1000
  pageSize?: number;
970
1001
  maxRows?: number;
971
- }): Promise<RowOf<T>[]>;
1002
+ }): Promise<Pick<R, C>[]>;
972
1003
  /** One explicit window (`from`..`to`, inclusive) plus the exact total. */
973
- declare function page<T extends TableDescriptor<any, any, any>>(db: Db, table: T, window: {
1004
+ declare function page<R, I, U, C extends Col<R> = Col<R>>(db: Db, table: TableDescriptor<R, I, U>, window: ReadOptions<C> & {
974
1005
  from: number;
975
1006
  to: number;
976
- where?: Where;
977
- columns?: string;
978
- orderBy?: string;
1007
+ where?: Where<R>;
1008
+ orderBy?: Col<R>;
979
1009
  ascending?: boolean;
980
1010
  }): Promise<{
981
- rows: RowOf<T>[];
1011
+ rows: Pick<R, C>[];
982
1012
  total: number | null;
983
1013
  }>;
984
1014
  /** The exact count of matching rows (no rows transferred). */
985
- declare function count<T extends TableDescriptor<any, any, any>>(db: Db, table: T, where?: Where): Promise<number>;
1015
+ declare function count<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, where?: Where<R>, opts?: {
1016
+ includeDeleted?: boolean;
1017
+ }): Promise<number>;
986
1018
  /** Insert one row (or many) and return what the database stored. */
987
- declare function insert<T extends TableDescriptor<any, any, any>>(db: Db, table: T, values: InsertOf<T>): Promise<RowOf<T>>;
988
- declare function insert<T extends TableDescriptor<any, any, any>>(db: Db, table: T, values: InsertOf<T>[]): Promise<RowOf<T>[]>;
1019
+ declare function insert<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, values: I): Promise<R>;
1020
+ declare function insert<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, values: readonly I[]): Promise<R[]>;
1021
+ /** Every write on a versioned table is explicit: `{ expectedVersion }` (guarded) or `{ overwrite: true }`. */
989
1022
  type UpdateGuard = {
990
1023
  expectedVersion: number;
991
1024
  } | {
992
1025
  overwrite: true;
993
1026
  };
1027
+ type WriteResult<R> = {
1028
+ status: "saved";
1029
+ row: R;
1030
+ rebasedFrom?: {
1031
+ expectedVersion: number;
1032
+ currentVersion: number;
1033
+ };
1034
+ } | {
1035
+ status: "conflict";
1036
+ currentRow: R;
1037
+ currentVersion: number;
1038
+ } | {
1039
+ status: "not_found";
1040
+ };
994
1041
  /**
995
1042
  * Update one row by primary key. On a versioned table the guard is required:
996
1043
  * `{ expectedVersion }` (a conflict comes back, never a silent overwrite) or
997
- * `{ overwrite: true }`.
1044
+ * `{ overwrite: true }`; on an unversioned table `expectedVersion` is refused.
998
1045
  */
999
- declare function update<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown, patch: UpdateOf<T>, guard?: UpdateGuard): Promise<GuardedUpdateResult<RowOf<T> & {
1000
- version: number;
1001
- }> | {
1002
- status: "saved";
1003
- row: RowOf<T>;
1004
- } | {
1005
- status: "not_found";
1006
- }>;
1046
+ declare function update<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, patch: U, guard?: UpdateGuard): Promise<WriteResult<R>>;
1007
1047
  /**
1008
1048
  * Remove one row the way the table removes: soft delete (`deleted_at`) where it
1009
1049
  * has one; an archive-only table refuses (use `archive`); a hard delete only where
1010
- * the table has neither.
1050
+ * the table has neither. Guarded like `update` on a versioned table.
1011
1051
  */
1012
- declare function remove<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown): Promise<"soft_deleted" | "deleted" | "not_found">;
1013
- /** Archive (or, with `restore`, bring back) one row by the table's archive column. */
1014
- declare function archive<T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown, opts?: {
1052
+ declare function remove<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, guard?: UpdateGuard): Promise<{
1053
+ status: "soft_deleted" | "deleted" | "not_found";
1054
+ } | {
1055
+ status: "conflict";
1056
+ currentVersion: number;
1057
+ }>;
1058
+ /** Archive (or, with `restore`, bring back) one row by the table's archive column. Guarded like `update`. */
1059
+ declare function archive<R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, opts?: {
1015
1060
  restore?: boolean;
1016
- }): Promise<RowOf<T> | null>;
1061
+ guard?: UpdateGuard;
1062
+ }): Promise<WriteResult<R>>;
1017
1063
  /** Bring back an archived or soft-deleted row. */
1018
- declare const restore: <T extends TableDescriptor<any, any, any>>(db: Db, table: T, id: unknown) => Promise<RowOf<T> | null>;
1064
+ declare const restore: <R, I, U>(db: Db, table: TableDescriptor<R, I, U>, id: unknown, guard?: UpdateGuard) => Promise<WriteResult<R>>;
1019
1065
  /** Call a database function through its generated door (PostgREST chooses an overload by argument names). */
1020
1066
  declare function call<F extends FunctionDescriptor<any, any>>(db: Db, fn: F, args: ArgsOf<F>): Promise<ResultOf<F>>;
1021
- /** The compact flags index `matrx-data generate --flags-index` writes: "schema.table" → descriptor. */
1067
+ /** One schema's flags (`<schema>.flags.ts` from `matrx-data generate --flags-index`): "schema.table" → descriptor. */
1022
1068
  type FlagsIndex = Readonly<Record<string, TableDescriptor>>;
1069
+ /** Per-schema loaders (`flags-index.ts`): only the schema a runtime name names is loaded (pay-only). */
1070
+ type FlagLoaders = Readonly<Record<string, () => Promise<FlagsIndex>>>;
1023
1071
  /** The door for a table whose name arrives at runtime — loosely typed, the same rules. */
1024
1072
  declare function tableByName(index: FlagsIndex, name: string): TableDescriptor;
1073
+ /** `tableByName` over per-schema loaders: loads only the named schema's flags. */
1074
+ declare function loadTableByName(loaders: FlagLoaders, name: string): Promise<TableDescriptor>;
1025
1075
  /** The `postgres_changes` filter for a table — only published tables have one (an unpublished table delivers nothing). */
1026
1076
  declare function changesOf(table: TableDescriptor<any, any, any>): {
1027
1077
  schema: string;
1028
1078
  table: string;
1029
1079
  };
1030
1080
 
1031
- export { type ActiveOrgCookie, type ActiveOrgCookieOptions, type ActiveOrgSelection, type ArgsOf, type AuthRetryableResult, BulkWriteError, type CreateActiveOrgCookieArgs, type CreateDbOptions, type CreateOAuthRefreshFetchArgs, type CreateSessionRetryOptions, DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE, DEFAULT_ACTIVE_ORG_COOKIE_NAME, DatabaseError, type Db, DoorRefusedError, type FlagsIndex, type FunctionDescriptor, type GuardedUpdateArgs, type GuardedUpdateResult, IncompleteReadError, type InsertOf, type JsonObject, type MergeJsonColumnArgs, type MergeJsonColumnResult, type OAuthRefreshStorage, type OrgRule, type PagedReadQuery, type PagedReadResult, type QueryLike, type ReadAllRowsOptions, type ReadAllRowsRestOptions, RecordUnavailableError, type RecordUnavailableReason, type RecordUnavailableResolution, type ResultOf, type RowOf, type SessionSource, SessionUnavailableError, type SupabaseLike, type TableDescriptor, type TableType, type TryWriteOneResult, type UpdateGuard, type UpdateOf, type UserCredentialsPort, type VersionedRow, type Where, WriteDidNotLandError, type WriteDidNotLandReason, type WriteFailureWords, type WriteOneAction, type WriteOneOptions, type WriteOneRowResult, WriteRefusedError, archive, asJsonObject, assertDoor, assertWriteLanded, call, changesOf, count, createActiveOrgCookie, createDb, createOAuthRefreshFetch, createServiceDb, createSessionRetry, decodeActiveOrgSelection, decodeOAuthRefreshClaims, describeWriteFailure, encodeActiveOrgSelection, guardedUpdate, insert, isMissingSessionError, isRecordUnavailableError, listAll, mergeJsonColumn, page, personSentence, read, readAllRows, readAllRowsRest, recordUnavailableMessage, remove, restore, serverMessageFromBody, tableByName, tryReadAllRows, tryReadAllRowsRest, tryWriteOne, update, writeFailureStatus, writeOne, writeOneRow };
1081
+ export { type ActiveOrgCookie, type ActiveOrgCookieOptions, type ActiveOrgSelection, type ArgsOf, type AuthRetryableResult, BulkWriteError, type CreateActiveOrgCookieArgs, type CreateDbOptions, type CreateOAuthRefreshFetchArgs, type CreateSessionRetryOptions, DEFAULT_ACTIVE_ORG_COOKIE_MAX_AGE, DEFAULT_ACTIVE_ORG_COOKIE_NAME, DatabaseError, type Db, DoorRefusedError, type Filter, type FlagLoaders, type FlagsIndex, type FunctionDescriptor, type GuardedUpdateArgs, type GuardedUpdateResult, IncompleteReadError, type InsertOf, type JsonObject, type MergeJsonColumnArgs, type MergeJsonColumnResult, type OAuthRefreshStorage, type OrgRule, type PagedReadQuery, type PagedReadResult, type QueryLike, type ReadAllRowsOptions, type ReadAllRowsRestOptions, type ReadOptions, RecordUnavailableError, type RecordUnavailableReason, type RecordUnavailableResolution, type ResultOf, type RowOf, type SessionSource, SessionUnavailableError, type SupabaseLike, type TableDescriptor, type TableType, type TryWriteOneResult, type UpdateGuard, type UpdateOf, type UserCredentialsPort, type VersionedRow, type Where, WriteDidNotLandError, type WriteDidNotLandReason, type WriteFailureWords, type WriteOneAction, type WriteOneOptions, type WriteOneRowResult, WriteRefusedError, type WriteResult, archive, asJsonObject, assertDoor, assertWriteLanded, call, changesOf, count, createActiveOrgCookie, createDb, createOAuthRefreshFetch, createServiceDb, createSessionRetry, decodeActiveOrgSelection, decodeOAuthRefreshClaims, describeWriteFailure, encodeActiveOrgSelection, guardedUpdate, insert, isMissingSessionError, isRecordUnavailableError, listAll, loadTableByName, mergeJsonColumn, page, personSentence, read, readAllRows, readAllRowsRest, recordUnavailableMessage, remove, restore, serverMessageFromBody, tableByName, tryReadAllRows, tryReadAllRowsRest, tryWriteOne, update, writeFailureStatus, writeOne, writeOneRow };
package/dist/db.js CHANGED
@@ -976,18 +976,26 @@ function byPk(q, t, id) {
976
976
  const key = id;
977
977
  return t.pk.reduce((acc, col) => acc.eq(col, key[col]), q);
978
978
  }
979
- async function read(db, table, id, columns = "*") {
979
+ var selectOf = (columns) => columns && columns.length ? columns.join(",") : "*";
980
+ function live(q, table, includeDeleted) {
981
+ return table.softDelete && !includeDeleted ? q.is("deleted_at", null) : q;
982
+ }
983
+ function ordered(q, table, orderBy, ascending = true) {
984
+ const cols = orderBy ? [orderBy, ...table.pk.filter((c) => c !== orderBy)] : table.pk.length ? table.pk : ["id"];
985
+ return cols.reduce((acc, c) => acc.order(c, { ascending }), q);
986
+ }
987
+ async function read(db, table, id, opts = {}) {
980
988
  assertDoor(db, table, "read");
981
- return check(`read ${qualified(table)}`, await byPk(from(db, table).select(columns), table, id).maybeSingle());
989
+ const q = live(byPk(from(db, table).select(selectOf(opts.columns)), table, id), table, opts.includeDeleted);
990
+ return check(`read ${qualified(table)}`, await q.maybeSingle());
982
991
  }
983
992
  async function listAll(db, table, where, opts = {}) {
984
993
  assertDoor(db, table, "read");
985
- const order = opts.orderBy ?? table.pk[0] ?? "id";
986
994
  return readAllRows(
987
995
  ({ from: lo, to: hi }) => {
988
- let q = from(db, table).select(opts.columns ?? "*", { count: "exact" });
996
+ let q = live(from(db, table).select(selectOf(opts.columns), { count: "exact" }), table, opts.includeDeleted);
989
997
  if (where) q = where(q);
990
- return q.order(order, { ascending: true }).range(lo, hi);
998
+ return ordered(q, table, opts.orderBy).range(lo, hi);
991
999
  },
992
1000
  {
993
1001
  label: `listAll ${qualified(table)}`,
@@ -998,14 +1006,14 @@ async function listAll(db, table, where, opts = {}) {
998
1006
  }
999
1007
  async function page(db, table, window2) {
1000
1008
  assertDoor(db, table, "read");
1001
- let q = from(db, table).select(window2.columns ?? "*", { count: "exact" });
1009
+ let q = live(from(db, table).select(selectOf(window2.columns), { count: "exact" }), table, window2.includeDeleted);
1002
1010
  if (window2.where) q = window2.where(q);
1003
- const res = await q.order(window2.orderBy ?? table.pk[0] ?? "id", { ascending: window2.ascending ?? true }).range(window2.from, window2.to);
1011
+ const res = await ordered(q, table, window2.orderBy, window2.ascending ?? true).range(window2.from, window2.to);
1004
1012
  return { rows: check(`page ${qualified(table)}`, res) ?? [], total: res.count ?? null };
1005
1013
  }
1006
- async function count(db, table, where) {
1014
+ async function count(db, table, where, opts = {}) {
1007
1015
  assertDoor(db, table, "read");
1008
- let q = from(db, table).select("*", { count: "exact", head: true });
1016
+ let q = live(from(db, table).select("*", { count: "exact", head: true }), table, opts.includeDeleted);
1009
1017
  if (where) q = where(q);
1010
1018
  const res = await q;
1011
1019
  check(`count ${qualified(table)}`, res);
@@ -1038,6 +1046,24 @@ async function insert(db, table, values) {
1038
1046
  const stored = check(`insert ${qualified(table)}`, res);
1039
1047
  return many ? stored : stored[0];
1040
1048
  }
1049
+ async function writeRow(db, table, id, values, door, guard) {
1050
+ const expected = guard && "expectedVersion" in guard ? guard.expectedVersion : void 0;
1051
+ if (expected !== void 0 && !table.versioned) {
1052
+ throw new DoorRefusedError(door, "expectedVersion on a table that is not versioned \u2014 nothing would guard it; pass no guard");
1053
+ }
1054
+ if (table.versioned && expected === void 0 && !(guard && "overwrite" in guard && guard.overwrite)) {
1055
+ throw new DoorRefusedError(door, "a versioned table takes { expectedVersion } or { overwrite: true }");
1056
+ }
1057
+ if (expected !== void 0) {
1058
+ return await guardedUpdate({
1059
+ expectedVersion: expected,
1060
+ applyUpdate: ({ expectedVersion, nextVersion }) => byPk(from(db, table).update({ ...values, version: nextVersion }), table, id).eq("version", expectedVersion).select("*").maybeSingle(),
1061
+ fetchCurrent: () => byPk(from(db, table).select("*"), table, id).maybeSingle()
1062
+ });
1063
+ }
1064
+ const row = check(door, await byPk(from(db, table).update(values), table, id).select("*").maybeSingle());
1065
+ return row ? { status: "saved", row } : { status: "not_found" };
1066
+ }
1041
1067
  async function update(db, table, id, patch, guard) {
1042
1068
  assertDoor(db, table, "update");
1043
1069
  const door = `update ${qualified(table)}`;
@@ -1046,29 +1072,30 @@ async function update(db, table, id, patch, guard) {
1046
1072
  const blocked = table.readOnlyColumns.filter((c) => c in values);
1047
1073
  if (blocked.length) throw new DoorRefusedError(door, `read-only to clients: ${blocked.join(", ")}`);
1048
1074
  }
1049
- if (table.versioned && guard && "expectedVersion" in guard) {
1050
- return guardedUpdate({
1051
- expectedVersion: guard.expectedVersion,
1052
- applyUpdate: ({ expectedVersion, nextVersion }) => byPk(from(db, table).update({ ...values, version: nextVersion }), table, id).eq("version", expectedVersion).select("*").maybeSingle(),
1053
- fetchCurrent: () => byPk(from(db, table).select("*"), table, id).maybeSingle()
1054
- });
1055
- }
1056
- if (table.versioned && !(guard && "overwrite" in guard && guard.overwrite)) {
1057
- throw new DoorRefusedError(door, "a versioned table takes { expectedVersion } or { overwrite: true }");
1058
- }
1059
- const row = check(door, await byPk(from(db, table).update(values), table, id).select("*").maybeSingle());
1060
- return row ? { status: "saved", row } : { status: "not_found" };
1075
+ return writeRow(db, table, id, values, door, guard);
1061
1076
  }
1062
- async function remove(db, table, id) {
1077
+ async function remove(db, table, id, guard) {
1063
1078
  assertDoor(db, table, "remove");
1064
1079
  const door = `remove ${qualified(table)}`;
1065
1080
  if (table.softDelete) {
1066
- const row = check(door, await byPk(from(db, table).update({ deleted_at: (/* @__PURE__ */ new Date()).toISOString() }), table, id).select("*").maybeSingle());
1067
- return row ? "soft_deleted" : "not_found";
1081
+ const res = await writeRow(db, table, id, { deleted_at: (/* @__PURE__ */ new Date()).toISOString() }, door, guard);
1082
+ return res.status === "saved" ? { status: "soft_deleted" } : res.status === "conflict" ? { status: "conflict", currentVersion: res.currentVersion } : res;
1068
1083
  }
1069
1084
  if (table.archive) throw new DoorRefusedError(door, "this table archives instead of deleting \u2014 use archive()");
1070
- const rows = check(door, await byPk(from(db, table).delete(), table, id).select("*"));
1071
- return rows && rows.length ? "deleted" : "not_found";
1085
+ const expected = guard && "expectedVersion" in guard ? guard.expectedVersion : void 0;
1086
+ if (expected !== void 0 && !table.versioned) throw new DoorRefusedError(door, "expectedVersion on a table that is not versioned");
1087
+ if (table.versioned && expected === void 0 && !(guard && "overwrite" in guard)) {
1088
+ throw new DoorRefusedError(door, "a versioned table takes { expectedVersion } or { overwrite: true }");
1089
+ }
1090
+ let q = byPk(from(db, table).delete(), table, id);
1091
+ if (expected !== void 0) q = q.eq("version", expected);
1092
+ const rows = check(door, await q.select("*"));
1093
+ if (rows && rows.length) return { status: "deleted" };
1094
+ if (expected !== void 0) {
1095
+ const current = check(door, await byPk(from(db, table).select("version"), table, id).maybeSingle());
1096
+ if (current) return { status: "conflict", currentVersion: current.version };
1097
+ }
1098
+ return { status: "not_found" };
1072
1099
  }
1073
1100
  async function archive(db, table, id, opts = {}) {
1074
1101
  assertDoor(db, table, "archive");
@@ -1078,9 +1105,9 @@ async function archive(db, table, id, opts = {}) {
1078
1105
  else if (table.archive === "is_archived") values = { is_archived: !opts.restore };
1079
1106
  else if (table.softDelete && opts.restore) values = { deleted_at: null };
1080
1107
  else throw new DoorRefusedError(door, "the table has no archive column");
1081
- return check(door, await byPk(from(db, table).update(values), table, id).select("*").maybeSingle());
1108
+ return writeRow(db, table, id, values, door, opts.guard);
1082
1109
  }
1083
- var restore = (db, table, id) => archive(db, table, id, { restore: true });
1110
+ var restore = (db, table, id, guard) => archive(db, table, id, guard ? { restore: true, guard } : { restore: true });
1084
1111
  async function call(db, fn, args) {
1085
1112
  const door = `call ${fn.schema}.${fn.name}`;
1086
1113
  const role = db.role === "service" ? "service_role" : "authenticated";
@@ -1094,6 +1121,12 @@ function tableByName(index, name) {
1094
1121
  if (!t) throw new DoorRefusedError(`tableByName ${name}`, "no such table in the database description (schema-qualify it: schema.table)");
1095
1122
  return t;
1096
1123
  }
1124
+ async function loadTableByName(loaders, name) {
1125
+ const schema = name.slice(0, name.indexOf("."));
1126
+ const load = schema ? loaders[schema] : void 0;
1127
+ if (!load) throw new DoorRefusedError(`tableByName ${name}`, "no such schema in the database description (schema-qualify it: schema.table)");
1128
+ return tableByName(await load(), name);
1129
+ }
1097
1130
  function changesOf(table) {
1098
1131
  if (!table.realtime) throw new DoorRefusedError(`changesOf ${qualified(table)}`, "the table is not in the supabase_realtime publication");
1099
1132
  return { schema: table.schema, table: table.name };
@@ -1132,6 +1165,7 @@ export {
1132
1165
  isMissingSessionError,
1133
1166
  isRecordUnavailableError,
1134
1167
  listAll,
1168
+ loadTableByName,
1135
1169
  mergeJsonColumn,
1136
1170
  page,
1137
1171
  parseCookieHeader,