@headless-lms/adapter-db 0.6.3 → 0.7.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/index.d.ts CHANGED
@@ -25,6 +25,7 @@ import { DrizzleRolesRepository } from "./repositories/roles.js";
25
25
  import { DrizzleOutboxAppender, DrizzleOutboxStore, OUTBOX_CLAIM_MS, OUTBOX_MAX_ATTEMPTS } from "./repositories/outbox.js";
26
26
  import { DrizzleProgressRepository } from "./repositories/progress.js";
27
27
  import { DrizzleSettingsRepository, deepMerge } from "./repositories/settings.js";
28
+ import { DrizzleOrgHostIndex } from "./repositories/org-hosts.js";
28
29
  import { DrizzleSubscriptionsRepository } from "./repositories/subscriptions.js";
29
30
  import { DrizzleAdminRepository } from "./repositories/admin.js";
30
- export { type DatabasePoolOptions, type Db, DrizzleAdminRepository, DrizzleAssetsRepository, DrizzleAutomationRunsRepository, DrizzleAutomationsRepository, DrizzleChatRepository, DrizzleConnectionsRepository, DrizzleContentRepository, DrizzleCredentialStore, DrizzleDevicesRepository, DrizzleDigestRunRepository, DrizzleDiscussionRepository, DrizzleEntitlementsRepository, DrizzleIdentityRepository, DrizzleMemberRepository, DrizzleMembersRepository, DrizzleMentionsRepository, DrizzleNotificationSettingsRepository, DrizzleNotificationsRepository, DrizzleOrganizationsRepository, DrizzleOutboxAppender, DrizzleOutboxStore, DrizzlePostRepository, DrizzleProgressRepository, DrizzleRolesRepository, DrizzleSettingsRepository, DrizzleSubscriptionsRepository, DrizzleUnitOfWork, OUTBOX_CLAIM_MS, OUTBOX_MAX_ATTEMPTS, SETTINGS_NAMESPACE, authorProfileColumns, createDb, decryptSecret, deepMerge, encryptSecret, migrationsFolder, orgUserProfileColumns, runMigrations, index_d_exports as schema, studentFirstName, studentLastName, studentProfileColumns };
31
+ export { type DatabasePoolOptions, type Db, DrizzleAdminRepository, DrizzleAssetsRepository, DrizzleAutomationRunsRepository, DrizzleAutomationsRepository, DrizzleChatRepository, DrizzleConnectionsRepository, DrizzleContentRepository, DrizzleCredentialStore, DrizzleDevicesRepository, DrizzleDigestRunRepository, DrizzleDiscussionRepository, DrizzleEntitlementsRepository, DrizzleIdentityRepository, DrizzleMemberRepository, DrizzleMembersRepository, DrizzleMentionsRepository, DrizzleNotificationSettingsRepository, DrizzleNotificationsRepository, DrizzleOrgHostIndex, DrizzleOrganizationsRepository, DrizzleOutboxAppender, DrizzleOutboxStore, DrizzlePostRepository, DrizzleProgressRepository, DrizzleRolesRepository, DrizzleSettingsRepository, DrizzleSubscriptionsRepository, DrizzleUnitOfWork, OUTBOX_CLAIM_MS, OUTBOX_MAX_ATTEMPTS, SETTINGS_NAMESPACE, authorProfileColumns, createDb, decryptSecret, deepMerge, encryptSecret, migrationsFolder, orgUserProfileColumns, runMigrations, index_d_exports as schema, studentFirstName, studentLastName, studentProfileColumns };
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ import { DrizzleOrganizationsRepository } from "./repositories/organizations.js"
25
25
  import { DrizzleRolesRepository } from "./repositories/roles.js";
26
26
  import { DrizzleOutboxAppender, DrizzleOutboxStore, OUTBOX_CLAIM_MS, OUTBOX_MAX_ATTEMPTS } from "./repositories/outbox.js";
27
27
  import { DrizzleProgressRepository } from "./repositories/progress.js";
