@shizuoka-its/core 3.0.0 → 3.0.1
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/_virtual/_rolldown/runtime.cjs +20 -0
- package/dist/application/usecase/member/RegisterMember.cjs +1 -0
- package/dist/application/usecase/member/RegisterMember.cjs.map +1 -1
- package/dist/infrastructure/drizzle/DrizzleDiscordAccountRepository.cjs +1 -0
- package/dist/infrastructure/drizzle/DrizzleDiscordAccountRepository.cjs.map +1 -1
- package/dist/infrastructure/drizzle/DrizzleEventRepository.cjs +1 -0
- package/dist/infrastructure/drizzle/DrizzleEventRepository.cjs.map +1 -1
- package/dist/infrastructure/drizzle/DrizzleKarteRepository.cjs +1 -0
- package/dist/infrastructure/drizzle/DrizzleKarteRepository.cjs.map +1 -1
- package/dist/infrastructure/drizzle/DrizzleMemberRepository.cjs +1 -0
- package/dist/infrastructure/drizzle/DrizzleMemberRepository.cjs.map +1 -1
- package/dist/infrastructure/drizzle/client.cjs +10 -8
- package/dist/infrastructure/drizzle/client.cjs.map +1 -1
- package/dist/infrastructure/drizzle/client.mjs +8 -8
- package/dist/infrastructure/drizzle/client.mjs.map +1 -1
- package/package.json +2 -3
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
3
8
|
var __exportAll = (all, no_symbols) => {
|
|
4
9
|
let target = {};
|
|
5
10
|
for (var name in all) __defProp(target, name, {
|
|
@@ -9,5 +14,20 @@ var __exportAll = (all, no_symbols) => {
|
|
|
9
14
|
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
15
|
return target;
|
|
11
16
|
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
19
|
+
key = keys[i];
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
21
|
+
get: ((k) => from[k]).bind(null, key),
|
|
22
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
28
|
+
value: mod,
|
|
29
|
+
enumerable: true
|
|
30
|
+
}) : target, mod));
|
|
12
31
|
//#endregion
|
|
13
32
|
exports.__exportAll = __exportAll;
|
|
33
|
+
exports.__toESM = __toESM;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require("../../../_virtual/_rolldown/runtime.cjs");
|
|
1
2
|
const require_ApplicationExceptions = require("../../exceptions/ApplicationExceptions.cjs");
|
|
2
3
|
const require_base = require("../base.cjs");
|
|
3
4
|
const require_Member = require("../../../domain/aggregates/member/Member.cjs");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegisterMember.cjs","names":["IUseCase","MemberEmailAlreadyExistsException","ActiveMember","memberId"],"sources":["../../../../src/application/usecase/member/RegisterMember.ts"],"sourcesContent":["import { v4 as uuid } from \"uuid\";\nimport {\n\tActiveMember,\n\ttype CompleteAffiliation,\n\ttype Email,\n\ttype Member,\n\ttype MemberRepository,\n\ttype Recorded,\n\ttype StudentId,\n\ttype UniversityEmail,\n\tmemberId,\n} from \"#domain\";\nimport { MemberEmailAlreadyExistsException } from \"../../exceptions\";\nimport { IUseCase } from \"../base\";\n\nexport interface RegisterMemberInput {\n\tname: string;\n\tstudentId: StudentId;\n\temail: UniversityEmail;\n\tpersonalEmail: Recorded<Email>;\n\taffiliation: CompleteAffiliation;\n}\n\nexport interface RegisterMemberOutput {\n\tmember: Member;\n}\n\nexport class RegisterMemberUseCase extends IUseCase<RegisterMemberInput, RegisterMemberOutput> {\n\tconstructor(private readonly memberRepo: MemberRepository) {\n\t\tsuper();\n\t}\n\n\tasync execute(input: RegisterMemberInput): Promise<RegisterMemberOutput> {\n\t\tconst existingMember = await this.memberRepo.findByEmail(input.email);\n\t\tif (existingMember) {\n\t\t\tthrow new MemberEmailAlreadyExistsException(input.email.getValue());\n\t\t}\n\n\t\tconst member = ActiveMember.register({\n\t\t\tid: memberId(uuid()),\n\t\t\temail: input.email,\n\t\t\tname: input.name,\n\t\t\tpersonalEmail: input.personalEmail,\n\t\t\tstudentId: input.studentId,\n\t\t\taffiliation: input.affiliation,\n\t\t});\n\n\t\tawait this.memberRepo.save(member);\n\n\t\treturn { member };\n\t}\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"RegisterMember.cjs","names":["IUseCase","MemberEmailAlreadyExistsException","ActiveMember","memberId"],"sources":["../../../../src/application/usecase/member/RegisterMember.ts"],"sourcesContent":["import { v4 as uuid } from \"uuid\";\nimport {\n\tActiveMember,\n\ttype CompleteAffiliation,\n\ttype Email,\n\ttype Member,\n\ttype MemberRepository,\n\ttype Recorded,\n\ttype StudentId,\n\ttype UniversityEmail,\n\tmemberId,\n} from \"#domain\";\nimport { MemberEmailAlreadyExistsException } from \"../../exceptions\";\nimport { IUseCase } from \"../base\";\n\nexport interface RegisterMemberInput {\n\tname: string;\n\tstudentId: StudentId;\n\temail: UniversityEmail;\n\tpersonalEmail: Recorded<Email>;\n\taffiliation: CompleteAffiliation;\n}\n\nexport interface RegisterMemberOutput {\n\tmember: Member;\n}\n\nexport class RegisterMemberUseCase extends IUseCase<RegisterMemberInput, RegisterMemberOutput> {\n\tconstructor(private readonly memberRepo: MemberRepository) {\n\t\tsuper();\n\t}\n\n\tasync execute(input: RegisterMemberInput): Promise<RegisterMemberOutput> {\n\t\tconst existingMember = await this.memberRepo.findByEmail(input.email);\n\t\tif (existingMember) {\n\t\t\tthrow new MemberEmailAlreadyExistsException(input.email.getValue());\n\t\t}\n\n\t\tconst member = ActiveMember.register({\n\t\t\tid: memberId(uuid()),\n\t\t\temail: input.email,\n\t\t\tname: input.name,\n\t\t\tpersonalEmail: input.personalEmail,\n\t\t\tstudentId: input.studentId,\n\t\t\taffiliation: input.affiliation,\n\t\t});\n\n\t\tawait this.memberRepo.save(member);\n\n\t\treturn { member };\n\t}\n}\n"],"mappings":";;;;;;;AA2BA,IAAa,wBAAb,cAA2CA,aAAAA,SAAoD;CAC9F,YAAY,YAA+C;AAC1D,SAAO;AADqB,OAAA,aAAA;;CAI7B,MAAM,QAAQ,OAA2D;AAExE,MADuB,MAAM,KAAK,WAAW,YAAY,MAAM,MAAM,CAEpE,OAAM,IAAIC,8BAAAA,kCAAkC,MAAM,MAAM,UAAU,CAAC;EAGpE,MAAM,SAASC,eAAAA,aAAa,SAAS;GACpC,IAAIC,iBAAAA,UAAAA,GAAAA,KAAAA,KAAe,CAAC;GACpB,OAAO,MAAM;GACb,MAAM,MAAM;GACZ,eAAe,MAAM;GACrB,WAAW,MAAM;GACjB,aAAa,MAAM;GACnB,CAAC;AAEF,QAAM,KAAK,WAAW,KAAK,OAAO;AAElC,SAAO,EAAE,QAAQ"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require("../../_virtual/_rolldown/runtime.cjs");
|
|
1
2
|
const require_DiscordAccount = require("../../domain/aggregates/discord-account/DiscordAccount.cjs");
|
|
2
3
|
const require_DiscordId = require("../../domain/aggregates/discord-account/DiscordId.cjs");
|
|
3
4
|
const require_MemberId = require("../../domain/aggregates/member/MemberId.cjs");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DrizzleDiscordAccountRepository.cjs","names":["DiscordAccount","discordId","memberId","getDb","discordAccounts","discordAccountDomainEvents","serializeDiscordAccountEventPayload"],"sources":["../../../src/infrastructure/drizzle/DrizzleDiscordAccountRepository.ts"],"sourcesContent":["import { v4 as uuid } from \"uuid\";\nimport { eq } from \"drizzle-orm\";\nimport {\n\tDiscordAccount,\n\ttype DiscordAccountRepository,\n\ttype DiscordId,\n\ttype MemberId,\n\tdiscordId,\n\tmemberId,\n} from \"#domain\";\nimport { getDb } from \"./client\";\nimport { discordAccountDomainEvents, discordAccounts } from \"./schema\";\nimport { serializeDiscordAccountEventPayload } from \"./serializeDiscordAccountEvent\";\n\ntype DiscordAccountRow = typeof discordAccounts.$inferSelect;\n\nfunction toDomain(row: DiscordAccountRow): DiscordAccount {\n\treturn DiscordAccount.reconstruct(discordId(row.discordId), memberId(row.memberId), row.nickName);\n}\n\nexport class DrizzleDiscordAccountRepository implements DiscordAccountRepository {\n\tasync findByDiscordId(id: DiscordId): Promise<DiscordAccount | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.discordAccounts.findFirst({\n\t\t\twhere: eq(discordAccounts.discordId, id as string),\n\t\t});\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findByMemberId(id: MemberId): Promise<DiscordAccount[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.discordAccounts.findMany({\n\t\t\twhere: eq(discordAccounts.memberId, id as string),\n\t\t});\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync findAll(): Promise<DiscordAccount[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.discordAccounts.findMany();\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync save(account: DiscordAccount): Promise<void> {\n\t\tconst db = getDb();\n\t\tconst now = new Date().toISOString();\n\t\tconst events = account.getDomainEvents();\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\tawait tx\n\t\t\t\t.insert(discordAccounts)\n\t\t\t\t.values({\n\t\t\t\t\tdiscordId: account.discordId as string,\n\t\t\t\t\tnickName: account.nickName,\n\t\t\t\t\tmemberId: account.memberId as string,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: discordAccounts.discordId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tnickName: account.nickName,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\tif (events.length > 0) {\n\t\t\t\tawait tx.insert(discordAccountDomainEvents).values(\n\t\t\t\t\tevents.map((event) => ({\n\t\t\t\t\t\tid: uuid(),\n\t\t\t\t\t\tdiscordId: event.discordId as string,\n\t\t\t\t\t\tmemberId: event.memberId as string,\n\t\t\t\t\t\teventName: event.eventName,\n\t\t\t\t\t\tpayload: serializeDiscordAccountEventPayload(event),\n\t\t\t\t\t\toccurredAt: event.occurredAt.toISOString(),\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\tasync delete(id: DiscordId): Promise<void> {\n\t\tconst db = getDb();\n\t\tawait db.delete(discordAccounts).where(eq(discordAccounts.discordId, id as string));\n\t}\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DrizzleDiscordAccountRepository.cjs","names":["DiscordAccount","discordId","memberId","getDb","discordAccounts","discordAccountDomainEvents","serializeDiscordAccountEventPayload"],"sources":["../../../src/infrastructure/drizzle/DrizzleDiscordAccountRepository.ts"],"sourcesContent":["import { v4 as uuid } from \"uuid\";\nimport { eq } from \"drizzle-orm\";\nimport {\n\tDiscordAccount,\n\ttype DiscordAccountRepository,\n\ttype DiscordId,\n\ttype MemberId,\n\tdiscordId,\n\tmemberId,\n} from \"#domain\";\nimport { getDb } from \"./client\";\nimport { discordAccountDomainEvents, discordAccounts } from \"./schema\";\nimport { serializeDiscordAccountEventPayload } from \"./serializeDiscordAccountEvent\";\n\ntype DiscordAccountRow = typeof discordAccounts.$inferSelect;\n\nfunction toDomain(row: DiscordAccountRow): DiscordAccount {\n\treturn DiscordAccount.reconstruct(discordId(row.discordId), memberId(row.memberId), row.nickName);\n}\n\nexport class DrizzleDiscordAccountRepository implements DiscordAccountRepository {\n\tasync findByDiscordId(id: DiscordId): Promise<DiscordAccount | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.discordAccounts.findFirst({\n\t\t\twhere: eq(discordAccounts.discordId, id as string),\n\t\t});\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findByMemberId(id: MemberId): Promise<DiscordAccount[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.discordAccounts.findMany({\n\t\t\twhere: eq(discordAccounts.memberId, id as string),\n\t\t});\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync findAll(): Promise<DiscordAccount[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.discordAccounts.findMany();\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync save(account: DiscordAccount): Promise<void> {\n\t\tconst db = getDb();\n\t\tconst now = new Date().toISOString();\n\t\tconst events = account.getDomainEvents();\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\tawait tx\n\t\t\t\t.insert(discordAccounts)\n\t\t\t\t.values({\n\t\t\t\t\tdiscordId: account.discordId as string,\n\t\t\t\t\tnickName: account.nickName,\n\t\t\t\t\tmemberId: account.memberId as string,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: discordAccounts.discordId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tnickName: account.nickName,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\tif (events.length > 0) {\n\t\t\t\tawait tx.insert(discordAccountDomainEvents).values(\n\t\t\t\t\tevents.map((event) => ({\n\t\t\t\t\t\tid: uuid(),\n\t\t\t\t\t\tdiscordId: event.discordId as string,\n\t\t\t\t\t\tmemberId: event.memberId as string,\n\t\t\t\t\t\teventName: event.eventName,\n\t\t\t\t\t\tpayload: serializeDiscordAccountEventPayload(event),\n\t\t\t\t\t\toccurredAt: event.occurredAt.toISOString(),\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\tasync delete(id: DiscordId): Promise<void> {\n\t\tconst db = getDb();\n\t\tawait db.delete(discordAccounts).where(eq(discordAccounts.discordId, id as string));\n\t}\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAS,SAAS,KAAwC;AACzD,QAAOA,uBAAAA,eAAe,YAAYC,kBAAAA,UAAU,IAAI,UAAU,EAAEC,iBAAAA,SAAS,IAAI,SAAS,EAAE,IAAI,SAAS;;AAGlG,IAAa,kCAAb,MAAiF;CAChF,MAAM,gBAAgB,IAA+C;EAEpE,MAAM,MAAM,MADDC,eAAAA,OAAO,CACG,MAAM,gBAAgB,UAAU,EACpD,QAAA,GAAA,YAAA,IAAUC,eAAAA,gBAAgB,WAAW,GAAa,EAClD,CAAC;AACF,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,SAAS,IAAI;;CAGrB,MAAM,eAAe,IAAyC;AAK7D,UAHa,MADFD,eAAAA,OAAO,CACI,MAAM,gBAAgB,SAAS,EACpD,QAAA,GAAA,YAAA,IAAUC,eAAAA,gBAAgB,UAAU,GAAa,EACjD,CAAC,EACU,IAAI,SAAS;;CAG1B,MAAM,UAAqC;AAG1C,UADa,MADFD,eAAAA,OAAO,CACI,MAAM,gBAAgB,UAAU,EAC1C,IAAI,SAAS;;CAG1B,MAAM,KAAK,SAAwC;EAClD,MAAM,KAAKA,eAAAA,OAAO;EAClB,MAAM,uBAAM,IAAI,MAAM,EAAC,aAAa;EACpC,MAAM,SAAS,QAAQ,iBAAiB;AAExC,QAAM,GAAG,YAAY,OAAO,OAAO;AAClC,SAAM,GACJ,OAAOC,eAAAA,gBAAgB,CACvB,OAAO;IACP,WAAW,QAAQ;IACnB,UAAU,QAAQ;IAClB,UAAU,QAAQ;IAClB,WAAW;IACX,CAAC,CACD,mBAAmB;IACnB,QAAQA,eAAAA,gBAAgB;IACxB,KAAK;KACJ,UAAU,QAAQ;KAClB,WAAW;KACX;IACD,CAAC;AAEH,OAAI,OAAO,SAAS,EACnB,OAAM,GAAG,OAAOC,eAAAA,2BAA2B,CAAC,OAC3C,OAAO,KAAK,WAAW;IACtB,KAAA,GAAA,KAAA,KAAU;IACV,WAAW,MAAM;IACjB,UAAU,MAAM;IAChB,WAAW,MAAM;IACjB,SAASC,qCAAAA,oCAAoC,MAAM;IACnD,YAAY,MAAM,WAAW,aAAa;IAC1C,EAAE,CACH;IAED;;CAGH,MAAM,OAAO,IAA8B;AAE1C,QADWH,eAAAA,OAAO,CACT,OAAOC,eAAAA,gBAAgB,CAAC,OAAA,GAAA,YAAA,IAASA,eAAAA,gBAAgB,WAAW,GAAa,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require("../../_virtual/_rolldown/runtime.cjs");
|
|
1
2
|
const require_Event = require("../../domain/aggregates/event/Event.cjs");
|
|
2
3
|
const require_EventId = require("../../domain/aggregates/event/EventId.cjs");
|
|
3
4
|
const require_Exhibit = require("../../domain/aggregates/event/Exhibit.cjs");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DrizzleEventRepository.cjs","names":["Event","eventId","memberId","LightningTalk","exhibitId","LightningTalkDuration","Url","Exhibit","getDb","events","exhibits","lightningTalks","memberExhibits","memberEvents"],"sources":["../../../src/infrastructure/drizzle/DrizzleEventRepository.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { eq, inArray } from \"drizzle-orm\";\nimport {\n\tEvent,\n\ttype EventId,\n\ttype EventRepository,\n\tExhibit,\n\ttype ExhibitId,\n\tLightningTalk,\n\tLightningTalkDuration,\n\ttype MemberId,\n\tUrl,\n\teventId,\n\texhibitId,\n\tmemberId,\n} from \"#domain\";\nimport { type DrizzleDb, getDb } from \"./client\";\nimport { events, exhibits, lightningTalks, memberEvents, memberExhibits } from \"./schema\";\n\n// ============================================================================\n// Type Definitions - Using $inferSelect for schema-derived types\n// ============================================================================\n\n/** Exhibit with related data - matches Drizzle's relational query result */\ntype ExhibitWithRelations = typeof exhibits.$inferSelect & {\n\tlightningTalk: typeof lightningTalks.$inferSelect | null;\n\tmemberExhibits: (typeof memberExhibits.$inferSelect)[];\n};\n\n/** Event with all related data - matches Drizzle's relational query result */\ntype EventWithRelations = typeof events.$inferSelect & {\n\texhibits: ExhibitWithRelations[];\n\tmemberEvents: (typeof memberEvents.$inferSelect)[];\n};\n\n// ============================================================================\n// Repository Implementation\n// ============================================================================\n\nexport class DrizzleEventRepository implements EventRepository {\n\t/**\n\t * Converts a database record to a domain Event entity.\n\t */\n\tprivate toDomain(record: EventWithRelations): Event {\n\t\tconst event = new Event(eventId(record.id), record.name, new Date(record.date));\n\n\t\t// Load event member IDs\n\t\tfor (const memberEvent of record.memberEvents) {\n\t\t\tevent.addMemberId(memberId(memberEvent.memberId));\n\t\t}\n\n\t\t// Load exhibits\n\t\tfor (const exhibitRecord of record.exhibits) {\n\t\t\tconst exhibit = this.createExhibitFromRecord(exhibitRecord);\n\n\t\t\t// Load exhibit member IDs\n\t\t\tfor (const memberExhibit of exhibitRecord.memberExhibits) {\n\t\t\t\texhibit.addMemberId(memberId(memberExhibit.memberId));\n\t\t\t}\n\n\t\t\tevent.addExhibit(exhibit);\n\t\t}\n\n\t\treturn event;\n\t}\n\n\t/**\n\t * Creates an Exhibit domain entity from a database record.\n\t */\n\tprivate createExhibitFromRecord(record: ExhibitWithRelations): Exhibit {\n\t\tif (record.lightningTalk) {\n\t\t\tconst lt = record.lightningTalk;\n\t\t\tconst lightningTalk = new LightningTalk(\n\t\t\t\texhibitId(lt.exhibitId),\n\t\t\t\tnew Date(lt.startTime),\n\t\t\t\tnew LightningTalkDuration(lt.duration),\n\t\t\t\tlt.slideUrl ? new Url(lt.slideUrl) : undefined,\n\t\t\t);\n\n\t\t\treturn Exhibit.createWithLightningTalk(\n\t\t\t\texhibitId(record.id),\n\t\t\t\trecord.name,\n\t\t\t\tlightningTalk,\n\t\t\t\trecord.description ?? undefined,\n\t\t\t\trecord.markdownContent ?? undefined,\n\t\t\t\trecord.url ? new Url(record.url) : undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn new Exhibit(\n\t\t\texhibitId(record.id),\n\t\t\trecord.name,\n\t\t\trecord.description ?? undefined,\n\t\t\trecord.markdownContent ?? undefined,\n\t\t\trecord.url ? new Url(record.url) : undefined,\n\t\t);\n\t}\n\n\t// ==========================================================================\n\t// Persistence Methods\n\t// ==========================================================================\n\n\tprivate async persistEvent(event: Event): Promise<void> {\n\t\tconst db = getDb();\n\t\tconst snapshot = event.toSnapshot();\n\t\tconst now = new Date().toISOString();\n\t\tconst dateStr = snapshot.date instanceof Date ? snapshot.date.toISOString() : snapshot.date;\n\n\t\t// 1) Event upsert\n\t\tawait db\n\t\t\t.insert(events)\n\t\t\t.values({\n\t\t\t\tid: snapshot.id,\n\t\t\t\tname: snapshot.name,\n\t\t\t\tdate: dateStr,\n\t\t\t\tupdatedAt: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: events.id,\n\t\t\t\tset: {\n\t\t\t\t\tname: snapshot.name,\n\t\t\t\t\tdate: dateStr,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t},\n\t\t\t});\n\n\t\t// 2) Find obsolete exhibits and clean up\n\t\tconst snapshotExhibitIds = snapshot.exhibits.map((ex) => ex.id);\n\t\tawait this.deleteObsoleteExhibits(db, snapshot.id, snapshotExhibitIds);\n\n\t\t// 3) Upsert exhibits\n\t\tfor (const ex of snapshot.exhibits) {\n\t\t\tawait this.upsertExhibit(db, snapshot.id, ex);\n\t\t}\n\n\t\t// 4) Sync member events\n\t\tawait this.syncMemberEvents(db, snapshot.id, event.getMemberIds());\n\n\t\t// 5) Sync member exhibits\n\t\tfor (const exhibitDomain of event.getExhibits()) {\n\t\t\tawait this.syncMemberExhibits(db, exhibitDomain.id, exhibitDomain.getMemberIds());\n\t\t}\n\t}\n\n\tprivate async deleteObsoleteExhibits(\n\t\tdb: DrizzleDb,\n\t\teventId: EventId,\n\t\tkeptExhibitIds: ExhibitId[],\n\t): Promise<void> {\n\t\tconst existingExhibits = await db\n\t\t\t.select({ id: exhibits.id })\n\t\t\t.from(exhibits)\n\t\t\t.where(eq(exhibits.eventId, eventId));\n\n\t\tconst existingIdSet = new Set(existingExhibits.map((r) => exhibitId(r.id)));\n\t\tconst keptIdSet = new Set(keptExhibitIds);\n\t\tconst obsoleteIds = [...existingIdSet].filter((id) => !keptIdSet.has(id));\n\n\t\tif (obsoleteIds.length > 0) {\n\t\t\tawait db.delete(lightningTalks).where(inArray(lightningTalks.exhibitId, obsoleteIds));\n\t\t\tawait db.delete(memberExhibits).where(inArray(memberExhibits.exhibitId, obsoleteIds));\n\t\t\tawait db.delete(exhibits).where(inArray(exhibits.id, obsoleteIds));\n\t\t}\n\t}\n\n\tprivate async upsertExhibit(\n\t\tdb: DrizzleDb,\n\t\teventId: EventId,\n\t\tex: ReturnType<Event[\"toSnapshot\"]>[\"exhibits\"][number],\n\t): Promise<void> {\n\t\tconst now = new Date().toISOString();\n\t\tawait db\n\t\t\t.insert(exhibits)\n\t\t\t.values({\n\t\t\t\tid: ex.id,\n\t\t\t\tname: ex.name,\n\t\t\t\tdescription: ex.description ?? null,\n\t\t\t\tmarkdownContent: ex.markdownContent ?? null,\n\t\t\t\turl: ex.url?.getValue() ?? null,\n\t\t\t\teventId: eventId,\n\t\t\t\tupdatedAt: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: exhibits.id,\n\t\t\t\tset: {\n\t\t\t\t\tname: ex.name,\n\t\t\t\t\tdescription: ex.description ?? null,\n\t\t\t\t\tmarkdownContent: ex.markdownContent ?? null,\n\t\t\t\t\turl: ex.url?.getValue() ?? null,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t},\n\t\t\t});\n\n\t\tif (ex.lightningTalk) {\n\t\t\tconst startTimeStr =\n\t\t\t\tex.lightningTalk.startTime instanceof Date\n\t\t\t\t\t? ex.lightningTalk.startTime.toISOString()\n\t\t\t\t\t: ex.lightningTalk.startTime;\n\t\t\tawait db\n\t\t\t\t.insert(lightningTalks)\n\t\t\t\t.values({\n\t\t\t\t\texhibitId: ex.id,\n\t\t\t\t\tstartTime: startTimeStr,\n\t\t\t\t\tduration: ex.lightningTalk.durationMinutes.getValue(),\n\t\t\t\t\tslideUrl: ex.lightningTalk.slideUrl?.getValue() ?? null,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: lightningTalks.exhibitId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tstartTime: startTimeStr,\n\t\t\t\t\t\tduration: ex.lightningTalk.durationMinutes.getValue(),\n\t\t\t\t\t\tslideUrl: ex.lightningTalk.slideUrl?.getValue() ?? null,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t} else {\n\t\t\tawait db.delete(lightningTalks).where(eq(lightningTalks.exhibitId, ex.id));\n\t\t}\n\t}\n\n\tprivate async syncMemberEvents(\n\t\tdb: DrizzleDb,\n\t\teventId: EventId,\n\t\tmemberIds: MemberId[],\n\t): Promise<void> {\n\t\tawait db.delete(memberEvents).where(eq(memberEvents.eventId, eventId));\n\n\t\tconst now = new Date().toISOString();\n\t\tfor (const memberId of memberIds) {\n\t\t\tawait db\n\t\t\t\t.insert(memberEvents)\n\t\t\t\t.values({\n\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\tmemberId,\n\t\t\t\t\teventId,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoNothing();\n\t\t}\n\t}\n\n\tprivate async syncMemberExhibits(\n\t\tdb: DrizzleDb,\n\t\texhibitId: ExhibitId,\n\t\tmemberIds: MemberId[],\n\t): Promise<void> {\n\t\tawait db.delete(memberExhibits).where(eq(memberExhibits.exhibitId, exhibitId));\n\n\t\tconst now = new Date().toISOString();\n\t\tfor (const memberId of memberIds) {\n\t\t\tawait db\n\t\t\t\t.insert(memberExhibits)\n\t\t\t\t.values({\n\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\tmemberId,\n\t\t\t\t\texhibitId,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoNothing();\n\t\t}\n\t}\n\n\t// ==========================================================================\n\t// Query Methods\n\t// ==========================================================================\n\n\tasync findById(id: EventId): Promise<Event | null> {\n\t\tconst db = getDb();\n\t\tconst record = await db.query.events.findFirst({\n\t\t\twhere: eq(events.id, id),\n\t\t\twith: {\n\t\t\t\tmemberEvents: true,\n\t\t\t\texhibits: {\n\t\t\t\t\twith: {\n\t\t\t\t\t\tlightningTalk: true,\n\t\t\t\t\t\tmemberExhibits: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\tif (!record) return null;\n\t\treturn this.toDomain(record);\n\t}\n\n\tasync findByParticipantMemberId(memberId: MemberId): Promise<Event[]> {\n\t\tconst db = getDb();\n\n\t\tconst participations = await db\n\t\t\t.select({ eventId: memberEvents.eventId })\n\t\t\t.from(memberEvents)\n\t\t\t.where(eq(memberEvents.memberId, memberId));\n\n\t\tif (participations.length === 0) return [];\n\n\t\tconst eventIds = participations.map((p) => p.eventId);\n\t\tconst records = await db.query.events.findMany({\n\t\t\twhere: inArray(events.id, eventIds),\n\t\t\twith: {\n\t\t\t\tmemberEvents: true,\n\t\t\t\texhibits: {\n\t\t\t\t\twith: {\n\t\t\t\t\t\tlightningTalk: true,\n\t\t\t\t\t\tmemberExhibits: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn records.map((r) => this.toDomain(r));\n\t}\n\n\tasync findByExhibitId(exhibitId: ExhibitId): Promise<Event | null> {\n\t\tconst db = getDb();\n\n\t\tconst exhibit = await db\n\t\t\t.select({ eventId: exhibits.eventId })\n\t\t\t.from(exhibits)\n\t\t\t.where(eq(exhibits.id, exhibitId))\n\t\t\t.limit(1);\n\n\t\tif (exhibit.length === 0) return null;\n\t\treturn this.findById(eventId(exhibit[0].eventId));\n\t}\n\n\tasync findAll(): Promise<Event[]> {\n\t\tconst db = getDb();\n\t\tconst records = await db.query.events.findMany({\n\t\t\twith: {\n\t\t\t\tmemberEvents: true,\n\t\t\t\texhibits: {\n\t\t\t\t\twith: {\n\t\t\t\t\t\tlightningTalk: true,\n\t\t\t\t\t\tmemberExhibits: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn records.map((r) => this.toDomain(r));\n\t}\n\n\tasync save(event: Event): Promise<void> {\n\t\tawait this.persistEvent(event);\n\t}\n\n\tasync delete(eventId: EventId): Promise<void> {\n\t\tconst db = getDb();\n\n\t\tconst exhibitRecords = await db\n\t\t\t.select({ id: exhibits.id })\n\t\t\t.from(exhibits)\n\t\t\t.where(eq(exhibits.eventId, eventId));\n\t\tconst exhibitIds = exhibitRecords.map((ex) => ex.id);\n\n\t\tif (exhibitIds.length > 0) {\n\t\t\tawait db.delete(lightningTalks).where(inArray(lightningTalks.exhibitId, exhibitIds));\n\t\t\tawait db.delete(memberExhibits).where(inArray(memberExhibits.exhibitId, exhibitIds));\n\t\t}\n\n\t\tawait db.delete(memberEvents).where(eq(memberEvents.eventId, eventId));\n\t\tawait db.delete(exhibits).where(eq(exhibits.eventId, eventId));\n\t\tawait db.delete(events).where(eq(events.id, eventId));\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DrizzleEventRepository.cjs","names":["Event","eventId","memberId","LightningTalk","exhibitId","LightningTalkDuration","Url","Exhibit","getDb","events","exhibits","lightningTalks","memberExhibits","memberEvents"],"sources":["../../../src/infrastructure/drizzle/DrizzleEventRepository.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { eq, inArray } from \"drizzle-orm\";\nimport {\n\tEvent,\n\ttype EventId,\n\ttype EventRepository,\n\tExhibit,\n\ttype ExhibitId,\n\tLightningTalk,\n\tLightningTalkDuration,\n\ttype MemberId,\n\tUrl,\n\teventId,\n\texhibitId,\n\tmemberId,\n} from \"#domain\";\nimport { type DrizzleDb, getDb } from \"./client\";\nimport { events, exhibits, lightningTalks, memberEvents, memberExhibits } from \"./schema\";\n\n// ============================================================================\n// Type Definitions - Using $inferSelect for schema-derived types\n// ============================================================================\n\n/** Exhibit with related data - matches Drizzle's relational query result */\ntype ExhibitWithRelations = typeof exhibits.$inferSelect & {\n\tlightningTalk: typeof lightningTalks.$inferSelect | null;\n\tmemberExhibits: (typeof memberExhibits.$inferSelect)[];\n};\n\n/** Event with all related data - matches Drizzle's relational query result */\ntype EventWithRelations = typeof events.$inferSelect & {\n\texhibits: ExhibitWithRelations[];\n\tmemberEvents: (typeof memberEvents.$inferSelect)[];\n};\n\n// ============================================================================\n// Repository Implementation\n// ============================================================================\n\nexport class DrizzleEventRepository implements EventRepository {\n\t/**\n\t * Converts a database record to a domain Event entity.\n\t */\n\tprivate toDomain(record: EventWithRelations): Event {\n\t\tconst event = new Event(eventId(record.id), record.name, new Date(record.date));\n\n\t\t// Load event member IDs\n\t\tfor (const memberEvent of record.memberEvents) {\n\t\t\tevent.addMemberId(memberId(memberEvent.memberId));\n\t\t}\n\n\t\t// Load exhibits\n\t\tfor (const exhibitRecord of record.exhibits) {\n\t\t\tconst exhibit = this.createExhibitFromRecord(exhibitRecord);\n\n\t\t\t// Load exhibit member IDs\n\t\t\tfor (const memberExhibit of exhibitRecord.memberExhibits) {\n\t\t\t\texhibit.addMemberId(memberId(memberExhibit.memberId));\n\t\t\t}\n\n\t\t\tevent.addExhibit(exhibit);\n\t\t}\n\n\t\treturn event;\n\t}\n\n\t/**\n\t * Creates an Exhibit domain entity from a database record.\n\t */\n\tprivate createExhibitFromRecord(record: ExhibitWithRelations): Exhibit {\n\t\tif (record.lightningTalk) {\n\t\t\tconst lt = record.lightningTalk;\n\t\t\tconst lightningTalk = new LightningTalk(\n\t\t\t\texhibitId(lt.exhibitId),\n\t\t\t\tnew Date(lt.startTime),\n\t\t\t\tnew LightningTalkDuration(lt.duration),\n\t\t\t\tlt.slideUrl ? new Url(lt.slideUrl) : undefined,\n\t\t\t);\n\n\t\t\treturn Exhibit.createWithLightningTalk(\n\t\t\t\texhibitId(record.id),\n\t\t\t\trecord.name,\n\t\t\t\tlightningTalk,\n\t\t\t\trecord.description ?? undefined,\n\t\t\t\trecord.markdownContent ?? undefined,\n\t\t\t\trecord.url ? new Url(record.url) : undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn new Exhibit(\n\t\t\texhibitId(record.id),\n\t\t\trecord.name,\n\t\t\trecord.description ?? undefined,\n\t\t\trecord.markdownContent ?? undefined,\n\t\t\trecord.url ? new Url(record.url) : undefined,\n\t\t);\n\t}\n\n\t// ==========================================================================\n\t// Persistence Methods\n\t// ==========================================================================\n\n\tprivate async persistEvent(event: Event): Promise<void> {\n\t\tconst db = getDb();\n\t\tconst snapshot = event.toSnapshot();\n\t\tconst now = new Date().toISOString();\n\t\tconst dateStr = snapshot.date instanceof Date ? snapshot.date.toISOString() : snapshot.date;\n\n\t\t// 1) Event upsert\n\t\tawait db\n\t\t\t.insert(events)\n\t\t\t.values({\n\t\t\t\tid: snapshot.id,\n\t\t\t\tname: snapshot.name,\n\t\t\t\tdate: dateStr,\n\t\t\t\tupdatedAt: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: events.id,\n\t\t\t\tset: {\n\t\t\t\t\tname: snapshot.name,\n\t\t\t\t\tdate: dateStr,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t},\n\t\t\t});\n\n\t\t// 2) Find obsolete exhibits and clean up\n\t\tconst snapshotExhibitIds = snapshot.exhibits.map((ex) => ex.id);\n\t\tawait this.deleteObsoleteExhibits(db, snapshot.id, snapshotExhibitIds);\n\n\t\t// 3) Upsert exhibits\n\t\tfor (const ex of snapshot.exhibits) {\n\t\t\tawait this.upsertExhibit(db, snapshot.id, ex);\n\t\t}\n\n\t\t// 4) Sync member events\n\t\tawait this.syncMemberEvents(db, snapshot.id, event.getMemberIds());\n\n\t\t// 5) Sync member exhibits\n\t\tfor (const exhibitDomain of event.getExhibits()) {\n\t\t\tawait this.syncMemberExhibits(db, exhibitDomain.id, exhibitDomain.getMemberIds());\n\t\t}\n\t}\n\n\tprivate async deleteObsoleteExhibits(\n\t\tdb: DrizzleDb,\n\t\teventId: EventId,\n\t\tkeptExhibitIds: ExhibitId[],\n\t): Promise<void> {\n\t\tconst existingExhibits = await db\n\t\t\t.select({ id: exhibits.id })\n\t\t\t.from(exhibits)\n\t\t\t.where(eq(exhibits.eventId, eventId));\n\n\t\tconst existingIdSet = new Set(existingExhibits.map((r) => exhibitId(r.id)));\n\t\tconst keptIdSet = new Set(keptExhibitIds);\n\t\tconst obsoleteIds = [...existingIdSet].filter((id) => !keptIdSet.has(id));\n\n\t\tif (obsoleteIds.length > 0) {\n\t\t\tawait db.delete(lightningTalks).where(inArray(lightningTalks.exhibitId, obsoleteIds));\n\t\t\tawait db.delete(memberExhibits).where(inArray(memberExhibits.exhibitId, obsoleteIds));\n\t\t\tawait db.delete(exhibits).where(inArray(exhibits.id, obsoleteIds));\n\t\t}\n\t}\n\n\tprivate async upsertExhibit(\n\t\tdb: DrizzleDb,\n\t\teventId: EventId,\n\t\tex: ReturnType<Event[\"toSnapshot\"]>[\"exhibits\"][number],\n\t): Promise<void> {\n\t\tconst now = new Date().toISOString();\n\t\tawait db\n\t\t\t.insert(exhibits)\n\t\t\t.values({\n\t\t\t\tid: ex.id,\n\t\t\t\tname: ex.name,\n\t\t\t\tdescription: ex.description ?? null,\n\t\t\t\tmarkdownContent: ex.markdownContent ?? null,\n\t\t\t\turl: ex.url?.getValue() ?? null,\n\t\t\t\teventId: eventId,\n\t\t\t\tupdatedAt: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: exhibits.id,\n\t\t\t\tset: {\n\t\t\t\t\tname: ex.name,\n\t\t\t\t\tdescription: ex.description ?? null,\n\t\t\t\t\tmarkdownContent: ex.markdownContent ?? null,\n\t\t\t\t\turl: ex.url?.getValue() ?? null,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t},\n\t\t\t});\n\n\t\tif (ex.lightningTalk) {\n\t\t\tconst startTimeStr =\n\t\t\t\tex.lightningTalk.startTime instanceof Date\n\t\t\t\t\t? ex.lightningTalk.startTime.toISOString()\n\t\t\t\t\t: ex.lightningTalk.startTime;\n\t\t\tawait db\n\t\t\t\t.insert(lightningTalks)\n\t\t\t\t.values({\n\t\t\t\t\texhibitId: ex.id,\n\t\t\t\t\tstartTime: startTimeStr,\n\t\t\t\t\tduration: ex.lightningTalk.durationMinutes.getValue(),\n\t\t\t\t\tslideUrl: ex.lightningTalk.slideUrl?.getValue() ?? null,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: lightningTalks.exhibitId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tstartTime: startTimeStr,\n\t\t\t\t\t\tduration: ex.lightningTalk.durationMinutes.getValue(),\n\t\t\t\t\t\tslideUrl: ex.lightningTalk.slideUrl?.getValue() ?? null,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t} else {\n\t\t\tawait db.delete(lightningTalks).where(eq(lightningTalks.exhibitId, ex.id));\n\t\t}\n\t}\n\n\tprivate async syncMemberEvents(\n\t\tdb: DrizzleDb,\n\t\teventId: EventId,\n\t\tmemberIds: MemberId[],\n\t): Promise<void> {\n\t\tawait db.delete(memberEvents).where(eq(memberEvents.eventId, eventId));\n\n\t\tconst now = new Date().toISOString();\n\t\tfor (const memberId of memberIds) {\n\t\t\tawait db\n\t\t\t\t.insert(memberEvents)\n\t\t\t\t.values({\n\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\tmemberId,\n\t\t\t\t\teventId,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoNothing();\n\t\t}\n\t}\n\n\tprivate async syncMemberExhibits(\n\t\tdb: DrizzleDb,\n\t\texhibitId: ExhibitId,\n\t\tmemberIds: MemberId[],\n\t): Promise<void> {\n\t\tawait db.delete(memberExhibits).where(eq(memberExhibits.exhibitId, exhibitId));\n\n\t\tconst now = new Date().toISOString();\n\t\tfor (const memberId of memberIds) {\n\t\t\tawait db\n\t\t\t\t.insert(memberExhibits)\n\t\t\t\t.values({\n\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\tmemberId,\n\t\t\t\t\texhibitId,\n\t\t\t\t\tupdatedAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoNothing();\n\t\t}\n\t}\n\n\t// ==========================================================================\n\t// Query Methods\n\t// ==========================================================================\n\n\tasync findById(id: EventId): Promise<Event | null> {\n\t\tconst db = getDb();\n\t\tconst record = await db.query.events.findFirst({\n\t\t\twhere: eq(events.id, id),\n\t\t\twith: {\n\t\t\t\tmemberEvents: true,\n\t\t\t\texhibits: {\n\t\t\t\t\twith: {\n\t\t\t\t\t\tlightningTalk: true,\n\t\t\t\t\t\tmemberExhibits: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\tif (!record) return null;\n\t\treturn this.toDomain(record);\n\t}\n\n\tasync findByParticipantMemberId(memberId: MemberId): Promise<Event[]> {\n\t\tconst db = getDb();\n\n\t\tconst participations = await db\n\t\t\t.select({ eventId: memberEvents.eventId })\n\t\t\t.from(memberEvents)\n\t\t\t.where(eq(memberEvents.memberId, memberId));\n\n\t\tif (participations.length === 0) return [];\n\n\t\tconst eventIds = participations.map((p) => p.eventId);\n\t\tconst records = await db.query.events.findMany({\n\t\t\twhere: inArray(events.id, eventIds),\n\t\t\twith: {\n\t\t\t\tmemberEvents: true,\n\t\t\t\texhibits: {\n\t\t\t\t\twith: {\n\t\t\t\t\t\tlightningTalk: true,\n\t\t\t\t\t\tmemberExhibits: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn records.map((r) => this.toDomain(r));\n\t}\n\n\tasync findByExhibitId(exhibitId: ExhibitId): Promise<Event | null> {\n\t\tconst db = getDb();\n\n\t\tconst exhibit = await db\n\t\t\t.select({ eventId: exhibits.eventId })\n\t\t\t.from(exhibits)\n\t\t\t.where(eq(exhibits.id, exhibitId))\n\t\t\t.limit(1);\n\n\t\tif (exhibit.length === 0) return null;\n\t\treturn this.findById(eventId(exhibit[0].eventId));\n\t}\n\n\tasync findAll(): Promise<Event[]> {\n\t\tconst db = getDb();\n\t\tconst records = await db.query.events.findMany({\n\t\t\twith: {\n\t\t\t\tmemberEvents: true,\n\t\t\t\texhibits: {\n\t\t\t\t\twith: {\n\t\t\t\t\t\tlightningTalk: true,\n\t\t\t\t\t\tmemberExhibits: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn records.map((r) => this.toDomain(r));\n\t}\n\n\tasync save(event: Event): Promise<void> {\n\t\tawait this.persistEvent(event);\n\t}\n\n\tasync delete(eventId: EventId): Promise<void> {\n\t\tconst db = getDb();\n\n\t\tconst exhibitRecords = await db\n\t\t\t.select({ id: exhibits.id })\n\t\t\t.from(exhibits)\n\t\t\t.where(eq(exhibits.eventId, eventId));\n\t\tconst exhibitIds = exhibitRecords.map((ex) => ex.id);\n\n\t\tif (exhibitIds.length > 0) {\n\t\t\tawait db.delete(lightningTalks).where(inArray(lightningTalks.exhibitId, exhibitIds));\n\t\t\tawait db.delete(memberExhibits).where(inArray(memberExhibits.exhibitId, exhibitIds));\n\t\t}\n\n\t\tawait db.delete(memberEvents).where(eq(memberEvents.eventId, eventId));\n\t\tawait db.delete(exhibits).where(eq(exhibits.eventId, eventId));\n\t\tawait db.delete(events).where(eq(events.id, eventId));\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DrizzleKarteRepository.cjs","names":["notRecorded","recorded","StudentId","CONSULTATION_CATEGORIES","memberId","workDuration","Karte","karteId","getDb","kartes","karteAssignees"],"sources":["../../../src/infrastructure/drizzle/DrizzleKarteRepository.ts"],"sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n\tCONSULTATION_CATEGORIES,\n\tKarte,\n\tStudentId,\n\tkarteId,\n\tmemberId,\n\tnotRecorded,\n\trecorded,\n\tworkDuration,\n\ttype Assignee,\n\ttype Client,\n\ttype Consultation,\n\ttype ConsultationCategoryId,\n\ttype ConsultedAt,\n\ttype FollowUp,\n\ttype KarteId,\n\ttype KarteRepository,\n\ttype NonEmptyArray,\n\ttype Recorded,\n\ttype Resolution,\n\ttype SupportRecord,\n} from \"#domain\";\nimport { getDb } from \"./client\";\nimport { karteAssignees, kartes } from \"./schema\";\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\ntype KarteRow = typeof kartes.$inferSelect;\n\ntype KarteWithRelations = KarteRow & {\n\tkarteAssignees: (typeof karteAssignees.$inferSelect)[];\n};\n\n// ============================================================================\n// Domain ↔ DB Mapping\n// ============================================================================\n\nfunction toRecordedClient(row: KarteRow): Recorded<Client> {\n\tif (row.clientType === null) return notRecorded();\n\n\tif (row.clientName === null) {\n\t\tthrow new Error(`データ不整合: clientType=${row.clientType} だが clientName が null`);\n\t}\n\tconst name = row.clientName;\n\n\tswitch (row.clientType) {\n\t\tcase \"student\": {\n\t\t\tif (row.clientStudentId === null || row.clientAffiliation === null) {\n\t\t\t\tthrow new Error(\"データ不整合: student だが studentId または affiliation が null\");\n\t\t\t}\n\t\t\treturn recorded({\n\t\t\t\ttype: \"student\",\n\t\t\t\tname,\n\t\t\t\tstudentId: StudentId.fromString(row.clientStudentId),\n\t\t\t\taffiliation: row.clientAffiliation,\n\t\t\t} as Client);\n\t\t}\n\t\tcase \"teacher\":\n\t\t\treturn recorded({ type: \"teacher\", name });\n\t\tcase \"staff\":\n\t\t\treturn recorded({ type: \"staff\", name });\n\t\tcase \"other\":\n\t\t\treturn recorded({ type: \"other\", name });\n\t}\n}\n\n/**\n * 配列をNonEmptyArrayに変換する。\n * 要素が1つ以上あることを実行時に検証し、型を正しく導出する。\n */\nfunction toNonEmptyArray<T>(arr: T[]): NonEmptyArray<T> {\n\tconst [first, ...rest] = arr;\n\tif (first === undefined) {\n\t\tthrow new Error(\"Expected non-empty array\");\n\t}\n\treturn [first, ...rest];\n}\n\nfunction toConsultation(row: KarteRow): Consultation {\n\treturn {\n\t\tcategories:\n\t\t\trow.categoryIds.length > 0\n\t\t\t\t? recorded(\n\t\t\t\t\t\ttoNonEmptyArray(\n\t\t\t\t\t\t\trow.categoryIds.map((id) => {\n\t\t\t\t\t\t\t\tconst categoryId = id as ConsultationCategoryId;\n\t\t\t\t\t\t\t\tconst master = CONSULTATION_CATEGORIES.find((c) => c.id === categoryId);\n\t\t\t\t\t\t\t\treturn { id: categoryId, displayName: master?.displayName ?? id };\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t: notRecorded(),\n\t\ttargetDevice: row.targetDevice !== null ? recorded(row.targetDevice) : notRecorded(),\n\t\ttroubleDetails: row.troubleDetails !== null ? recorded(row.troubleDetails) : notRecorded(),\n\t};\n}\n\nfunction toResolution(row: KarteRow): Recorded<Resolution> {\n\tif (row.resolutionType === null) return notRecorded();\n\n\tswitch (row.resolutionType) {\n\t\tcase \"resolved\":\n\t\t\treturn recorded({ type: \"resolved\" });\n\t\tcase \"unresolved\":\n\t\t\treturn recorded({\n\t\t\t\ttype: \"unresolved\",\n\t\t\t\tfollowUp: row.followUp !== null ? recorded(row.followUp as FollowUp) : notRecorded(),\n\t\t\t});\n\t}\n}\n\nfunction toAssignee(row: typeof karteAssignees.$inferSelect): Assignee {\n\tif (row.assigneeType === \"resolved\") {\n\t\tif (row.memberId === null) {\n\t\t\tthrow new Error(\"データ不整合: assigneeType=resolved だが memberId が null\");\n\t\t}\n\t\treturn { type: \"resolved\", memberId: memberId(row.memberId) };\n\t}\n\tif (row.assigneeName === null) {\n\t\tthrow new Error(\"データ不整合: assigneeType=unresolved だが assigneeName が null\");\n\t}\n\treturn { type: \"unresolved\", name: row.assigneeName };\n}\n\nfunction toSupportRecord(row: KarteWithRelations): SupportRecord {\n\treturn {\n\t\tassignees:\n\t\t\trow.karteAssignees.length > 0\n\t\t\t\t? recorded(toNonEmptyArray(row.karteAssignees.map(toAssignee)))\n\t\t\t\t: notRecorded(),\n\t\tcontent: row.supportContent !== null ? recorded(row.supportContent) : notRecorded(),\n\t\tresolution: toResolution(row),\n\t\tworkDuration:\n\t\t\trow.workDurationMinutes !== null\n\t\t\t\t? recorded(workDuration(row.workDurationMinutes))\n\t\t\t\t: notRecorded(),\n\t};\n}\n\nfunction toDomain(row: KarteWithRelations): Karte {\n\treturn Karte.reconstruct({\n\t\tid: karteId(row.id),\n\t\trecordedAt: row.recordedAt,\n\t\tconsultedAt: deserializeConsultedAt(row.consultedAt, row.consultedAtPrecision),\n\t\tlastUpdatedAt: row.lastUpdatedAt,\n\t\tclient: toRecordedClient(row),\n\t\tconsent: {\n\t\t\tliabilityConsent: row.liabilityConsent,\n\t\t\tdisclosureConsent: row.disclosureConsent,\n\t\t},\n\t\tconsultation: toConsultation(row),\n\t\tsupportRecord: toSupportRecord(row),\n\t});\n}\n\n// ============================================================================\n// Persistence Helpers\n// ============================================================================\n\ntype KarteInsert = typeof kartes.$inferInsert;\n\nfunction clientToColumns(\n\tclient: Recorded<Client>,\n): Pick<KarteInsert, \"clientType\" | \"clientName\" | \"clientStudentId\" | \"clientAffiliation\"> {\n\tif (client.type === \"notRecorded\") {\n\t\treturn {\n\t\t\tclientType: null,\n\t\t\tclientName: null,\n\t\t\tclientStudentId: null,\n\t\t\tclientAffiliation: null,\n\t\t};\n\t}\n\tconst c = client.value;\n\treturn {\n\t\tclientType: c.type,\n\t\tclientName: c.name,\n\t\tclientStudentId: c.type === \"student\" ? c.studentId.getValue() : null,\n\t\tclientAffiliation: c.type === \"student\" ? c.affiliation : null,\n\t};\n}\n\nfunction resolutionToColumns(\n\tresolution: Recorded<Resolution>,\n): Pick<KarteInsert, \"resolutionType\" | \"followUp\"> {\n\tif (resolution.type === \"notRecorded\") {\n\t\treturn { resolutionType: null, followUp: null };\n\t}\n\tconst r = resolution.value;\n\tif (r.type === \"resolved\") {\n\t\treturn { resolutionType: \"resolved\", followUp: null };\n\t}\n\treturn {\n\t\tresolutionType: \"unresolved\",\n\t\tfollowUp: r.followUp.type === \"recorded\" ? r.followUp.value : null,\n\t};\n}\n\nfunction recordedToNullable<T>(r: Recorded<T>): T | null {\n\treturn r.type === \"recorded\" ? r.value : null;\n}\n\n/** DB → ConsultedAt の復元(精度情報を使って正確に復元する) */\nfunction deserializeConsultedAt(\n\tdate: Date | null,\n\tprecision: ConsultedAt[\"precision\"] | null,\n): Recorded<ConsultedAt> {\n\tif (date === null || precision === null) return notRecorded();\n\tswitch (precision) {\n\t\tcase \"year\":\n\t\t\treturn recorded({ precision: \"year\", year: date.getFullYear() });\n\t\tcase \"yearMonth\":\n\t\t\treturn recorded({\n\t\t\t\tprecision: \"yearMonth\",\n\t\t\t\tyear: date.getFullYear(),\n\t\t\t\tmonth: date.getMonth() + 1,\n\t\t\t});\n\t\tcase \"date\":\n\t\t\treturn recorded({ precision: \"date\", value: new Date(date) });\n\t\tcase \"datetime\":\n\t\t\treturn recorded({ precision: \"datetime\", value: new Date(date) });\n\t}\n}\n\n/** ConsultedAt → DBのtimestampカラム用Date | null */\nfunction consultedAtToDate(r: Recorded<ConsultedAt>): Date | null {\n\tif (r.type === \"notRecorded\") return null;\n\tconst ca = r.value;\n\tswitch (ca.precision) {\n\t\tcase \"datetime\":\n\t\tcase \"date\":\n\t\t\treturn ca.value;\n\t\tcase \"yearMonth\":\n\t\t\treturn new Date(ca.year, ca.month - 1, 1);\n\t\tcase \"year\":\n\t\t\treturn new Date(ca.year, 0, 1);\n\t}\n}\n\n/** ConsultedAt → 精度enum値 | null */\nfunction consultedAtToPrecision(r: Recorded<ConsultedAt>): ConsultedAt[\"precision\"] | null {\n\treturn r.type === \"recorded\" ? r.value.precision : null;\n}\n\n// ============================================================================\n// Repository Implementation\n// ============================================================================\n\nexport class DrizzleKarteRepository implements KarteRepository {\n\tasync findById(id: KarteId): Promise<Karte | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.kartes.findFirst({\n\t\t\twhere: eq(kartes.id, id as string),\n\t\t\twith: {\n\t\t\t\tkarteAssignees: true,\n\t\t\t},\n\t\t});\n\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findAll(): Promise<Karte[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.kartes.findMany({\n\t\t\twith: {\n\t\t\t\tkarteAssignees: true,\n\t\t\t},\n\t\t\torderBy: (kartes, { desc }) => [desc(kartes.recordedAt)],\n\t\t});\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync save(karte: Karte): Promise<void> {\n\t\tconst db = getDb();\n\n\t\tconst clientCols = clientToColumns(karte.client);\n\t\tconst resCols = resolutionToColumns(karte.supportRecord.resolution);\n\n\t\tconst values = {\n\t\t\tid: karte.id as string,\n\t\t\trecordedAt: karte.recordedAt,\n\t\t\tconsultedAt: consultedAtToDate(karte.consultedAt),\n\t\t\tconsultedAtPrecision: consultedAtToPrecision(karte.consultedAt),\n\t\t\tlastUpdatedAt: karte.lastUpdatedAt,\n\t\t\tclientType: clientCols.clientType,\n\t\t\tclientName: clientCols.clientName,\n\t\t\tclientStudentId: clientCols.clientStudentId,\n\t\t\tclientAffiliation: clientCols.clientAffiliation,\n\t\t\tliabilityConsent: karte.consent.liabilityConsent,\n\t\t\tdisclosureConsent: karte.consent.disclosureConsent,\n\t\t\tcategoryIds:\n\t\t\t\tkarte.consultation.categories.type === \"recorded\"\n\t\t\t\t\t? karte.consultation.categories.value.map((c) => c.id)\n\t\t\t\t\t: [],\n\t\t\ttroubleDetails: recordedToNullable(karte.consultation.troubleDetails),\n\t\t\ttargetDevice: recordedToNullable(karte.consultation.targetDevice),\n\t\t\tsupportContent: recordedToNullable(karte.supportRecord.content),\n\t\t\tresolutionType: resCols.resolutionType,\n\t\t\tfollowUp: resCols.followUp,\n\t\t\tworkDurationMinutes: recordedToNullable(karte.supportRecord.workDuration),\n\t\t};\n\n\t\tconst { id: _, ...updateValues } = values;\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\t// Upsert karte\n\t\t\tawait tx.insert(kartes).values(values).onConflictDoUpdate({\n\t\t\t\ttarget: kartes.id,\n\t\t\t\tset: updateValues,\n\t\t\t});\n\n\t\t\t// Sync assignees (delete-all-then-insert)\n\t\t\tawait tx.delete(karteAssignees).where(eq(karteAssignees.karteId, karte.id as string));\n\n\t\t\tif (karte.supportRecord.assignees.type === \"recorded\") {\n\t\t\t\tconst assigneeRows = karte.supportRecord.assignees.value.map((assignee) => ({\n\t\t\t\t\tkarteId: karte.id as string,\n\t\t\t\t\tassigneeType: assignee.type as Assignee[\"type\"],\n\t\t\t\t\tmemberId: assignee.type === \"resolved\" ? (assignee.memberId as string) : null,\n\t\t\t\t\tassigneeName: assignee.type === \"unresolved\" ? assignee.name : null,\n\t\t\t\t}));\n\t\t\t\tawait tx.insert(karteAssignees).values(assigneeRows);\n\t\t\t}\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DrizzleKarteRepository.cjs","names":["notRecorded","recorded","StudentId","CONSULTATION_CATEGORIES","memberId","workDuration","Karte","karteId","getDb","kartes","karteAssignees"],"sources":["../../../src/infrastructure/drizzle/DrizzleKarteRepository.ts"],"sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n\tCONSULTATION_CATEGORIES,\n\tKarte,\n\tStudentId,\n\tkarteId,\n\tmemberId,\n\tnotRecorded,\n\trecorded,\n\tworkDuration,\n\ttype Assignee,\n\ttype Client,\n\ttype Consultation,\n\ttype ConsultationCategoryId,\n\ttype ConsultedAt,\n\ttype FollowUp,\n\ttype KarteId,\n\ttype KarteRepository,\n\ttype NonEmptyArray,\n\ttype Recorded,\n\ttype Resolution,\n\ttype SupportRecord,\n} from \"#domain\";\nimport { getDb } from \"./client\";\nimport { karteAssignees, kartes } from \"./schema\";\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\ntype KarteRow = typeof kartes.$inferSelect;\n\ntype KarteWithRelations = KarteRow & {\n\tkarteAssignees: (typeof karteAssignees.$inferSelect)[];\n};\n\n// ============================================================================\n// Domain ↔ DB Mapping\n// ============================================================================\n\nfunction toRecordedClient(row: KarteRow): Recorded<Client> {\n\tif (row.clientType === null) return notRecorded();\n\n\tif (row.clientName === null) {\n\t\tthrow new Error(`データ不整合: clientType=${row.clientType} だが clientName が null`);\n\t}\n\tconst name = row.clientName;\n\n\tswitch (row.clientType) {\n\t\tcase \"student\": {\n\t\t\tif (row.clientStudentId === null || row.clientAffiliation === null) {\n\t\t\t\tthrow new Error(\"データ不整合: student だが studentId または affiliation が null\");\n\t\t\t}\n\t\t\treturn recorded({\n\t\t\t\ttype: \"student\",\n\t\t\t\tname,\n\t\t\t\tstudentId: StudentId.fromString(row.clientStudentId),\n\t\t\t\taffiliation: row.clientAffiliation,\n\t\t\t} as Client);\n\t\t}\n\t\tcase \"teacher\":\n\t\t\treturn recorded({ type: \"teacher\", name });\n\t\tcase \"staff\":\n\t\t\treturn recorded({ type: \"staff\", name });\n\t\tcase \"other\":\n\t\t\treturn recorded({ type: \"other\", name });\n\t}\n}\n\n/**\n * 配列をNonEmptyArrayに変換する。\n * 要素が1つ以上あることを実行時に検証し、型を正しく導出する。\n */\nfunction toNonEmptyArray<T>(arr: T[]): NonEmptyArray<T> {\n\tconst [first, ...rest] = arr;\n\tif (first === undefined) {\n\t\tthrow new Error(\"Expected non-empty array\");\n\t}\n\treturn [first, ...rest];\n}\n\nfunction toConsultation(row: KarteRow): Consultation {\n\treturn {\n\t\tcategories:\n\t\t\trow.categoryIds.length > 0\n\t\t\t\t? recorded(\n\t\t\t\t\t\ttoNonEmptyArray(\n\t\t\t\t\t\t\trow.categoryIds.map((id) => {\n\t\t\t\t\t\t\t\tconst categoryId = id as ConsultationCategoryId;\n\t\t\t\t\t\t\t\tconst master = CONSULTATION_CATEGORIES.find((c) => c.id === categoryId);\n\t\t\t\t\t\t\t\treturn { id: categoryId, displayName: master?.displayName ?? id };\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t: notRecorded(),\n\t\ttargetDevice: row.targetDevice !== null ? recorded(row.targetDevice) : notRecorded(),\n\t\ttroubleDetails: row.troubleDetails !== null ? recorded(row.troubleDetails) : notRecorded(),\n\t};\n}\n\nfunction toResolution(row: KarteRow): Recorded<Resolution> {\n\tif (row.resolutionType === null) return notRecorded();\n\n\tswitch (row.resolutionType) {\n\t\tcase \"resolved\":\n\t\t\treturn recorded({ type: \"resolved\" });\n\t\tcase \"unresolved\":\n\t\t\treturn recorded({\n\t\t\t\ttype: \"unresolved\",\n\t\t\t\tfollowUp: row.followUp !== null ? recorded(row.followUp as FollowUp) : notRecorded(),\n\t\t\t});\n\t}\n}\n\nfunction toAssignee(row: typeof karteAssignees.$inferSelect): Assignee {\n\tif (row.assigneeType === \"resolved\") {\n\t\tif (row.memberId === null) {\n\t\t\tthrow new Error(\"データ不整合: assigneeType=resolved だが memberId が null\");\n\t\t}\n\t\treturn { type: \"resolved\", memberId: memberId(row.memberId) };\n\t}\n\tif (row.assigneeName === null) {\n\t\tthrow new Error(\"データ不整合: assigneeType=unresolved だが assigneeName が null\");\n\t}\n\treturn { type: \"unresolved\", name: row.assigneeName };\n}\n\nfunction toSupportRecord(row: KarteWithRelations): SupportRecord {\n\treturn {\n\t\tassignees:\n\t\t\trow.karteAssignees.length > 0\n\t\t\t\t? recorded(toNonEmptyArray(row.karteAssignees.map(toAssignee)))\n\t\t\t\t: notRecorded(),\n\t\tcontent: row.supportContent !== null ? recorded(row.supportContent) : notRecorded(),\n\t\tresolution: toResolution(row),\n\t\tworkDuration:\n\t\t\trow.workDurationMinutes !== null\n\t\t\t\t? recorded(workDuration(row.workDurationMinutes))\n\t\t\t\t: notRecorded(),\n\t};\n}\n\nfunction toDomain(row: KarteWithRelations): Karte {\n\treturn Karte.reconstruct({\n\t\tid: karteId(row.id),\n\t\trecordedAt: row.recordedAt,\n\t\tconsultedAt: deserializeConsultedAt(row.consultedAt, row.consultedAtPrecision),\n\t\tlastUpdatedAt: row.lastUpdatedAt,\n\t\tclient: toRecordedClient(row),\n\t\tconsent: {\n\t\t\tliabilityConsent: row.liabilityConsent,\n\t\t\tdisclosureConsent: row.disclosureConsent,\n\t\t},\n\t\tconsultation: toConsultation(row),\n\t\tsupportRecord: toSupportRecord(row),\n\t});\n}\n\n// ============================================================================\n// Persistence Helpers\n// ============================================================================\n\ntype KarteInsert = typeof kartes.$inferInsert;\n\nfunction clientToColumns(\n\tclient: Recorded<Client>,\n): Pick<KarteInsert, \"clientType\" | \"clientName\" | \"clientStudentId\" | \"clientAffiliation\"> {\n\tif (client.type === \"notRecorded\") {\n\t\treturn {\n\t\t\tclientType: null,\n\t\t\tclientName: null,\n\t\t\tclientStudentId: null,\n\t\t\tclientAffiliation: null,\n\t\t};\n\t}\n\tconst c = client.value;\n\treturn {\n\t\tclientType: c.type,\n\t\tclientName: c.name,\n\t\tclientStudentId: c.type === \"student\" ? c.studentId.getValue() : null,\n\t\tclientAffiliation: c.type === \"student\" ? c.affiliation : null,\n\t};\n}\n\nfunction resolutionToColumns(\n\tresolution: Recorded<Resolution>,\n): Pick<KarteInsert, \"resolutionType\" | \"followUp\"> {\n\tif (resolution.type === \"notRecorded\") {\n\t\treturn { resolutionType: null, followUp: null };\n\t}\n\tconst r = resolution.value;\n\tif (r.type === \"resolved\") {\n\t\treturn { resolutionType: \"resolved\", followUp: null };\n\t}\n\treturn {\n\t\tresolutionType: \"unresolved\",\n\t\tfollowUp: r.followUp.type === \"recorded\" ? r.followUp.value : null,\n\t};\n}\n\nfunction recordedToNullable<T>(r: Recorded<T>): T | null {\n\treturn r.type === \"recorded\" ? r.value : null;\n}\n\n/** DB → ConsultedAt の復元(精度情報を使って正確に復元する) */\nfunction deserializeConsultedAt(\n\tdate: Date | null,\n\tprecision: ConsultedAt[\"precision\"] | null,\n): Recorded<ConsultedAt> {\n\tif (date === null || precision === null) return notRecorded();\n\tswitch (precision) {\n\t\tcase \"year\":\n\t\t\treturn recorded({ precision: \"year\", year: date.getFullYear() });\n\t\tcase \"yearMonth\":\n\t\t\treturn recorded({\n\t\t\t\tprecision: \"yearMonth\",\n\t\t\t\tyear: date.getFullYear(),\n\t\t\t\tmonth: date.getMonth() + 1,\n\t\t\t});\n\t\tcase \"date\":\n\t\t\treturn recorded({ precision: \"date\", value: new Date(date) });\n\t\tcase \"datetime\":\n\t\t\treturn recorded({ precision: \"datetime\", value: new Date(date) });\n\t}\n}\n\n/** ConsultedAt → DBのtimestampカラム用Date | null */\nfunction consultedAtToDate(r: Recorded<ConsultedAt>): Date | null {\n\tif (r.type === \"notRecorded\") return null;\n\tconst ca = r.value;\n\tswitch (ca.precision) {\n\t\tcase \"datetime\":\n\t\tcase \"date\":\n\t\t\treturn ca.value;\n\t\tcase \"yearMonth\":\n\t\t\treturn new Date(ca.year, ca.month - 1, 1);\n\t\tcase \"year\":\n\t\t\treturn new Date(ca.year, 0, 1);\n\t}\n}\n\n/** ConsultedAt → 精度enum値 | null */\nfunction consultedAtToPrecision(r: Recorded<ConsultedAt>): ConsultedAt[\"precision\"] | null {\n\treturn r.type === \"recorded\" ? r.value.precision : null;\n}\n\n// ============================================================================\n// Repository Implementation\n// ============================================================================\n\nexport class DrizzleKarteRepository implements KarteRepository {\n\tasync findById(id: KarteId): Promise<Karte | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.kartes.findFirst({\n\t\t\twhere: eq(kartes.id, id as string),\n\t\t\twith: {\n\t\t\t\tkarteAssignees: true,\n\t\t\t},\n\t\t});\n\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findAll(): Promise<Karte[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.kartes.findMany({\n\t\t\twith: {\n\t\t\t\tkarteAssignees: true,\n\t\t\t},\n\t\t\torderBy: (kartes, { desc }) => [desc(kartes.recordedAt)],\n\t\t});\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync save(karte: Karte): Promise<void> {\n\t\tconst db = getDb();\n\n\t\tconst clientCols = clientToColumns(karte.client);\n\t\tconst resCols = resolutionToColumns(karte.supportRecord.resolution);\n\n\t\tconst values = {\n\t\t\tid: karte.id as string,\n\t\t\trecordedAt: karte.recordedAt,\n\t\t\tconsultedAt: consultedAtToDate(karte.consultedAt),\n\t\t\tconsultedAtPrecision: consultedAtToPrecision(karte.consultedAt),\n\t\t\tlastUpdatedAt: karte.lastUpdatedAt,\n\t\t\tclientType: clientCols.clientType,\n\t\t\tclientName: clientCols.clientName,\n\t\t\tclientStudentId: clientCols.clientStudentId,\n\t\t\tclientAffiliation: clientCols.clientAffiliation,\n\t\t\tliabilityConsent: karte.consent.liabilityConsent,\n\t\t\tdisclosureConsent: karte.consent.disclosureConsent,\n\t\t\tcategoryIds:\n\t\t\t\tkarte.consultation.categories.type === \"recorded\"\n\t\t\t\t\t? karte.consultation.categories.value.map((c) => c.id)\n\t\t\t\t\t: [],\n\t\t\ttroubleDetails: recordedToNullable(karte.consultation.troubleDetails),\n\t\t\ttargetDevice: recordedToNullable(karte.consultation.targetDevice),\n\t\t\tsupportContent: recordedToNullable(karte.supportRecord.content),\n\t\t\tresolutionType: resCols.resolutionType,\n\t\t\tfollowUp: resCols.followUp,\n\t\t\tworkDurationMinutes: recordedToNullable(karte.supportRecord.workDuration),\n\t\t};\n\n\t\tconst { id: _, ...updateValues } = values;\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\t// Upsert karte\n\t\t\tawait tx.insert(kartes).values(values).onConflictDoUpdate({\n\t\t\t\ttarget: kartes.id,\n\t\t\t\tset: updateValues,\n\t\t\t});\n\n\t\t\t// Sync assignees (delete-all-then-insert)\n\t\t\tawait tx.delete(karteAssignees).where(eq(karteAssignees.karteId, karte.id as string));\n\n\t\t\tif (karte.supportRecord.assignees.type === \"recorded\") {\n\t\t\t\tconst assigneeRows = karte.supportRecord.assignees.value.map((assignee) => ({\n\t\t\t\t\tkarteId: karte.id as string,\n\t\t\t\t\tassigneeType: assignee.type as Assignee[\"type\"],\n\t\t\t\t\tmemberId: assignee.type === \"resolved\" ? (assignee.memberId as string) : null,\n\t\t\t\t\tassigneeName: assignee.type === \"unresolved\" ? assignee.name : null,\n\t\t\t\t}));\n\t\t\t\tawait tx.insert(karteAssignees).values(assigneeRows);\n\t\t\t}\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DrizzleMemberRepository.cjs","names":["memberId","UniversityEmail","recorded","Email","notRecorded","ActiveMember","StudentId","UnconfirmedMember","FormerMember","getDb","members","memberDomainEvents","serializeMemberEventPayload"],"sources":["../../../src/infrastructure/drizzle/DrizzleMemberRepository.ts"],"sourcesContent":["import { v4 as uuid } from \"uuid\";\nimport { eq } from \"drizzle-orm\";\nimport {\n\tActiveMember,\n\tEmail,\n\tFormerMember,\n\tStudentId,\n\tUnconfirmedMember,\n\tUniversityEmail,\n\tmemberId,\n\tnotRecorded,\n\trecorded,\n\ttype CompleteAffiliation,\n\ttype Member,\n\ttype MemberId,\n\ttype MemberRepository,\n\ttype Recorded,\n} from \"#domain\";\nimport { getDb } from \"./client\";\nimport { memberDomainEvents, members } from \"./schema\";\nimport { serializeMemberEventPayload } from \"./serializeMemberEvent\";\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\ntype MemberRow = typeof members.$inferSelect;\n\n// ============================================================================\n// Domain ↔ DB Mapping\n// ============================================================================\n\nfunction toDomain(row: MemberRow): Member {\n\tconst id = memberId(row.id);\n\tconst email = new UniversityEmail(row.email);\n\tconst name = row.name;\n\tconst personalEmail: Recorded<Email> =\n\t\trow.personalEmail !== null ? recorded(new Email(row.personalEmail)) : notRecorded();\n\n\tswitch (row.status) {\n\t\tcase \"active\": {\n\t\t\tif (row.affiliation === null || row.studentId === null) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`データ不整合: status=active だが affiliation または studentId が null (id=${row.id})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ActiveMember.reconstruct({\n\t\t\t\tid,\n\t\t\t\temail,\n\t\t\t\tname,\n\t\t\t\tpersonalEmail,\n\t\t\t\tstudentId: StudentId.fromString(row.studentId),\n\t\t\t\taffiliation: row.affiliation as CompleteAffiliation,\n\t\t\t});\n\t\t}\n\t\tcase \"unconfirmed\":\n\t\t\treturn UnconfirmedMember.reconstruct({ id, email, name, personalEmail });\n\t\tcase \"former\":\n\t\t\treturn FormerMember.reconstruct({ id, email, name, personalEmail });\n\t}\n}\n\n// ============================================================================\n// Persistence Helpers\n// ============================================================================\n\ntype MemberInsert = typeof members.$inferInsert;\n\nfunction toInsertValues(member: Member): MemberInsert {\n\tconst base = {\n\t\tid: member.id as string,\n\t\tname: member.name,\n\t\temail: member.email.getValue(),\n\t\tpersonalEmail:\n\t\t\tmember.personalEmail.type === \"recorded\" ? member.personalEmail.value.getValue() : null,\n\t\tstatus: member.status,\n\t\tupdatedAt: new Date().toISOString(),\n\t};\n\n\tswitch (member.status) {\n\t\tcase \"active\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tstudentId: member.studentId.getValue(),\n\t\t\t\taffiliation: member.affiliation,\n\t\t\t};\n\t\tcase \"unconfirmed\":\n\t\tcase \"former\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tstudentId: null,\n\t\t\t\taffiliation: null,\n\t\t\t};\n\t}\n}\n\n// ============================================================================\n// Repository Implementation\n// ============================================================================\n\nexport class DrizzleMemberRepository implements MemberRepository {\n\tasync findById(id: MemberId): Promise<Member | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.members.findFirst({\n\t\t\twhere: eq(members.id, id as string),\n\t\t});\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findByEmail(email: UniversityEmail): Promise<Member | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.members.findFirst({\n\t\t\twhere: eq(members.email, email.getValue()),\n\t\t});\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findAll(): Promise<Member[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.members.findMany();\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync save(member: Member): Promise<void> {\n\t\tconst db = getDb();\n\t\tconst values = toInsertValues(member);\n\t\tconst events = member.getDomainEvents();\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\tawait tx\n\t\t\t\t.insert(members)\n\t\t\t\t.values(values)\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: members.id,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tname: values.name,\n\t\t\t\t\t\temail: values.email,\n\t\t\t\t\t\tpersonalEmail: values.personalEmail,\n\t\t\t\t\t\tstatus: values.status,\n\t\t\t\t\t\tstudentId: values.studentId,\n\t\t\t\t\t\taffiliation: values.affiliation,\n\t\t\t\t\t\tupdatedAt: values.updatedAt,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\tif (events.length > 0) {\n\t\t\t\tawait tx.insert(memberDomainEvents).values(\n\t\t\t\t\tevents.map((event) => ({\n\t\t\t\t\t\tid: uuid(),\n\t\t\t\t\t\tmemberId: event.id as string,\n\t\t\t\t\t\temail: event.email.getValue(),\n\t\t\t\t\t\teventName: event.eventName,\n\t\t\t\t\t\tpayload: serializeMemberEventPayload(event),\n\t\t\t\t\t\toccurredAt: event.occurredAt.toISOString(),\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DrizzleMemberRepository.cjs","names":["memberId","UniversityEmail","recorded","Email","notRecorded","ActiveMember","StudentId","UnconfirmedMember","FormerMember","getDb","members","memberDomainEvents","serializeMemberEventPayload"],"sources":["../../../src/infrastructure/drizzle/DrizzleMemberRepository.ts"],"sourcesContent":["import { v4 as uuid } from \"uuid\";\nimport { eq } from \"drizzle-orm\";\nimport {\n\tActiveMember,\n\tEmail,\n\tFormerMember,\n\tStudentId,\n\tUnconfirmedMember,\n\tUniversityEmail,\n\tmemberId,\n\tnotRecorded,\n\trecorded,\n\ttype CompleteAffiliation,\n\ttype Member,\n\ttype MemberId,\n\ttype MemberRepository,\n\ttype Recorded,\n} from \"#domain\";\nimport { getDb } from \"./client\";\nimport { memberDomainEvents, members } from \"./schema\";\nimport { serializeMemberEventPayload } from \"./serializeMemberEvent\";\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\ntype MemberRow = typeof members.$inferSelect;\n\n// ============================================================================\n// Domain ↔ DB Mapping\n// ============================================================================\n\nfunction toDomain(row: MemberRow): Member {\n\tconst id = memberId(row.id);\n\tconst email = new UniversityEmail(row.email);\n\tconst name = row.name;\n\tconst personalEmail: Recorded<Email> =\n\t\trow.personalEmail !== null ? recorded(new Email(row.personalEmail)) : notRecorded();\n\n\tswitch (row.status) {\n\t\tcase \"active\": {\n\t\t\tif (row.affiliation === null || row.studentId === null) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`データ不整合: status=active だが affiliation または studentId が null (id=${row.id})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ActiveMember.reconstruct({\n\t\t\t\tid,\n\t\t\t\temail,\n\t\t\t\tname,\n\t\t\t\tpersonalEmail,\n\t\t\t\tstudentId: StudentId.fromString(row.studentId),\n\t\t\t\taffiliation: row.affiliation as CompleteAffiliation,\n\t\t\t});\n\t\t}\n\t\tcase \"unconfirmed\":\n\t\t\treturn UnconfirmedMember.reconstruct({ id, email, name, personalEmail });\n\t\tcase \"former\":\n\t\t\treturn FormerMember.reconstruct({ id, email, name, personalEmail });\n\t}\n}\n\n// ============================================================================\n// Persistence Helpers\n// ============================================================================\n\ntype MemberInsert = typeof members.$inferInsert;\n\nfunction toInsertValues(member: Member): MemberInsert {\n\tconst base = {\n\t\tid: member.id as string,\n\t\tname: member.name,\n\t\temail: member.email.getValue(),\n\t\tpersonalEmail:\n\t\t\tmember.personalEmail.type === \"recorded\" ? member.personalEmail.value.getValue() : null,\n\t\tstatus: member.status,\n\t\tupdatedAt: new Date().toISOString(),\n\t};\n\n\tswitch (member.status) {\n\t\tcase \"active\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tstudentId: member.studentId.getValue(),\n\t\t\t\taffiliation: member.affiliation,\n\t\t\t};\n\t\tcase \"unconfirmed\":\n\t\tcase \"former\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tstudentId: null,\n\t\t\t\taffiliation: null,\n\t\t\t};\n\t}\n}\n\n// ============================================================================\n// Repository Implementation\n// ============================================================================\n\nexport class DrizzleMemberRepository implements MemberRepository {\n\tasync findById(id: MemberId): Promise<Member | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.members.findFirst({\n\t\t\twhere: eq(members.id, id as string),\n\t\t});\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findByEmail(email: UniversityEmail): Promise<Member | null> {\n\t\tconst db = getDb();\n\t\tconst row = await db.query.members.findFirst({\n\t\t\twhere: eq(members.email, email.getValue()),\n\t\t});\n\t\tif (!row) return null;\n\t\treturn toDomain(row);\n\t}\n\n\tasync findAll(): Promise<Member[]> {\n\t\tconst db = getDb();\n\t\tconst rows = await db.query.members.findMany();\n\t\treturn rows.map(toDomain);\n\t}\n\n\tasync save(member: Member): Promise<void> {\n\t\tconst db = getDb();\n\t\tconst values = toInsertValues(member);\n\t\tconst events = member.getDomainEvents();\n\n\t\tawait db.transaction(async (tx) => {\n\t\t\tawait tx\n\t\t\t\t.insert(members)\n\t\t\t\t.values(values)\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: members.id,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tname: values.name,\n\t\t\t\t\t\temail: values.email,\n\t\t\t\t\t\tpersonalEmail: values.personalEmail,\n\t\t\t\t\t\tstatus: values.status,\n\t\t\t\t\t\tstudentId: values.studentId,\n\t\t\t\t\t\taffiliation: values.affiliation,\n\t\t\t\t\t\tupdatedAt: values.updatedAt,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\tif (events.length > 0) {\n\t\t\t\tawait tx.insert(memberDomainEvents).values(\n\t\t\t\t\tevents.map((event) => ({\n\t\t\t\t\t\tid: uuid(),\n\t\t\t\t\t\tmemberId: event.id as string,\n\t\t\t\t\t\temail: event.email.getValue(),\n\t\t\t\t\t\teventName: event.eventName,\n\t\t\t\t\t\tpayload: serializeMemberEventPayload(event),\n\t\t\t\t\t\toccurredAt: event.occurredAt.toISOString(),\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAgCA,SAAS,SAAS,KAAwB;CACzC,MAAM,KAAKA,iBAAAA,SAAS,IAAI,GAAG;CAC3B,MAAM,QAAQ,IAAIC,wBAAAA,gBAAgB,IAAI,MAAM;CAC5C,MAAM,OAAO,IAAI;CACjB,MAAM,gBACL,IAAI,kBAAkB,OAAOC,iBAAAA,SAAS,IAAIC,cAAAA,MAAM,IAAI,cAAc,CAAC,GAAGC,iBAAAA,aAAa;AAEpF,SAAQ,IAAI,QAAZ;EACC,KAAK;AACJ,OAAI,IAAI,gBAAgB,QAAQ,IAAI,cAAc,KACjD,OAAM,IAAI,MACT,iEAAiE,IAAI,GAAG,GACxE;AAEF,UAAOC,eAAAA,aAAa,YAAY;IAC/B;IACA;IACA;IACA;IACA,WAAWC,kBAAAA,UAAU,WAAW,IAAI,UAAU;IAC9C,aAAa,IAAI;IACjB,CAAC;EAEH,KAAK,cACJ,QAAOC,eAAAA,kBAAkB,YAAY;GAAE;GAAI;GAAO;GAAM;GAAe,CAAC;EACzE,KAAK,SACJ,QAAOC,eAAAA,aAAa,YAAY;GAAE;GAAI;GAAO;GAAM;GAAe,CAAC;;;AAUtE,SAAS,eAAe,QAA8B;CACrD,MAAM,OAAO;EACZ,IAAI,OAAO;EACX,MAAM,OAAO;EACb,OAAO,OAAO,MAAM,UAAU;EAC9B,eACC,OAAO,cAAc,SAAS,aAAa,OAAO,cAAc,MAAM,UAAU,GAAG;EACpF,QAAQ,OAAO;EACf,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;AAED,SAAQ,OAAO,QAAf;EACC,KAAK,SACJ,QAAO;GACN,GAAG;GACH,WAAW,OAAO,UAAU,UAAU;GACtC,aAAa,OAAO;GACpB;EACF,KAAK;EACL,KAAK,SACJ,QAAO;GACN,GAAG;GACH,WAAW;GACX,aAAa;GACb;;;AAQJ,IAAa,0BAAb,MAAiE;CAChE,MAAM,SAAS,IAAsC;EAEpD,MAAM,MAAM,MADDC,eAAAA,OAAO,CACG,MAAM,QAAQ,UAAU,EAC5C,QAAA,GAAA,YAAA,IAAUC,eAAAA,QAAQ,IAAI,GAAa,EACnC,CAAC;AACF,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,SAAS,IAAI;;CAGrB,MAAM,YAAY,OAAgD;EAEjE,MAAM,MAAM,MADDD,eAAAA,OAAO,CACG,MAAM,QAAQ,UAAU,EAC5C,QAAA,GAAA,YAAA,IAAUC,eAAAA,QAAQ,OAAO,MAAM,UAAU,CAAC,EAC1C,CAAC;AACF,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,SAAS,IAAI;;CAGrB,MAAM,UAA6B;AAGlC,UADa,MADFD,eAAAA,OAAO,CACI,MAAM,QAAQ,UAAU,EAClC,IAAI,SAAS;;CAG1B,MAAM,KAAK,QAA+B;EACzC,MAAM,KAAKA,eAAAA,OAAO;EAClB,MAAM,SAAS,eAAe,OAAO;EACrC,MAAM,SAAS,OAAO,iBAAiB;AAEvC,QAAM,GAAG,YAAY,OAAO,OAAO;AAClC,SAAM,GACJ,OAAOC,eAAAA,QAAQ,CACf,OAAO,OAAO,CACd,mBAAmB;IACnB,QAAQA,eAAAA,QAAQ;IAChB,KAAK;KACJ,MAAM,OAAO;KACb,OAAO,OAAO;KACd,eAAe,OAAO;KACtB,QAAQ,OAAO;KACf,WAAW,OAAO;KAClB,aAAa,OAAO;KACpB,WAAW,OAAO;KAClB;IACD,CAAC;AAEH,OAAI,OAAO,SAAS,EACnB,OAAM,GAAG,OAAOC,eAAAA,mBAAmB,CAAC,OACnC,OAAO,KAAK,WAAW;IACtB,KAAA,GAAA,KAAA,KAAU;IACV,UAAU,MAAM;IAChB,OAAO,MAAM,MAAM,UAAU;IAC7B,WAAW,MAAM;IACjB,SAASC,6BAAAA,4BAA4B,MAAM;IAC3C,YAAY,MAAM,WAAW,aAAa;IAC1C,EAAE,CACH;IAED"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
1
2
|
const require_schema = require("./schema.cjs");
|
|
2
|
-
let
|
|
3
|
-
let
|
|
3
|
+
let drizzle_orm_postgres_js = require("drizzle-orm/postgres-js");
|
|
4
|
+
let postgres = require("postgres");
|
|
5
|
+
postgres = require_runtime.__toESM(postgres);
|
|
4
6
|
//#region src/infrastructure/drizzle/client.ts
|
|
5
|
-
let
|
|
6
|
-
function
|
|
7
|
-
if (!
|
|
7
|
+
let client = null;
|
|
8
|
+
function getClient() {
|
|
9
|
+
if (!client) {
|
|
8
10
|
const connectionString = process.env.DATABASE_URL;
|
|
9
11
|
if (!connectionString) throw new Error("DATABASE_URL environment variable is not set");
|
|
10
|
-
|
|
12
|
+
client = (0, postgres.default)(connectionString, { prepare: false });
|
|
11
13
|
}
|
|
12
|
-
return
|
|
14
|
+
return client;
|
|
13
15
|
}
|
|
14
16
|
function getDb() {
|
|
15
|
-
return (0,
|
|
17
|
+
return (0, drizzle_orm_postgres_js.drizzle)(getClient(), { schema: require_schema.schema_exports });
|
|
16
18
|
}
|
|
17
19
|
//#endregion
|
|
18
20
|
exports.getDb = getDb;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.cjs","names":[
|
|
1
|
+
{"version":3,"file":"client.cjs","names":[],"sources":["../../../src/infrastructure/drizzle/client.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport postgres, { type Sql } from \"postgres\";\nimport * as schema from \"./schema\";\n\nlet client: Sql | null = null;\n\nfunction getClient(): Sql {\n\tif (!client) {\n\t\tconst connectionString = process.env.DATABASE_URL;\n\t\tif (!connectionString) {\n\t\t\tthrow new Error(\"DATABASE_URL environment variable is not set\");\n\t\t}\n\t\t// Supabase の Transaction pool mode は prepared statement をサポートしないため無効化\n\t\tclient = postgres(connectionString, { prepare: false });\n\t}\n\treturn client;\n}\n\nexport function getDb() {\n\treturn drizzle(getClient(), { schema });\n}\n\nexport type DrizzleDb = ReturnType<typeof getDb>;\n"],"mappings":";;;;;;AAKA,IAAI,SAAqB;AAEzB,SAAS,YAAiB;AACzB,KAAI,CAAC,QAAQ;EACZ,MAAM,mBAAmB,QAAQ,IAAI;AACrC,MAAI,CAAC,iBACJ,OAAM,IAAI,MAAM,+CAA+C;AAGhE,YAAA,GAAA,SAAA,SAAkB,kBAAkB,EAAE,SAAS,OAAO,CAAC;;AAExD,QAAO;;AAGR,SAAgB,QAAQ;AACvB,SAAA,GAAA,wBAAA,SAAe,WAAW,EAAE,EAAE,QAAA,eAAA,gBAAQ,CAAC"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { schema_exports } from "./schema.mjs";
|
|
2
|
-
import { drizzle } from "drizzle-orm/
|
|
3
|
-
import
|
|
2
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
3
|
+
import postgres from "postgres";
|
|
4
4
|
//#region src/infrastructure/drizzle/client.ts
|
|
5
|
-
let
|
|
6
|
-
function
|
|
7
|
-
if (!
|
|
5
|
+
let client = null;
|
|
6
|
+
function getClient() {
|
|
7
|
+
if (!client) {
|
|
8
8
|
const connectionString = process.env.DATABASE_URL;
|
|
9
9
|
if (!connectionString) throw new Error("DATABASE_URL environment variable is not set");
|
|
10
|
-
|
|
10
|
+
client = postgres(connectionString, { prepare: false });
|
|
11
11
|
}
|
|
12
|
-
return
|
|
12
|
+
return client;
|
|
13
13
|
}
|
|
14
14
|
function getDb() {
|
|
15
|
-
return drizzle(
|
|
15
|
+
return drizzle(getClient(), { schema: schema_exports });
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
18
|
export { getDb };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/infrastructure/drizzle/client.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/infrastructure/drizzle/client.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport postgres, { type Sql } from \"postgres\";\nimport * as schema from \"./schema\";\n\nlet client: Sql | null = null;\n\nfunction getClient(): Sql {\n\tif (!client) {\n\t\tconst connectionString = process.env.DATABASE_URL;\n\t\tif (!connectionString) {\n\t\t\tthrow new Error(\"DATABASE_URL environment variable is not set\");\n\t\t}\n\t\t// Supabase の Transaction pool mode は prepared statement をサポートしないため無効化\n\t\tclient = postgres(connectionString, { prepare: false });\n\t}\n\treturn client;\n}\n\nexport function getDb() {\n\treturn drizzle(getClient(), { schema });\n}\n\nexport type DrizzleDb = ReturnType<typeof getDb>;\n"],"mappings":";;;;AAKA,IAAI,SAAqB;AAEzB,SAAS,YAAiB;AACzB,KAAI,CAAC,QAAQ;EACZ,MAAM,mBAAmB,QAAQ,IAAI;AACrC,MAAI,CAAC,iBACJ,OAAM,IAAI,MAAM,+CAA+C;AAGhE,WAAS,SAAS,kBAAkB,EAAE,SAAS,OAAO,CAAC;;AAExD,QAAO;;AAGR,SAAgB,QAAQ;AACvB,QAAO,QAAQ,WAAW,EAAE,EAAE,QAAA,gBAAQ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shizuoka-its/core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "ITS core library",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "ISC",
|
|
@@ -53,12 +53,11 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"drizzle-orm": "^0.45.1",
|
|
56
|
-
"
|
|
56
|
+
"postgres": "^3.4.8",
|
|
57
57
|
"uuid": "^13.0.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^25.5.0",
|
|
61
|
-
"@types/pg": "^8.20.0",
|
|
62
61
|
"drizzle-kit": "^0.31.10",
|
|
63
62
|
"typescript": "^6.0.2",
|
|
64
63
|
"vite-plus": "^0.1.13"
|