@voyant-travel/mice 0.2.0 → 0.3.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.
@@ -0,0 +1,333 @@
1
+ /**
2
+ * First-class rooming manifest (§9-Q3) — program-centric, replacing the
3
+ * fragmented roomTypeId + sharingGroupId + allocations-JSONB on booking
4
+ * traveler details. A shared room is one assignment with MANY delegates via the
5
+ * explicit join (§9-Q5). See RFC voyant#1489 (Phase 3).
6
+ */
7
+ export declare const roomingAssignments: import("drizzle-orm/pg-core").PgTableWithColumns<{
8
+ name: "mice_rooming_assignments";
9
+ schema: undefined;
10
+ columns: {
11
+ id: import("drizzle-orm/pg-core").PgColumn<{
12
+ name: string;
13
+ tableName: "mice_rooming_assignments";
14
+ dataType: "string";
15
+ columnType: "PgText";
16
+ data: string;
17
+ driverParam: string;
18
+ notNull: true;
19
+ hasDefault: true;
20
+ isPrimaryKey: true;
21
+ isAutoincrement: false;
22
+ hasRuntimeDefault: true;
23
+ enumValues: [string, ...string[]];
24
+ baseColumn: never;
25
+ identity: undefined;
26
+ generated: undefined;
27
+ }, {}, {}>;
28
+ programId: import("drizzle-orm/pg-core").PgColumn<{
29
+ name: string;
30
+ tableName: "mice_rooming_assignments";
31
+ dataType: "string";
32
+ columnType: "PgText";
33
+ data: string;
34
+ driverParam: string;
35
+ notNull: true;
36
+ hasDefault: false;
37
+ isPrimaryKey: false;
38
+ isAutoincrement: false;
39
+ hasRuntimeDefault: false;
40
+ enumValues: [string, ...string[]];
41
+ baseColumn: never;
42
+ identity: undefined;
43
+ generated: undefined;
44
+ }, {}, {}>;
45
+ roomBlockId: import("drizzle-orm/pg-core").PgColumn<{
46
+ name: string;
47
+ tableName: "mice_rooming_assignments";
48
+ dataType: "string";
49
+ columnType: "PgText";
50
+ data: string;
51
+ driverParam: string;
52
+ notNull: false;
53
+ hasDefault: false;
54
+ isPrimaryKey: false;
55
+ isAutoincrement: false;
56
+ hasRuntimeDefault: false;
57
+ enumValues: [string, ...string[]];
58
+ baseColumn: never;
59
+ identity: undefined;
60
+ generated: undefined;
61
+ }, {}, {}>;
62
+ roomTypeId: import("drizzle-orm/pg-core").PgColumn<{
63
+ name: string;
64
+ tableName: "mice_rooming_assignments";
65
+ dataType: "string";
66
+ columnType: "PgText";
67
+ data: string;
68
+ driverParam: string;
69
+ notNull: false;
70
+ hasDefault: false;
71
+ isPrimaryKey: false;
72
+ isAutoincrement: false;
73
+ hasRuntimeDefault: false;
74
+ enumValues: [string, ...string[]];
75
+ baseColumn: never;
76
+ identity: undefined;
77
+ generated: undefined;
78
+ }, {}, {}>;
79
+ bedConfig: import("drizzle-orm/pg-core").PgColumn<{
80
+ name: "bed_config";
81
+ tableName: "mice_rooming_assignments";
82
+ dataType: "string";
83
+ columnType: "PgText";
84
+ data: string;
85
+ driverParam: string;
86
+ notNull: false;
87
+ hasDefault: false;
88
+ isPrimaryKey: false;
89
+ isAutoincrement: false;
90
+ hasRuntimeDefault: false;
91
+ enumValues: [string, ...string[]];
92
+ baseColumn: never;
93
+ identity: undefined;
94
+ generated: undefined;
95
+ }, {}, {}>;
96
+ sharingGroupId: import("drizzle-orm/pg-core").PgColumn<{
97
+ name: "sharing_group_id";
98
+ tableName: "mice_rooming_assignments";
99
+ dataType: "string";
100
+ columnType: "PgText";
101
+ data: string;
102
+ driverParam: string;
103
+ notNull: false;
104
+ hasDefault: false;
105
+ isPrimaryKey: false;
106
+ isAutoincrement: false;
107
+ hasRuntimeDefault: false;
108
+ enumValues: [string, ...string[]];
109
+ baseColumn: never;
110
+ identity: undefined;
111
+ generated: undefined;
112
+ }, {}, {}>;
113
+ checkIn: import("drizzle-orm/pg-core").PgColumn<{
114
+ name: "check_in";
115
+ tableName: "mice_rooming_assignments";
116
+ dataType: "string";
117
+ columnType: "PgDateString";
118
+ data: string;
119
+ driverParam: string;
120
+ notNull: false;
121
+ hasDefault: false;
122
+ isPrimaryKey: false;
123
+ isAutoincrement: false;
124
+ hasRuntimeDefault: false;
125
+ enumValues: undefined;
126
+ baseColumn: never;
127
+ identity: undefined;
128
+ generated: undefined;
129
+ }, {}, {}>;
130
+ checkOut: import("drizzle-orm/pg-core").PgColumn<{
131
+ name: "check_out";
132
+ tableName: "mice_rooming_assignments";
133
+ dataType: "string";
134
+ columnType: "PgDateString";
135
+ data: string;
136
+ driverParam: string;
137
+ notNull: false;
138
+ hasDefault: false;
139
+ isPrimaryKey: false;
140
+ isAutoincrement: false;
141
+ hasRuntimeDefault: false;
142
+ enumValues: undefined;
143
+ baseColumn: never;
144
+ identity: undefined;
145
+ generated: undefined;
146
+ }, {}, {}>;
147
+ specialRequests: import("drizzle-orm/pg-core").PgColumn<{
148
+ name: "special_requests";
149
+ tableName: "mice_rooming_assignments";
150
+ dataType: "string";
151
+ columnType: "PgText";
152
+ data: string;
153
+ driverParam: string;
154
+ notNull: false;
155
+ hasDefault: false;
156
+ isPrimaryKey: false;
157
+ isAutoincrement: false;
158
+ hasRuntimeDefault: false;
159
+ enumValues: [string, ...string[]];
160
+ baseColumn: never;
161
+ identity: undefined;
162
+ generated: undefined;
163
+ }, {}, {}>;
164
+ metadata: import("drizzle-orm/pg-core").PgColumn<{
165
+ name: "metadata";
166
+ tableName: "mice_rooming_assignments";
167
+ dataType: "json";
168
+ columnType: "PgJsonb";
169
+ data: Record<string, unknown>;
170
+ driverParam: unknown;
171
+ notNull: false;
172
+ hasDefault: false;
173
+ isPrimaryKey: false;
174
+ isAutoincrement: false;
175
+ hasRuntimeDefault: false;
176
+ enumValues: undefined;
177
+ baseColumn: never;
178
+ identity: undefined;
179
+ generated: undefined;
180
+ }, {}, {
181
+ $type: Record<string, unknown>;
182
+ }>;
183
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
184
+ name: "created_at";
185
+ tableName: "mice_rooming_assignments";
186
+ dataType: "date";
187
+ columnType: "PgTimestamp";
188
+ data: Date;
189
+ driverParam: string;
190
+ notNull: true;
191
+ hasDefault: true;
192
+ isPrimaryKey: false;
193
+ isAutoincrement: false;
194
+ hasRuntimeDefault: false;
195
+ enumValues: undefined;
196
+ baseColumn: never;
197
+ identity: undefined;
198
+ generated: undefined;
199
+ }, {}, {}>;
200
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
201
+ name: "updated_at";
202
+ tableName: "mice_rooming_assignments";
203
+ dataType: "date";
204
+ columnType: "PgTimestamp";
205
+ data: Date;
206
+ driverParam: string;
207
+ notNull: true;
208
+ hasDefault: true;
209
+ isPrimaryKey: false;
210
+ isAutoincrement: false;
211
+ hasRuntimeDefault: false;
212
+ enumValues: undefined;
213
+ baseColumn: never;
214
+ identity: undefined;
215
+ generated: undefined;
216
+ }, {}, {}>;
217
+ };
218
+ dialect: "pg";
219
+ }>;
220
+ export declare const roomingAssignmentDelegates: import("drizzle-orm/pg-core").PgTableWithColumns<{
221
+ name: "mice_rooming_assignment_delegates";
222
+ schema: undefined;
223
+ columns: {
224
+ id: import("drizzle-orm/pg-core").PgColumn<{
225
+ name: string;
226
+ tableName: "mice_rooming_assignment_delegates";
227
+ dataType: "string";
228
+ columnType: "PgText";
229
+ data: string;
230
+ driverParam: string;
231
+ notNull: true;
232
+ hasDefault: true;
233
+ isPrimaryKey: true;
234
+ isAutoincrement: false;
235
+ hasRuntimeDefault: true;
236
+ enumValues: [string, ...string[]];
237
+ baseColumn: never;
238
+ identity: undefined;
239
+ generated: undefined;
240
+ }, {}, {}>;
241
+ roomingAssignmentId: import("drizzle-orm/pg-core").PgColumn<{
242
+ name: string;
243
+ tableName: "mice_rooming_assignment_delegates";
244
+ dataType: "string";
245
+ columnType: "PgText";
246
+ data: string;
247
+ driverParam: string;
248
+ notNull: true;
249
+ hasDefault: false;
250
+ isPrimaryKey: false;
251
+ isAutoincrement: false;
252
+ hasRuntimeDefault: false;
253
+ enumValues: [string, ...string[]];
254
+ baseColumn: never;
255
+ identity: undefined;
256
+ generated: undefined;
257
+ }, {}, {}>;
258
+ delegateId: import("drizzle-orm/pg-core").PgColumn<{
259
+ name: string;
260
+ tableName: "mice_rooming_assignment_delegates";
261
+ dataType: "string";
262
+ columnType: "PgText";
263
+ data: string;
264
+ driverParam: string;
265
+ notNull: true;
266
+ hasDefault: false;
267
+ isPrimaryKey: false;
268
+ isAutoincrement: false;
269
+ hasRuntimeDefault: false;
270
+ enumValues: [string, ...string[]];
271
+ baseColumn: never;
272
+ identity: undefined;
273
+ generated: undefined;
274
+ }, {}, {}>;
275
+ isPrimary: import("drizzle-orm/pg-core").PgColumn<{
276
+ name: "is_primary";
277
+ tableName: "mice_rooming_assignment_delegates";
278
+ dataType: "boolean";
279
+ columnType: "PgBoolean";
280
+ data: boolean;
281
+ driverParam: boolean;
282
+ notNull: true;
283
+ hasDefault: true;
284
+ isPrimaryKey: false;
285
+ isAutoincrement: false;
286
+ hasRuntimeDefault: false;
287
+ enumValues: undefined;
288
+ baseColumn: never;
289
+ identity: undefined;
290
+ generated: undefined;
291
+ }, {}, {}>;
292
+ bedLabel: import("drizzle-orm/pg-core").PgColumn<{
293
+ name: "bed_label";
294
+ tableName: "mice_rooming_assignment_delegates";
295
+ dataType: "string";
296
+ columnType: "PgText";
297
+ data: string;
298
+ driverParam: string;
299
+ notNull: false;
300
+ hasDefault: false;
301
+ isPrimaryKey: false;
302
+ isAutoincrement: false;
303
+ hasRuntimeDefault: false;
304
+ enumValues: [string, ...string[]];
305
+ baseColumn: never;
306
+ identity: undefined;
307
+ generated: undefined;
308
+ }, {}, {}>;
309
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
310
+ name: "created_at";
311
+ tableName: "mice_rooming_assignment_delegates";
312
+ dataType: "date";
313
+ columnType: "PgTimestamp";
314
+ data: Date;
315
+ driverParam: string;
316
+ notNull: true;
317
+ hasDefault: true;
318
+ isPrimaryKey: false;
319
+ isAutoincrement: false;
320
+ hasRuntimeDefault: false;
321
+ enumValues: undefined;
322
+ baseColumn: never;
323
+ identity: undefined;
324
+ generated: undefined;
325
+ }, {}, {}>;
326
+ };
327
+ dialect: "pg";
328
+ }>;
329
+ export type RoomingAssignment = typeof roomingAssignments.$inferSelect;
330
+ export type NewRoomingAssignment = typeof roomingAssignments.$inferInsert;
331
+ export type RoomingAssignmentDelegate = typeof roomingAssignmentDelegates.$inferSelect;
332
+ export type NewRoomingAssignmentDelegate = typeof roomingAssignmentDelegates.$inferInsert;
333
+ //# sourceMappingURL=schema-rooming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-rooming.d.ts","sourceRoot":"","sources":["../src/schema-rooming.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwB9B,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBtC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACzE,MAAM,MAAM,yBAAyB,GAAG,OAAO,0BAA0B,CAAC,YAAY,CAAA;AACtF,MAAM,MAAM,4BAA4B,GAAG,OAAO,0BAA0B,CAAC,YAAY,CAAA"}
@@ -0,0 +1,47 @@
1
+ import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
2
+ import { boolean, date, index, jsonb, pgTable, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core";
3
+ import { programs } from "./schema.js";
4
+ import { programDelegates } from "./schema-delegates.js";
5
+ /**
6
+ * First-class rooming manifest (§9-Q3) — program-centric, replacing the
7
+ * fragmented roomTypeId + sharingGroupId + allocations-JSONB on booking
8
+ * traveler details. A shared room is one assignment with MANY delegates via the
9
+ * explicit join (§9-Q5). See RFC voyant#1489 (Phase 3).
10
+ */
11
+ export const roomingAssignments = pgTable("mice_rooming_assignments", {
12
+ id: typeId("mice_rooming_assignments"),
13
+ // Intra-package FK:
14
+ programId: typeIdRef("program_id")
15
+ .notNull()
16
+ .references(() => programs.id, { onDelete: "cascade" }),
17
+ // Cross-package → loose columns + defineLink at the deployment:
18
+ roomBlockId: typeIdRef("room_block_id"), // → accommodations.roomBlocks
19
+ roomTypeId: typeIdRef("room_type_id"), // → accommodations.roomTypes
20
+ bedConfig: text("bed_config"),
21
+ sharingGroupId: text("sharing_group_id"),
22
+ checkIn: date("check_in"),
23
+ checkOut: date("check_out"),
24
+ specialRequests: text("special_requests"),
25
+ metadata: jsonb("metadata").$type(),
26
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
27
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
28
+ }, (table) => [
29
+ index("idx_mice_rooming_assignments_program").on(table.programId),
30
+ index("idx_mice_rooming_assignments_room_block").on(table.roomBlockId),
31
+ ]);
32
+ export const roomingAssignmentDelegates = pgTable("mice_rooming_assignment_delegates", {
33
+ id: typeId("mice_rooming_assignment_delegates"),
34
+ roomingAssignmentId: typeIdRef("rooming_assignment_id")
35
+ .notNull()
36
+ .references(() => roomingAssignments.id, { onDelete: "cascade" }),
37
+ delegateId: typeIdRef("delegate_id")
38
+ .notNull()
39
+ .references(() => programDelegates.id, { onDelete: "cascade" }),
40
+ isPrimary: boolean("is_primary").notNull().default(false),
41
+ bedLabel: text("bed_label"),
42
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
43
+ }, (table) => [
44
+ index("idx_mice_rooming_assignment_delegates_assignment").on(table.roomingAssignmentId),
45
+ // A delegate appears at most once per rooming assignment.
46
+ uniqueIndex("uidx_mice_rooming_assignment_delegates_pair").on(table.roomingAssignmentId, table.delegateId),
47
+ ]);
package/dist/schema.d.ts CHANGED
@@ -326,5 +326,9 @@ export declare const programs: import("drizzle-orm/pg-core").PgTableWithColumns<
326
326
  }>;