28
+ import { DrizzleOrgHostIndex } from "./repositories/org-hosts.js";
28
29
  import { DrizzleSubscriptionsRepository } from "./repositories/subscriptions.js";
29
30
  import { DrizzleAdminRepository } from "./repositories/admin.js";
30
- export { DrizzleAdminRepository, DrizzleAssetsRepository, DrizzleAutomationRunsRepository, DrizzleAutomationsRepository, DrizzleChatRepository, DrizzleConnectionsRepository, DrizzleContentRepository, DrizzleCredentialStore, DrizzleDevicesRepository, DrizzleDigestRunRepository, DrizzleDiscussionRepository, DrizzleEntitlementsRepository, DrizzleIdentityRepository, DrizzleMemberRepository, DrizzleMembersRepository, DrizzleMentionsRepository, DrizzleNotificationSettingsRepository, DrizzleNotificationsRepository, DrizzleOrganizationsRepository, DrizzleOutboxAppender, DrizzleOutboxStore, DrizzlePostRepository, DrizzleProgressRepository, DrizzleRolesRepository, DrizzleSettingsRepository, DrizzleSubscriptionsRepository, DrizzleUnitOfWork, OUTBOX_CLAIM_MS, OUTBOX_MAX_ATTEMPTS, SETTINGS_NAMESPACE, authorProfileColumns, createDb, decryptSecret, deepMerge, encryptSecret, migrationsFolder, orgUserProfileColumns, runMigrations, schema_exports as schema, studentFirstName, studentLastName, studentProfileColumns };
31
+ export { DrizzleAdminRepository, DrizzleAssetsRepository, DrizzleAutomationRunsRepository, DrizzleAutomationsRepository, DrizzleChatRepository, DrizzleConnectionsRepository, DrizzleContentRepository, DrizzleCredentialStore, DrizzleDevicesRepository, DrizzleDigestRunRepository, DrizzleDiscussionRepository, DrizzleEntitlementsRepository, DrizzleIdentityRepository, DrizzleMemberRepository, DrizzleMembersRepository, DrizzleMentionsRepository, DrizzleNotificationSettingsRepository, DrizzleNotificationsRepository, DrizzleOrgHostIndex, DrizzleOrganizationsRepository, DrizzleOutboxAppender, DrizzleOutboxStore, DrizzlePostRepository, DrizzleProgressRepository, DrizzleRolesRepository, DrizzleSettingsRepository, DrizzleSubscriptionsRepository, DrizzleUnitOfWork, OUTBOX_CLAIM_MS, OUTBOX_MAX_ATTEMPTS, SETTINGS_NAMESPACE, authorProfileColumns, createDb, decryptSecret, deepMerge, encryptSecret, migrationsFolder, orgUserProfileColumns, runMigrations, schema_exports as schema, studentFirstName, studentLastName, studentProfileColumns };
@@ -0,0 +1,11 @@
1
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import { OrgHostIndex } from "@headless-lms/core/organizations";
3
+ //#region src/repositories/org-hosts.d.ts
4
+ declare class DrizzleOrgHostIndex implements OrgHostIndex {
5
+ private readonly db;
6
+ constructor(db: NodePgDatabase);
7
+ findOrgIdsByHost(host: string): Promise<string[]>;
8
+ }
9
+ //#endregion
10
+ export { DrizzleOrgHostIndex };
11
+ //# sourceMappingURL=org-hosts.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { settings } from "../schema/settings.js";
2
+ import { translateDbErrors } from "./pg-errors.js";
3
+ import { and, eq, or, sql } from "drizzle-orm";
4
+ import { SettingsNamespace } from "@headless-lms/core/shared/settings";
5
+ //#region src/repositories/org-hosts.ts
6
+ var DrizzleOrgHostIndex = class {
7
+ db;
8
+ constructor(db) {
9
+ this.db = db;
10
+ }
11
+ async findOrgIdsByHost(host) {
12
+ return (await this.db.selectDistinct({ orgId: settings.orgId }).from(settings).where(and(eq(settings.namespace, SettingsNamespace.organizations), or(sql`(${settings.value} #>> '{hosts,courses}') = ${host}`, sql`(${settings.value} #>> '{hosts,community}') = ${host}`)))).map((row) => row.orgId);
13
+ }
14
+ };
15
+ translateDbErrors(DrizzleOrgHostIndex);
16
+ //#endregion
17
+ export { DrizzleOrgHostIndex };
18
+
19
+ //# sourceMappingURL=org-hosts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-hosts.js","names":[],"sources":["../../src/repositories/org-hosts.ts"],"sourcesContent":["import { and, eq, or, sql } from \"drizzle-orm\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { OrgHostIndex } from \"@headless-lms/core/organizations\";\nimport { SettingsNamespace } from \"@headless-lms/core/shared/settings\";\nimport { settings } from \"../schema/settings.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\nexport class DrizzleOrgHostIndex implements OrgHostIndex {\n constructor(private readonly db: NodePgDatabase) {}\n\n async findOrgIdsByHost(host: string): Promise<string[]> {\n const rows = await this.db\n .selectDistinct({ orgId: settings.orgId })\n .from(settings)\n .where(\n and(\n eq(settings.namespace, SettingsNamespace.organizations),\n or(\n sql`(${settings.value} #>> '{hosts,courses}') = ${host}`,\n sql`(${settings.value} #>> '{hosts,community}') = ${host}`,\n ),\n ),\n );\n return rows.map((row) => row.orgId);\n }\n}\ntranslateDbErrors(DrizzleOrgHostIndex);\n"],"mappings":";;;;;AAOA,IAAa,sBAAb,MAAyD;CAC1B;CAA7B,YAAY,IAAqC;EAApB,KAAA,KAAA;CAAqB;CAElD,MAAM,iBAAiB,MAAiC;EAatD,QAAO,MAZY,KAAK,GACrB,eAAe,EAAE,OAAO,SAAS,MAAM,CAAC,CAAC,CACzC,KAAK,QAAQ,CAAC,CACd,MACC,IACE,GAAG,SAAS,WAAW,kBAAkB,aAAa,GACtD,GACE,GAAG,IAAI,SAAS,MAAM,4BAA4B,QAClD,GAAG,IAAI,SAAS,MAAM,8BAA8B,MACtD,CACF,CACF,EAAA,CACU,KAAK,QAAQ,IAAI,KAAK;CACpC;AACF;AACA,kBAAkB,mBAAmB"}
@@ -1,6 +1,6 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { Logger } from "@headless-lms/core/types";
3
2
  import { SettingsRecord, SettingsRepository } from "@headless-lms/core/shared/settings";
