@porulle/db 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/column.d.ts CHANGED
@@ -7,21 +7,28 @@
7
7
  * import { defineTable, column } from "@porulle/db";
8
8
  *
9
9
  * export const giftCards = defineTable("gift_cards", {
10
+ * // Per-org unique (default): UNIQUE (organization_id, code)
10
11
  * code: column.text({ unique: true }),
12
+ * // Globally unique across all tenants: UNIQUE (public_code)
13
+ * publicCode: column.text({ unique: "global" }),
11
14
  * balance: column.integer(),
12
15
  * status: column.text({ enum: ["active", "disabled"], default: "active" }),
13
16
  * });
17
+ *
18
+ * Child tables (FK to org-scoped parent) have no organizationId; unique: true
19
+ * creates UNIQUE (col) on the child column.
14
20
  */
21
+ export type ColumnUnique = boolean | "global";
15
22
  export interface TextColumnDef {
16
23
  readonly _type: "text";
17
- unique?: boolean;
24
+ unique?: ColumnUnique;
18
25
  optional?: boolean;
19
26
  enum?: readonly string[];
20
27
  default?: string;
21
28
  }
22
29
  export interface IntegerColumnDef {
23
30
  readonly _type: "integer";
24
- unique?: boolean;
31
+ unique?: ColumnUnique;
25
32
  optional?: boolean;
26
33
  default?: number;
27
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"column.d.ts","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,SAAS,GACjB,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,aAAa,CAAC;AAElB,eAAO,MAAM,MAAM;kBACH,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,KAAG,aAAa;qBAKzC,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,KAAG,gBAAgB;qBAKlD,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,KAAG,gBAAgB;kBAKrD,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,KAAG,aAAa;uBAKvC,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,KAAG,kBAAkB;kBAK3D,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,KAAG,aAAa;CAI3D,CAAC"}
1
+ {"version":3,"file":"column.d.ts","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,SAAS,GACjB,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,aAAa,CAAC;AAElB,eAAO,MAAM,MAAM;kBACH,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,KAAG,aAAa;qBAKzC,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,KAAG,gBAAgB;qBAKlD,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,KAAG,gBAAgB;kBAKrD,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,KAAG,aAAa;uBAKvC,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,KAAG,kBAAkB;kBAK3D,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,KAAG,aAAa;CAI3D,CAAC"}
package/dist/column.js CHANGED
@@ -7,10 +7,16 @@
7
7
  * import { defineTable, column } from "@porulle/db";
8
8
  *
9
9
  * export const giftCards = defineTable("gift_cards", {
10
+ * // Per-org unique (default): UNIQUE (organization_id, code)
10
11
  * code: column.text({ unique: true }),
12
+ * // Globally unique across all tenants: UNIQUE (public_code)
13
+ * publicCode: column.text({ unique: "global" }),
11
14
  * balance: column.integer(),
12
15
  * status: column.text({ enum: ["active", "disabled"], default: "active" }),
13
16
  * });
17
+ *
18
+ * Child tables (FK to org-scoped parent) have no organizationId; unique: true
19
+ * creates UNIQUE (col) on the child column.
14
20
  */
15
21
  export const column = {
16
22
  text: (opts) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"column.js","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiDH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,IAAmC,EAAiB,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAe;QACtB,GAAG,IAAI;KACR,CAAC;IAEF,OAAO,EAAE,CAAC,IAAsC,EAAoB,EAAE,CAAC,CAAC;QACtE,KAAK,EAAE,SAAkB;QACzB,GAAG,IAAI;KACR,CAAC;IAEF,OAAO,EAAE,CAAC,IAAsC,EAAoB,EAAE,CAAC,CAAC;QACtE,KAAK,EAAE,SAAkB;QACzB,GAAG,IAAI;KACR,CAAC;IAEF,IAAI,EAAE,CAAC,IAAmC,EAAiB,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAe;QACtB,GAAG,IAAI;KACR,CAAC;IAEF,SAAS,EAAE,CAAC,IAAwC,EAAsB,EAAE,CAAC,CAAC;QAC5E,KAAK,EAAE,WAAoB;QAC3B,GAAG,IAAI;KACR,CAAC;IAEF,IAAI,EAAE,CAAC,IAAmC,EAAiB,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAe;QACtB,GAAG,IAAI;KACR,CAAC;CACH,CAAC"}