327
327
  export type Program = typeof programs.$inferSelect;
328
328
  export type NewProgram = typeof programs.$inferInsert;
329
+ export type { BookingMiceDetail, NewBookingMiceDetail } from "./booking-extension.js";
330
+ export { bookingMiceDetails } from "./booking-extension.js";
331
+ export * from "./schema-delegates.js";
332
+ export * from "./schema-rooming.js";
329
333
  export * from "./schema-sessions.js";
330
334
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,qGAM1B,CAAA;AAEF,eAAO,MAAM,iBAAiB,iHAO5B,CAAA;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BpB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAErD,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,qGAM1B,CAAA;AAEF,eAAO,MAAM,iBAAiB,iHAO5B,CAAA;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BpB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAErD,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA"}
package/dist/schema.js CHANGED
@@ -48,4 +48,7 @@ export const programs = pgTable("mice_programs", {
48
48
  index("idx_mice_programs_status").on(table.status),
49
49
  index("idx_mice_programs_dates").on(table.startDate, table.endDate),
50
50
  ]);
51
+ export { bookingMiceDetails } from "./booking-extension.js";
52
+ export * from "./schema-delegates.js";
53
+ export * from "./schema-rooming.js";
51
54
  export * from "./schema-sessions.js";
@@ -0,0 +1,40 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import { type DelegateSessionEnrollment, type ProgramDelegate } from "./schema-delegates.js";
3
+ import type { CreateDelegateBody, DelegateListQuery, EnrollDelegateBody, UpdateDelegateBody } from "./validation-delegates.js";
4
+ export type CreateDelegateOutcome = {
5
+ status: "ok";
6
+ delegate: ProgramDelegate;
7
+ } | {
8
+ status: "program_not_found";
9
+ };
10
+ export declare function createDelegate(db: PostgresJsDatabase, input: CreateDelegateBody): Promise<CreateDelegateOutcome>;
11
+ export declare function getDelegate(db: PostgresJsDatabase, id: string): Promise<(ProgramDelegate & {
12
+ enrollments: DelegateSessionEnrollment[];
13
+ }) | null>;
14
+ export declare function listDelegates(db: PostgresJsDatabase, query: DelegateListQuery): Promise<{
15
+ data: ProgramDelegate[];
16
+ limit: number;
17
+ offset: number;
18
+ }>;
19
+ export declare function updateDelegate(db: PostgresJsDatabase, id: string, input: UpdateDelegateBody): Promise<ProgramDelegate | null>;
20
+ export type EnrollDelegateOutcome = {
21
+ status: "ok";
22
+ enrollment: DelegateSessionEnrollment;
23
+ idempotent: boolean;
24
+ } | {
25
+ status: "delegate_not_found";
26
+ } | {
27
+ status: "session_not_found";
28
+ } | {
29
+ status: "program_mismatch";
30
+ };
31
+ /** Enroll a delegate in a session. Idempotent on (delegate, session). */
32
+ export declare function enrollDelegate(db: PostgresJsDatabase, delegateId: string, input: EnrollDelegateBody): Promise<EnrollDelegateOutcome>;
33
+ export declare const delegateService: {
34
+ createDelegate: typeof createDelegate;
35
+ getDelegate: typeof getDelegate;
36
+ listDelegates: typeof listDelegates;
37
+ updateDelegate: typeof updateDelegate;
38
+ enrollDelegate: typeof enrollDelegate;
39
+ };
40
+ //# sourceMappingURL=service-delegates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-delegates.d.ts","sourceRoot":"","sources":["../src/service-delegates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EACL,KAAK,yBAAyB,EAE9B,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,2BAA2B,CAAA;AAYlC,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAA;AAEnC,wBAAsB,cAAc,CAClC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAUhC;AAED,wBAAsB,WAAW,CAC/B,EAAE,EAAE,kBAAkB,EACtB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,CAAC,eAAe,GAAG;IAAE,WAAW,EAAE,yBAAyB,EAAE,CAAA;CAAE,CAAC,GAAG,IAAI,CAAC,CAYlF;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC;IAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAYrE;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,kBAAkB,EACtB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAOjC;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,yBAAyB,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC5E;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAC/B;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAElC,yEAAyE;AACzE,wBAAsB,cAAc,CAClC,EAAE,EAAE,kBAAkB,EACtB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAqChC;AAED,eAAO,MAAM,eAAe;;;;;;CAM3B,CAAA"}
@@ -0,0 +1,107 @@
1
+ import { and, asc, eq } from "drizzle-orm";
2
+ import { programs } from "./schema.js";
3
+ import { delegateSessionEnrollments, programDelegates, } from "./schema-delegates.js";
4
+ import { programSessions } from "./schema-sessions.js";
5
+ /** Coerce ISO datetime strings (from validation) into Date for timestamp columns. */
6
+ function withTimestamps(input) {
7
+ const { arrivalAt, departureAt, ...rest } = input;
8
+ return {
9
+ ...rest,
10
+ ...(arrivalAt !== undefined ? { arrivalAt: new Date(arrivalAt) } : {}),
11
+ ...(departureAt !== undefined ? { departureAt: new Date(departureAt) } : {}),
12
+ };
13
+ }
14
+ export async function createDelegate(db, input) {
15
+ const [program] = await db
16
+ .select({ id: programs.id })
17
+ .from(programs)
18
+ .where(eq(programs.id, input.programId))
19
+ .limit(1);
20
+ if (!program)
21
+ return { status: "program_not_found" };
22
+ const [delegate] = await db.insert(programDelegates).values(withTimestamps(input)).returning();
23
+ if (!delegate)
24
+ throw new Error("createDelegate: insert returned no rows");
25
+ return { status: "ok", delegate };
26
+ }
27
+ export async function getDelegate(db, id) {
28
+ const [delegate] = await db
29
+ .select()
30
+ .from(programDelegates)
31
+ .where(eq(programDelegates.id, id))
32
+ .limit(1);
33
+ if (!delegate)
34
+ return null;
35
+ const enrollments = await db
36
+ .select()
37
+ .from(delegateSessionEnrollments)
38
+ .where(eq(delegateSessionEnrollments.delegateId, id));
39
+ return { ...delegate, enrollments };
40
+ }
41
+ export async function listDelegates(db, query) {
42
+ const conditions = [eq(programDelegates.programId, query.programId)];
43
+ if (query.status)
44
+ conditions.push(eq(programDelegates.status, query.status));
45
+ if (query.role)
46
+ conditions.push(eq(programDelegates.role, query.role));
47
+ const data = await db
48
+ .select()
49
+ .from(programDelegates)
50
+ .where(and(...conditions))
51
+ .orderBy(asc(programDelegates.createdAt))
52
+ .limit(query.limit)
53
+ .offset(query.offset);
54
+ return { data, limit: query.limit, offset: query.offset };
55
+ }
56
+ export async function updateDelegate(db, id, input) {
57
+ const [delegate] = await db
58
+ .update(programDelegates)
59
+ .set({ ...withTimestamps(input), updatedAt: new Date() })
60
+ .where(eq(programDelegates.id, id))
61
+ .returning();
62
+ return delegate ?? null;
63
+ }
64
+ /** Enroll a delegate in a session. Idempotent on (delegate, session). */
65
+ export async function enrollDelegate(db, delegateId, input) {
66
+ return db.transaction(async (tx) => {
67
+ const [delegate] = await tx
68
+ .select({ id: programDelegates.id, programId: programDelegates.programId })
69
+ .from(programDelegates)
70
+ .where(eq(programDelegates.id, delegateId))
71
+ .limit(1);
72
+ if (!delegate)
73
+ return { status: "delegate_not_found" };
74
+ const [session] = await tx
75
+ .select({ id: programSessions.id, programId: programSessions.programId })
76
+ .from(programSessions)
77
+ .where(eq(programSessions.id, input.sessionId))
78
+ .limit(1);
79
+ if (!session)
80
+ return { status: "session_not_found" };
81
+ // The session must belong to the delegate's program — neither the FK nor the
82
+ // unique index includes program_id, so guard against cross-program enrollment.
83
+ if (session.programId !== delegate.programId)
84
+ return { status: "program_mismatch" };
85
+ const [existing] = await tx
86
+ .select()
87
+ .from(delegateSessionEnrollments)
88
+ .where(and(eq(delegateSessionEnrollments.delegateId, delegateId), eq(delegateSessionEnrollments.sessionId, input.sessionId)))
89
+ .limit(1);
90
+ if (existing)
91
+ return { status: "ok", enrollment: existing, idempotent: true };
92
+ const [enrollment] = await tx
93
+ .insert(delegateSessionEnrollments)
94
+ .values({ delegateId, sessionId: input.sessionId, status: input.status })
95
+ .returning();
96
+ if (!enrollment)
97
+ throw new Error("enrollDelegate: insert returned no rows");
98
+ return { status: "ok", enrollment, idempotent: false };
99
+ });
100
+ }
101
+ export const delegateService = {
102
+ createDelegate,
103
+ getDelegate,
104
+ listDelegates,
105
+ updateDelegate,
106
+ enrollDelegate,
107
+ };
@@ -0,0 +1,50 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import { type RoomingAssignment, type RoomingAssignmentDelegate } from "./schema-rooming.js";
3
+ import type { CreateRoomingAssignmentBody, RoomingDelegateInput, UpdateRoomingAssignmentBody } from "./validation-rooming.js";
4
+ export type CreateRoomingAssignmentOutcome = {
5
+ status: "ok";
6
+ assignment: RoomingAssignment;
7
+ } | {
8
+ status: "program_not_found";
9
+ };
10
+ export declare function createRoomingAssignment(db: PostgresJsDatabase, input: CreateRoomingAssignmentBody): Promise<CreateRoomingAssignmentOutcome>;
11
+ export declare function getRoomingAssignment(db: PostgresJsDatabase, id: string): Promise<(RoomingAssignment & {
12
+ delegates: RoomingAssignmentDelegate[];
13
+ }) | null>;
14
+ export declare function listRoomingAssignments(db: PostgresJsDatabase, query: {
15
+ programId: string;
16
+ limit: number;
17
+ offset: number;
18
+ }): Promise<{
19
+ data: RoomingAssignment[];
20
+ limit: number;
21
+ offset: number;
22
+ }>;
23
+ export declare function updateRoomingAssignment(db: PostgresJsDatabase, id: string, input: UpdateRoomingAssignmentBody): Promise<RoomingAssignment | null>;
24
+ export type SetRoomingDelegatesOutcome = {
25
+ status: "ok";
26
+ delegates: RoomingAssignmentDelegate[];
27
+ } | {
28
+ status: "assignment_not_found";
29
+ } | {
30
+ status: "delegate_not_found";
31
+ missing: string[];
32
+ } | {
33
+ status: "program_mismatch";
34
+ offending: string[];
35
+ };
36
+ /**
37
+ * Replace the occupants of a rooming assignment (full replace). Validates the
38
+ * assignment and that every delegate exists AND belongs to the assignment's
39
+ * program before touching the join — a stale id is a 4xx (not an FK 500) and a
40
+ * cross-program delegate can't corrupt the program-scoped manifest.
41
+ */
42
+ export declare function setRoomingDelegates(db: PostgresJsDatabase, assignmentId: string, delegates: RoomingDelegateInput[]): Promise<SetRoomingDelegatesOutcome>;
43
+ export declare const roomingService: {
44
+ createRoomingAssignment: typeof createRoomingAssignment;
45
+ getRoomingAssignment: typeof getRoomingAssignment;
46
+ listRoomingAssignments: typeof listRoomingAssignments;
47
+ updateRoomingAssignment: typeof updateRoomingAssignment;
48
+ setRoomingDelegates: typeof setRoomingDelegates;
49
+ };
50
+ //# sourceMappingURL=service-rooming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-rooming.d.ts","sourceRoot":"","sources":["../src/service-rooming.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGjE,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAG/B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EACV,2BAA2B,EAC3B,oBAAoB,EACpB,2BAA2B,EAC5B,MAAM,yBAAyB,CAAA;AAEhC,MAAM,MAAM,8BAA8B,GACtC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,iBAAiB,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAA;AAEnC,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,8BAA8B,CAAC,CAUzC;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,kBAAkB,EACtB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,CAAC,iBAAiB,GAAG;IAAE,SAAS,EAAE,yBAAyB,EAAE,CAAA;CAAE,CAAC,GAAG,IAAI,CAAC,CAYlF;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC;IAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CASvE;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,kBAAkB,EACtB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAOnC;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,yBAAyB,EAAE,CAAA;CAAE,GACxD;IAAE,MAAM,EAAE,sBAAsB,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAEvD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,kBAAkB,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,oBAAoB,EAAE,GAChC,OAAO,CAAC,0BAA0B,CAAC,CAuCrC;AAED,eAAO,MAAM,cAAc;;;;;;CAM1B,CAAA"}