3
+ import { Logger } from "@headless-lms/core/types";
4
4
  //#region src/repositories/settings.d.ts
5
5
  /**
6
6
  * Recursive merge of a partial patch into a stored value: a patch cannot
@@ -1326,7 +1326,7 @@ declare const events: import("drizzle-orm/pg-core").PgTableWithColumns<{
1326
1326
  tableName: "events";
1327
1327
  dataType: "string";
1328
1328
  columnType: "PgText";
1329
- data: "draft" | "published" | "canceled";
1329
+ data: "canceled" | "draft" | "published";
1330
1330
  driverParam: string;
1331
1331
  notNull: true;
1332
1332
  hasDefault: true;
@@ -1,5 +1,6 @@
1
1
  import { organizations } from "./organizations.js";
2
- import { jsonb, pgTable, primaryKey, text, timestamp } from "drizzle-orm/pg-core";
2
+ import { sql } from "drizzle-orm";
3
+ import { index, jsonb, pgTable, primaryKey, text, timestamp } from "drizzle-orm/pg-core";
3
4
  //#region src/schema/settings.ts
4
5
  const settings = pgTable("settings", {
5
6
  orgId: text("org_id").notNull().references(() => organizations.id),
@@ -8,11 +9,15 @@ const settings = pgTable("settings", {
8
9
  value: jsonb("value").$type().notNull().default({}),
9
10
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
10
11
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => /* @__PURE__ */ new Date())
11
- }, (t) => ({ pk: primaryKey({ columns: [
12
- t.orgId,
13
- t.namespace,
14
- t.scopeId
15
- ] }) }));
12
+ }, (t) => ({
13
+ pk: primaryKey({ columns: [
14
+ t.orgId,
15
+ t.namespace,
16
+ t.scopeId
17
+ ] }),
18
+ coursesHost: index("settings_courses_host_idx").on(sql`(${t.value} #>> '{hosts,courses}')`).where(sql`${t.namespace} = 'organizations'`),
19
+ communityHost: index("settings_community_host_idx").on(sql`(${t.value} #>> '{hosts,community}')`).where(sql`${t.namespace} = 'organizations'`)
20
+ }));
16
21
  //#endregion