1
+ {"version":3,"file":"column.js","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAmDH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,IAAmC,EAAiB,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAe;QACtB,GAAG,IAAI;KACR,CAAC;IAEF,OAAO,EAAE,CAAC,IAAsC,EAAoB,EAAE,CAAC,CAAC;QACtE,KAAK,EAAE,SAAkB;QACzB,GAAG,IAAI;KACR,CAAC;IAEF,OAAO,EAAE,CAAC,IAAsC,EAAoB,EAAE,CAAC,CAAC;QACtE,KAAK,EAAE,SAAkB;QACzB,GAAG,IAAI;KACR,CAAC;IAEF,IAAI,EAAE,CAAC,IAAmC,EAAiB,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAe;QACtB,GAAG,IAAI;KACR,CAAC;IAEF,SAAS,EAAE,CAAC,IAAwC,EAAsB,EAAE,CAAC,CAAC;QAC5E,KAAK,EAAE,WAAoB;QAC3B,GAAG,IAAI;KACR,CAAC;IAEF,IAAI,EAAE,CAAC,IAAmC,EAAiB,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,MAAe;QACtB,GAAG,IAAI;KACR,CAAC;CACH,CAAC"}
@@ -13,9 +13,15 @@
13
13
  * import { defineTable, column } from "@porulle/db";
14
14
  *
15
15
  * export const giftCards = defineTable("gift_cards", {
16
+ * // Per-org unique (default): UNIQUE (organization_id, code)
16
17
  * code: column.text({ unique: true }),
18
+ * // Globally unique: UNIQUE (public_code)
19
+ * publicCode: column.text({ unique: "global" }),
17
20
  * balance: column.integer(),
18
21
  * });
22
+ *
23
+ * Child tables (FK to org-scoped parent) have no organizationId; unique: true
24
+ * creates UNIQUE (col) on the child column.
19
25
  */
20
26
  import type { ColumnDef } from "./column.js";