17
22
  export { settings };
18
23
 
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","names":[],"sources":["../../src/schema/settings.ts"],"sourcesContent":["// settings — cross-cutting configuration store. One row per\n// (org, namespace, scope): the namespace names the domain that owns and\n// interprets the values; the scope is the id the settings attach to (an org id\n// for org-wide defaults, otherwise any entity id — a course, an activity, …).\n//\n// Deliberately NOT bound to any domain: no FK to content or anywhere else, so\n// settings never learns what it is scoping. Rows orphaned by a deleted entity\n// are purged off that domain's *.deleted event, not by cascade.\nimport { pgTable, text, jsonb, timestamp, primaryKey } from \"drizzle-orm/pg-core\";\nimport type { Setting } from \"@headless-lms/core/schemas\";\nimport { organizations } from \"./organizations.js\";\nimport type { Expect, NoDrift } from \"./drift.js\";\n\nexport const settings = pgTable(\n \"settings\",\n {\n orgId: text(\"org_id\")\n .notNull()\n .references(() => organizations.id),\n // The owning domain: 'content', 'discussion', … Values are validated and\n // interpreted by that domain, never here.\n namespace: text(\"namespace\").notNull(),\n // Org-scoped rows carry the org id, so the column is never null and the\n // natural key can be the primary key.\n scopeId: text(\"scope_id\").notNull(),\n value: jsonb(\"value\").$type<Setting[\"value\"]>().notNull().default({}),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).notNull().defaultNow(),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true })\n .notNull()\n .defaultNow()\n .$onUpdate(() => new Date()),\n },\n (t) => ({\n pk: primaryKey({ columns: [t.orgId, t.namespace, t.scopeId] }),\n }),\n);\n\ntype _SettingsDrift = Expect<NoDrift<typeof settings.$inferSelect, Setting>>;\n"],"mappings":";;;AAaA,MAAa,WAAW,QACtB,YACA;CACE,OAAO,KAAK,QAAQ,CAAC,CAClB,QAAQ,CAAC,CACT,iBAAiB,cAAc,EAAE;CAGpC,WAAW,KAAK,WAAW,CAAC,CAAC,QAAQ;CAGrC,SAAS,KAAK,UAAU,CAAC,CAAC,QAAQ;CAClC,OAAO,MAAM,OAAO,CAAC,CAAC,MAAwB,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC;CACpE,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW;CAChF,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC,CAAC,CACvD,QAAQ,CAAC,CACT,WAAW,CAAC,CACZ,gCAAgB,IAAI,KAAK,CAAC;AAC/B,IACC,OAAO,EACN,IAAI,WAAW,EAAE,SAAS;CAAC,EAAE;CAAO,EAAE;CAAW,EAAE;AAAO,EAAE,CAAC,EAC/D,EACF"}
1
+ {"version":3,"file":"settings.js","names":[],"sources":["../../src/schema/settings.ts"],"sourcesContent":["// settings — cross-cutting configuration store. One row per\n// (org, namespace, scope): the namespace names the domain that owns and\n// interprets the values; the scope is the id the settings attach to (an org id\n// for org-wide defaults, otherwise any entity id — a course, an activity, …).\n//\n// Deliberately NOT bound to any domain: no FK to content or anywhere else, so\n// settings never learns what it is scoping. Rows orphaned by a deleted entity\n// are purged off that domain's *.deleted event, not by cascade.\nimport { sql } from \"drizzle-orm\";\nimport { pgTable, text, jsonb, timestamp, primaryKey, index } from \"drizzle-orm/pg-core\";\nimport type { Setting } from \"@headless-lms/core/schemas\";\nimport { organizations } from \"./organizations.js\";\nimport type { Expect, NoDrift } from \"./drift.js\";\n\nexport const settings = pgTable(\n \"settings\",\n {\n orgId: text(\"org_id\")\n .notNull()\n .references(() => organizations.id),\n // The owning domain: 'content', 'discussion', … Values are validated and\n // interpreted by that domain, never here.\n namespace: text(\"namespace\").notNull(),\n // Org-scoped rows carry the org id, so the column is never null and the\n // natural key can be the primary key.\n scopeId: text(\"scope_id\").notNull(),\n value: jsonb(\"value\").$type<Setting[\"value\"]>().notNull().default({}),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).notNull().defaultNow(),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true })\n .notNull()\n .defaultNow()\n .$onUpdate(() => new Date()),\n },\n (t) => ({\n pk: primaryKey({ columns: [t.orgId, t.namespace, t.scopeId] }),\n coursesHost: index(\"settings_courses_host_idx\")\n .on(sql`(${t.value} #>> '{hosts,courses}')`)\n .where(sql`${t.namespace} = 'organizations'`),\n communityHost: index(\"settings_community_host_idx\")\n .on(sql`(${t.value} #>> '{hosts,community}')`)\n .where(sql`${t.namespace} = 'organizations'`),\n }),\n);\n\ntype _SettingsDrift = Expect<NoDrift<typeof settings.$inferSelect, Setting>>;\n"],"mappings":";;;;AAcA,MAAa,WAAW,QACtB,YACA;CACE,OAAO,KAAK,QAAQ,CAAC,CAClB,QAAQ,CAAC,CACT,iBAAiB,cAAc,EAAE;CAGpC,WAAW,KAAK,WAAW,CAAC,CAAC,QAAQ;CAGrC,SAAS,KAAK,UAAU,CAAC,CAAC,QAAQ;CAClC,OAAO,MAAM,OAAO,CAAC,CAAC,MAAwB,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC;CACpE,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW;CAChF,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC,CAAC,CACvD,QAAQ,CAAC,CACT,WAAW,CAAC,CACZ,gCAAgB,IAAI,KAAK,CAAC;AAC/B,IACC,OAAO;CACN,IAAI,WAAW,EAAE,SAAS;EAAC,EAAE;EAAO,EAAE;EAAW,EAAE;CAAO,EAAE,CAAC;CAC7D,aAAa,MAAM,2BAA2B,CAAC,CAC5C,GAAG,GAAG,IAAI,EAAE,MAAM,wBAAwB,CAAC,CAC3C,MAAM,GAAG,GAAG,EAAE,UAAU,mBAAmB;CAC9C,eAAe,MAAM,6BAA6B,CAAC,CAChD,GAAG,GAAG,IAAI,EAAE,MAAM,0BAA0B,CAAC,CAC7C,MAAM,GAAG,GAAG,EAAE,UAAU,mBAAmB;AAChD,EACF"}
@@ -0,0 +1,2 @@
1
+ CREATE INDEX "settings_courses_host_idx" ON "settings" USING btree (("value" #>> '{hosts,courses}')) WHERE "settings"."namespace" = 'organizations';--> statement-breakpoint
2
+ CREATE INDEX "settings_community_host_idx" ON "settings" USING btree (("value" #>> '{hosts,community}')) WHERE "settings"."namespace" = 'organizations';