21
27
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"define-table.d.ts","sourceRoot":"","sources":["../src/define-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAcH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAgE7C;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAErC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsElE"}
1
+ {"version":3,"file":"define-table.d.ts","sourceRoot":"","sources":["../src/define-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAcH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAgE7C;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAErC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+ElE"}
@@ -13,9 +13,15 @@
13
13
  * import { defineTable, column } from "@porulle/db";
14
14
  *
15
15
  * export const giftCards = defineTable("gift_cards", {
16
+ * // Per-org unique (default): UNIQUE (organization_id, code)
16
17
  * code: column.text({ unique: true }),
18
+ * // Globally unique: UNIQUE (public_code)
19
+ * publicCode: column.text({ unique: "global" }),
17
20
  * balance: column.integer(),
18
21
  * });
22
+ *
23
+ * Child tables (FK to org-scoped parent) have no organizationId; unique: true
24
+ * creates UNIQUE (col) on the child column.
19
25
  */
20
26
  import { pgTable, uuid, text, integer, boolean, timestamp, jsonb, index, uniqueIndex, } from "drizzle-orm/pg-core";
21
27
  import { getTableColumns } from "drizzle-orm";
@@ -107,11 +113,17 @@ extraConfig) {
107
113
  // Map user columns to Drizzle builders
108
114
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
115
  const cols = {};
110
- const uniqueCols = [];
116
+ const perOrgUniqueCols = [];
117
+ const globalUniqueCols = [];
111
118
  for (const [colName, def] of Object.entries(columnDefs)) {
112
119
  cols[colName] = mapColumn(colName, def);
113
- if ("unique" in def && def.unique) {
114
- uniqueCols.push(colName);
120
+ if (!("unique" in def) || !def.unique)
121
+ continue;
122
+ if (def.unique === "global" || isChild) {
123
+ globalUniqueCols.push(colName);
124
+ }
125
+ else {
126
+ perOrgUniqueCols.push(colName);
115
127
  }
116
128
  }
117
129
  // Build full column set with auto-injected fields
@@ -138,12 +150,16 @@ extraConfig) {
138
150
  const indexes = {};
139
151
  if (!isChild) {
140
152
  indexes.orgIdx = index(`idx_${name}_org`).on(t.organizationId);
141
- for (const colName of uniqueCols) {
153
+ for (const colName of perOrgUniqueCols) {
142
154
  const sn = toSnake(colName);
143
155
  indexes[`${colName}Unique`] = uniqueIndex(`${name}_org_${sn}_unique`)
144
156
  .on(t.organizationId, t[colName]);
145
157
  }
146
158
  }
159
+ for (const colName of globalUniqueCols) {
160
+ const sn = toSnake(colName);
161
+ indexes[`${colName}Unique`] = uniqueIndex(`${name}_${sn}_unique`).on(t[colName]);
162
+ }
147
163
  if (extraConfig)
148
164
  Object.assign(indexes, extraConfig(t));
149
165
  return indexes;
@@ -1 +1 @@
1
- {"version":3,"file":"define-table.js","sourceRoot":"","sources":["../src/define-table.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"define-table.js","sourceRoot":"","sources":["../src/define-table.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/db",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "drizzle-orm": "^0.45.1",
15
15
  "postgres": "^3.4.7",
16
- "@porulle/core": "0.8.0"
16
+ "@porulle/core": "0.10.0"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/node": "^24.5.2",
package/src/column.ts CHANGED
@@ -7,15 +7,23 @@
7
7
  * import { defineTable, column } from "@porulle/db";
8
8
  *
9
9
  * export const giftCards = defineTable("gift_cards", {
10
+ * // Per-org unique (default): UNIQUE (organization_id, code)
10
11
  * code: column.text({ unique: true }),
12
+ * // Globally unique across all tenants: UNIQUE (public_code)
13
+ * publicCode: column.text({ unique: "global" }),
11
14
  * balance: column.integer(),
12
15
  * status: column.text({ enum: ["active", "disabled"], default: "active" }),
13
16
  * });
17
+ *
18
+ * Child tables (FK to org-scoped parent) have no organizationId; unique: true
19
+ * creates UNIQUE (col) on the child column.
14
20
  */
15
21
 
22
+ export type ColumnUnique = boolean | "global";
23
+
16
24
  export interface TextColumnDef {
17
25
  readonly _type: "text";
18
- unique?: boolean;
26
+ unique?: ColumnUnique;
19
27
  optional?: boolean;
20
28
  enum?: readonly string[];
21
29
  default?: string;
@@ -23,7 +31,7 @@ export interface TextColumnDef {
23
31
 
24
32
  export interface IntegerColumnDef {
25
33
  readonly _type: "integer";
26
- unique?: boolean;
34
+ unique?: ColumnUnique;
27
35
  optional?: boolean;
28
36
  default?: number;
29
37
  }
@@ -1,8 +1,25 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { getTableColumns } from "drizzle-orm";
3
+ import { getTableConfig } from "drizzle-orm/pg-core";
4
+ import type { PgTable } from "drizzle-orm/pg-core";
3
5
  import { defineTable } from "./define-table.js";
4
6
  import { column } from "./column.js";
5
7
 
8
+ function uniqueIndexColumnNames(table: PgTable) {
9
+ const { indexes } = getTableConfig(table);
10
+ return indexes
11
+ .filter((idx) => idx.config.unique)
12
+ .map((idx) => ({
13
+ name: idx.config.name,
14
+ columns: idx.config.columns.map((col) => {
15
+ if (col && typeof col === "object" && "name" in col && col.name) {
16
+ return col.name as string;
17
+ }
18
+ return null;
19
+ }),
20
+ }));
21
+ }
22
+
6
23
  describe("defineTable", () => {
7
24
  it("auto-injects id, organizationId, createdAt, updatedAt on top-level tables", () => {
8
25
  const table = defineTable("test_products", {
@@ -76,4 +93,40 @@ describe("defineTable", () => {
76
93
  const cols = getTableColumns(table);
77
94
  expect(cols).toHaveProperty("status");
78
95
  });
96
+
97
+ it("SEC-03: honors unique on child tables via UNIQUE (col)", () => {
98
+ const parent = defineTable("sec03_parent", {
99
+ slug: column.text({ unique: true }),
100
+ });
101
+
102
+ const child = defineTable("sec03_child", {
103
+ parentId: column.uuid({ references: parent }),
104
+ code: column.text({ unique: true }),
105
+ });
106
+
107
+ const uniqueIndexes = uniqueIndexColumnNames(child);
108
+ expect(uniqueIndexes).toHaveLength(1);
109
+ expect(uniqueIndexes[0]?.columns).toEqual(["code"]);
110
+ expect(uniqueIndexes[0]?.name).toBe("sec03_child_code_unique");
111
+ });
112
+
113
+ it("SEC-04: unique true is per-org composite; unique global is single-column", () => {
114
+ const perOrg = defineTable("sec04_per_org", {
115
+ sku: column.text({ unique: true }),
116
+ });
117
+
118
+ const perOrgIndexes = uniqueIndexColumnNames(perOrg);
119
+ expect(perOrgIndexes).toHaveLength(1);
120
+ expect(perOrgIndexes[0]?.columns).toEqual(["organization_id", "sku"]);
121
+ expect(perOrgIndexes[0]?.name).toBe("sec04_per_org_org_sku_unique");
122
+
123
+ const global = defineTable("sec04_global", {
124
+ publicCode: column.text({ unique: "global" }),
125
+ });
126
+
127
+ const globalIndexes = uniqueIndexColumnNames(global);
128
+ expect(globalIndexes).toHaveLength(1);
129
+ expect(globalIndexes[0]?.columns).toEqual(["public_code"]);
130
+ expect(globalIndexes[0]?.name).toBe("sec04_global_public_code_unique");
131
+ });
79
132
  });
@@ -13,9 +13,15 @@
13
13
  * import { defineTable, column } from "@porulle/db";
14
14
  *
15
15
  * export const giftCards = defineTable("gift_cards", {
16
+ * // Per-org unique (default): UNIQUE (organization_id, code)
16
17
  * code: column.text({ unique: true }),
18
+ * // Globally unique: UNIQUE (public_code)
19
+ * publicCode: column.text({ unique: "global" }),
17
20
  * balance: column.integer(),
18
21
  * });
22
+ *
23
+ * Child tables (FK to org-scoped parent) have no organizationId; unique: true
24
+ * creates UNIQUE (col) on the child column.
19
25
  */
20
26
 
21
27
  import {
@@ -115,12 +121,16 @@ export function defineTable(
115
121
  // Map user columns to Drizzle builders
116
122
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
123
  const cols: Record<string, any> = {};
118
- const uniqueCols: string[] = [];
124
+ const perOrgUniqueCols: string[] = [];
125
+ const globalUniqueCols: string[] = [];
119
126
 
120
127
  for (const [colName, def] of Object.entries(columnDefs)) {
121
128
  cols[colName] = mapColumn(colName, def);
122
- if ("unique" in def && def.unique) {
123
- uniqueCols.push(colName);
129
+ if (!("unique" in def) || !def.unique) continue;
130
+ if (def.unique === "global" || isChild) {
131
+ globalUniqueCols.push(colName);
132
+ } else {
133
+ perOrgUniqueCols.push(colName);
124
134
  }
125
135
  }
126
136
 
@@ -155,13 +165,18 @@ export function defineTable(
155
165
  if (!isChild) {
156
166
  indexes.orgIdx = index(`idx_${name}_org`).on(t.organizationId);
157
167
 
158
- for (const colName of uniqueCols) {
168
+ for (const colName of perOrgUniqueCols) {
159
169
  const sn = toSnake(colName);
160
170
  indexes[`${colName}Unique`] = uniqueIndex(`${name}_org_${sn}_unique`)
161
171
  .on(t.organizationId, t[colName]);
162
172
  }
163
173
  }
164
174
 
175
+ for (const colName of globalUniqueCols) {
176
+ const sn = toSnake(colName);
177
+ indexes[`${colName}Unique`] = uniqueIndex(`${name}_${sn}_unique`).on(t[colName]);
178
+ }
179
+
165
180
  if (extraConfig) Object.assign(indexes, extraConfig(t));
166
181
  return indexes;
167
182
  });