@robbeverhelst/do 1.294.0 → 1.296.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/do.js +562 -11
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -5051,7 +5051,14 @@ var files = pgTable("files", {
5051
5051
  ]);
5052
5052
 
5053
5053
  // ../../packages/schema/src/db/guests.ts
5054
- var guestTier = pgEnum("guest_tier", ["ceremony", "reception", "dinner", "party"]);
5054
+ var guestTier = pgEnum("guest_tier", [
5055
+ "civil",
5056
+ "church",
5057
+ "ceremony",
5058
+ "reception",
5059
+ "dinner",
5060
+ "party"
5061
+ ]);
5055
5062
  var households = pgTable("households", {
5056
5063
  id: text("id").primaryKey().references(() => nodes.id),
5057
5064
  tenantId: text("tenant_id").notNull().references(() => tenants.id),
@@ -5084,6 +5091,23 @@ var guestTables = pgTable("guest_tables", {
5084
5091
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5085
5092
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5086
5093
  }, (t) => [index("guest_tables_list_idx").on(t.guestListId)]);
5094
+ var guestListEvents = pgTable("guest_list_events", {
5095
+ id: text("id").primaryKey(),
5096
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
5097
+ guestListId: text("guest_list_id").notNull().references(() => guestLists.id, { onDelete: "cascade" }),
5098
+ key: guestTier("key").notNull(),
5099
+ name: jsonb("name").$type().notNull(),
5100
+ startsAt: timestamp("starts_at", { withTimezone: true }),
5101
+ endsAt: timestamp("ends_at", { withTimezone: true }),
5102
+ placeName: text("place_name"),
5103
+ address: text("address"),
5104
+ mapUrl: text("map_url"),
5105
+ dressCode: jsonb("dress_code").$type(),
5106
+ note: jsonb("note").$type(),
5107
+ sort: integer("sort").notNull().default(0),
5108
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5109
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5110
+ }, (t) => [uniqueIndex("guest_list_events_key_uniq").on(t.guestListId, t.key)]);
5087
5111
  var guests = pgTable("guests", {
5088
5112
  id: text("id").primaryKey(),
5089
5113
  tenantId: text("tenant_id").notNull().references(() => tenants.id),
@@ -5197,6 +5221,46 @@ var mailDeliveries = pgTable("mail_deliveries", {
5197
5221
  uniqueIndex("mail_deliveries_campaign_email_uniq").on(t.campaignId, t.toEmail)
5198
5222
  ]);
5199
5223
 
5224
+ // ../../packages/schema/src/db/rsvp-questions.ts
5225
+ var rsvpQuestionKind = pgEnum("rsvp_question_kind", [
5226
+ "choice",
5227
+ "multi",
5228
+ "text",
5229
+ "number",
5230
+ "yes_no"
5231
+ ]);
5232
+ var rsvpQuestionScope = pgEnum("rsvp_question_scope", ["household", "guest"]);
5233
+ var rsvpQuestions = pgTable("rsvp_questions", {
5234
+ id: text("id").primaryKey(),
5235
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
5236
+ guestListId: text("guest_list_id").notNull().references(() => guestLists.id, { onDelete: "cascade" }),
5237
+ kind: rsvpQuestionKind("kind").notNull(),
5238
+ label: jsonb("label").$type().notNull(),
5239
+ options: jsonb("options").$type().notNull().default([]),
5240
+ scope: rsvpQuestionScope("scope").notNull().default("household"),
5241
+ eventKey: text("event_key"),
5242
+ required: boolean("required").notNull().default(false),
5243
+ sort: integer("sort").notNull().default(0),
5244
+ showIf: jsonb("show_if").$type().notNull().default([]),
5245
+ preset: text("preset"),
5246
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5247
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5248
+ }, (t) => [index("rsvp_questions_list_idx").on(t.guestListId)]);
5249
+ var rsvpAnswers = pgTable("rsvp_answers", {
5250
+ id: text("id").primaryKey(),
5251
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
5252
+ guestListId: text("guest_list_id").notNull().references(() => guestLists.id, { onDelete: "cascade" }),
5253
+ questionId: text("question_id").notNull().references(() => rsvpQuestions.id, { onDelete: "cascade" }),
5254
+ householdId: text("household_id").notNull().references(() => households.id, { onDelete: "cascade" }),
5255
+ guestId: text("guest_id").references(() => guests.id, { onDelete: "cascade" }),
5256
+ value: jsonb("value").$type().notNull(),
5257
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5258
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5259
+ }, (t) => [
5260
+ unique("rsvp_answers_uniq").on(t.questionId, t.householdId, t.guestId).nullsNotDistinct(),
5261
+ index("rsvp_answers_list_idx").on(t.guestListId)
5262
+ ]);
5263
+
5200
5264
  // ../../packages/schema/src/db/calendar.ts
5201
5265
  var calendarLinkPurpose = pgEnum("calendar_link_purpose", ["block", "deadline"]);
5202
5266
  var calendarEventLinks = pgTable("calendar_event_links", {
@@ -20566,7 +20630,13 @@ var METRICS = {
20566
20630
  bedtime: { label: "Bedtime", unit: "clock", min: 0, max: 720, step: 5 }
20567
20631
  };
20568
20632
  var metricKeySchema = _enum2(["weight", "sleep", "mood", "rating", "energy", "bedtime"]);
20569
- var metricSourceSchema = _enum2(["manual", "device", "agent", "google_fit"]);
20633
+ var metricSourceSchema = _enum2([
20634
+ "manual",
20635
+ "device",
20636
+ "agent",
20637
+ "google_fit",
20638
+ "google_health"
20639
+ ]);
20570
20640
  var loggedMetricSourceSchema = _enum2(["manual", "device", "agent"]);
20571
20641
  var metricPointSchema = object({
20572
20642
  day: string2(),
@@ -20590,7 +20660,7 @@ var sessionSchema = object({
20590
20660
  sport: string2(),
20591
20661
  name: string2().nullable(),
20592
20662
  minutes: number2().nullable(),
20593
- source: _enum2(["google_fit", "strava"])
20663
+ source: _enum2(["google_fit", "google_health", "strava"])
20594
20664
  });
20595
20665
  var sessionsQuerySchema = object({
20596
20666
  days: exports_coerce.number().int().min(1).max(365).default(14)
@@ -22777,6 +22847,65 @@ var journalMilestoneListSchema = object({
22777
22847
  milestones: array(journalMilestoneSchema)
22778
22848
  });
22779
22849
 
22850
+ // ../../packages/schema/src/contracts/journal-recall.ts
22851
+ var JOURNAL_RECALL_LIMIT = 30;
22852
+ var JOURNAL_RECALL_MAX_DAYS = 366;
22853
+ var daysBetween = (from, to) => (Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / 86400000;
22854
+ var journalRecallQuerySchema = object({
22855
+ q: string2().trim().min(2, "search for at least 2 characters").max(100).optional(),
22856
+ around: journalDateSchema.optional(),
22857
+ from: journalDateSchema.optional(),
22858
+ to: journalDateSchema.optional(),
22859
+ limit: exports_coerce.number().int().min(1).max(JOURNAL_RECALL_LIMIT).optional()
22860
+ }).strict().refine((q) => !(q.around && (q.from || q.to)), {
22861
+ message: "give around, or from and to, not both",
22862
+ path: ["around"]
22863
+ }).refine((q) => !q.from || !q.to || q.from <= q.to, {
22864
+ message: "from must be on or before to",
22865
+ path: ["to"]
22866
+ }).refine((q) => !q.from || !q.to || daysBetween(q.from, q.to) < JOURNAL_RECALL_MAX_DAYS, {
22867
+ message: `a recall covers at most ${JOURNAL_RECALL_MAX_DAYS} days`,
22868
+ path: ["to"]
22869
+ });
22870
+ var journalRecallMilestoneSchema = object({
22871
+ id: string2(),
22872
+ title: string2(),
22873
+ kind: journalMilestoneKindSchema,
22874
+ happenedOn: journalDateSchema,
22875
+ precision: journalMilestonePrecisionSchema,
22876
+ planned: boolean3()
22877
+ });
22878
+ var journalRecallEarlierSchema = object({
22879
+ date: journalDateSchema,
22880
+ yearsAgo: number2().int().positive(),
22881
+ title: string2().nullable(),
22882
+ rating: number2().nullable()
22883
+ });
22884
+ var journalRecallSchema = object({
22885
+ q: string2().nullable(),
22886
+ from: journalDateSchema.nullable(),
22887
+ to: journalDateSchema.nullable(),
22888
+ timezone: string2().min(1),
22889
+ days: array(journalDayListItemSchema).max(JOURNAL_RECALL_LIMIT),
22890
+ rated: number2().int().nonnegative(),
22891
+ average: number2().nullable(),
22892
+ milestones: array(journalRecallMilestoneSchema),
22893
+ earlier: array(journalRecallEarlierSchema)
22894
+ });
22895
+ var journalDigestDaySchema = object({
22896
+ date: journalDateSchema,
22897
+ title: string2().nullable(),
22898
+ rating: number2().nullable()
22899
+ });
22900
+ var journalDigestSchema = object({
22901
+ from: journalDateSchema,
22902
+ to: journalDateSchema,
22903
+ timezone: string2().min(1),
22904
+ days: array(journalDigestDaySchema),
22905
+ milestones: array(journalRecallMilestoneSchema),
22906
+ text: string2()
22907
+ });
22908
+
22780
22909
  // ../../packages/schema/src/contracts/journal-recap.ts
22781
22910
  var journalWeekKeySchema = string2().regex(/^\d{4}-W(0[1-9]|[1-4]\d|5[0-3])$/, "must be an ISO week (YYYY-Www)");
22782
22911
  var JOURNAL_MOMENTS_MAX = 3;
@@ -25131,6 +25260,7 @@ var insuranceFieldsSchema = object({
25131
25260
  }).partial().strict();
25132
25261
  var contractFieldsSchema = object({
25133
25262
  customerNo: text4,
25263
+ product: text4,
25134
25264
  plan: text4,
25135
25265
  price: amount4,
25136
25266
  cadence: documentCadenceSchema,
@@ -26941,7 +27071,7 @@ var guestListViewSchema = guestListSchema.extend({
26941
27071
  var createGuestListInputSchema = object({
26942
27072
  id: ulidSchema,
26943
27073
  projectId: ulidSchema,
26944
- tiers: array(guestTierSchema).min(1).max(4).optional(),
27074
+ tiers: array(guestTierSchema).min(1).max(6).optional(),
26945
27075
  caps: capsSchema.optional(),
26946
27076
  sides: array(sideSchema).max(4).optional(),
26947
27077
  rsvpBy: keyDateDaySchema.nullish()
@@ -26970,7 +27100,7 @@ var createGuestInputSchema = object({
26970
27100
  guestListId: ulidSchema,
26971
27101
  contactId: ulidSchema,
26972
27102
  side: sideSchema.nullish(),
26973
- tiers: array(guestTierSchema).max(4).optional(),
27103
+ tiers: array(guestTierSchema).max(6).optional(),
26974
27104
  rsvp: answersSchema.optional(),
26975
27105
  plusOne: boolean3().optional(),
26976
27106
  plusOneName: string2().trim().min(1).max(120).nullish(),
@@ -27092,6 +27222,35 @@ var submitRsvpInputSchema = object({
27092
27222
  }).strict()).min(1).max(30),
27093
27223
  address: householdAddressSchema.nullish()
27094
27224
  }).strict();
27225
+ var eventTextSchema = (max) => object({
27226
+ nl: string2().trim().min(1).max(max).optional(),
27227
+ pt: string2().trim().min(1).max(max).optional()
27228
+ }).strict().refine((t) => t.nl !== undefined || t.pt !== undefined, "give it in NL or PT");
27229
+ var guestEventKeySchema = guestTierSchema;
27230
+ var guestListEventSchema = createSelectSchema(guestListEvents, {
27231
+ name: object({ nl: string2().optional(), pt: string2().optional() }),
27232
+ dressCode: object({ nl: string2().optional(), pt: string2().optional() }).nullable(),
27233
+ note: object({ nl: string2().optional(), pt: string2().optional() }).nullable(),
27234
+ startsAt: exports_coerce.date().nullable(),
27235
+ endsAt: exports_coerce.date().nullable(),
27236
+ createdAt: exports_coerce.date(),
27237
+ updatedAt: exports_coerce.date()
27238
+ });
27239
+ var eventFields = {
27240
+ name: eventTextSchema(120),
27241
+ startsAt: exports_coerce.date().nullish(),
27242
+ endsAt: exports_coerce.date().nullish(),
27243
+ placeName: string2().trim().min(1).max(120).nullish(),
27244
+ address: string2().trim().min(1).max(300).nullish(),
27245
+ mapUrl: string2().trim().url().max(500).refine((u) => /^https?:\/\//.test(u), "expected an http(s) link").nullish(),
27246
+ dressCode: eventTextSchema(200).nullish(),
27247
+ note: eventTextSchema(1000).nullish(),
27248
+ sort: number2().int().min(0).max(1e4).optional()
27249
+ };
27250
+ var endsAfterStart2 = (e) => !e.startsAt || !e.endsAt || e.endsAt >= e.startsAt;
27251
+ var createGuestListEventInputSchema = object({ id: ulidSchema, guestListId: ulidSchema, key: guestEventKeySchema, ...eventFields }).strict().refine(endsAfterStart2, { message: "endsAt is before startsAt", path: ["endsAt"] });
27252
+ var updateGuestListEventInputSchema = object(eventFields).partial().strict().refine(endsAfterStart2, { message: "endsAt is before startsAt", path: ["endsAt"] });
27253
+ var guestListEventsQuerySchema = object({ guestListId: ulidSchema });
27095
27254
  var guestTableSchema = createSelectSchema(guestTables, {
27096
27255
  createdAt: exports_coerce.date(),
27097
27256
  updatedAt: exports_coerce.date()
@@ -27181,6 +27340,10 @@ var mailFooterSchema = object({
27181
27340
  deleteBy: keyDateDaySchema.optional(),
27182
27341
  contact: email2().max(320).optional()
27183
27342
  }).strict();
27343
+ var mailLookShape = {
27344
+ couple: string2().trim().min(1).max(120).optional(),
27345
+ imagesUrl: url({ protocol: /^https$/ }).max(500).optional()
27346
+ };
27184
27347
  var lineSchema = string2().trim().min(1).max(2000);
27185
27348
  var mailMessageCopySchema = object({
27186
27349
  subject: string2().trim().min(1).max(200),
@@ -27191,6 +27354,7 @@ var mailMessageCopySchema = object({
27191
27354
  var mailMessageDataSchema = object({
27192
27355
  nl: mailMessageCopySchema,
27193
27356
  pt: mailMessageCopySchema.optional(),
27357
+ ...mailLookShape,
27194
27358
  footer: mailFooterSchema.optional()
27195
27359
  }).strict();
27196
27360
  var mailDaySchema = object({
@@ -27212,9 +27376,37 @@ var mailSaveTheDateDataSchema = object({
27212
27376
  pt: mailSaveTheDateCopySchema.optional(),
27213
27377
  days: array(mailDaySchema).min(1).max(5),
27214
27378
  calendarTitle: string2().trim().min(1).max(120).optional(),
27379
+ ...mailLookShape,
27215
27380
  footer: mailFooterSchema.optional()
27216
27381
  }).strict();
27217
27382
  var mailSaveTheDateDeliverySchema = object({ link: url().optional() }).strict();
27383
+ var mailProgrammeItemSchema = object({
27384
+ date: keyDateDaySchema.optional(),
27385
+ time: string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "time is HH:MM"),
27386
+ label: mailDaySchema.shape.label,
27387
+ place: string2().trim().min(1).max(200).optional()
27388
+ }).strict();
27389
+ var laterMailShape = {
27390
+ nl: mailSaveTheDateCopySchema,
27391
+ pt: mailSaveTheDateCopySchema.optional(),
27392
+ days: array(mailDaySchema).min(1).max(5),
27393
+ ...mailLookShape,
27394
+ footer: mailFooterSchema.optional()
27395
+ };
27396
+ var mailInvitationDataSchema = object({
27397
+ ...laterMailShape,
27398
+ programme: array(mailProgrammeItemSchema).max(12).optional(),
27399
+ rsvpBy: keyDateDaySchema.optional(),
27400
+ calendarTitle: string2().trim().min(1).max(120).optional()
27401
+ }).strict();
27402
+ var mailReminderDataSchema = object({ ...laterMailShape, rsvpBy: keyDateDaySchema }).strict();
27403
+ var mailFinalInfoDataSchema = object({ ...laterMailShape, programme: array(mailProgrammeItemSchema).max(12).optional() }).strict();
27404
+ var mailHouseholdDeliverySchema = object({
27405
+ link: url().optional(),
27406
+ household: string2().trim().min(1).max(200).optional(),
27407
+ programme: array(mailProgrammeItemSchema).max(12).optional(),
27408
+ table: string2().trim().min(1).max(120).optional()
27409
+ }).strict();
27218
27410
  var mailProviderEventSchema = looseObject({
27219
27411
  type: string2().min(1),
27220
27412
  created_at: string2().optional(),
@@ -27229,11 +27421,20 @@ var mailProviderEventSchema = looseObject({
27229
27421
  });
27230
27422
  var MAIL_CAMPAIGN_KINDS = ["message", "save_the_date", "invitation"];
27231
27423
  var mailCampaignKindSchema = _enum2(MAIL_CAMPAIGN_KINDS);
27232
- var MAIL_TEMPLATES = ["message", "save_the_date"];
27424
+ var MAIL_TEMPLATES = [
27425
+ "message",
27426
+ "save_the_date",
27427
+ "invitation",
27428
+ "reminder",
27429
+ "final_info"
27430
+ ];
27233
27431
  var mailTemplateSchema = _enum2(MAIL_TEMPLATES);
27234
27432
  var mailTemplateDataSchemas = {
27235
27433
  message: mailMessageDataSchema,
27236
- save_the_date: mailSaveTheDateDataSchema
27434
+ save_the_date: mailSaveTheDateDataSchema,
27435
+ invitation: mailInvitationDataSchema,
27436
+ reminder: mailReminderDataSchema,
27437
+ final_info: mailFinalInfoDataSchema
27237
27438
  };
27238
27439
  function mailTemplateDataError(template, data) {
27239
27440
  const parsed = mailTemplateDataSchemas[template].safeParse(data);
@@ -27289,6 +27490,100 @@ var mailCampaignViewSchema = mailCampaignSchema.extend({
27289
27490
  readyToSend: boolean3()
27290
27491
  });
27291
27492
 
27493
+ // ../../packages/schema/src/contracts/rsvp-questions.ts
27494
+ var RSVP_QUESTION_KINDS = ["choice", "multi", "text", "number", "yes_no"];
27495
+ var rsvpQuestionKindSchema = _enum2(RSVP_QUESTION_KINDS);
27496
+ var rsvpQuestionScopeSchema = _enum2(["household", "guest"]);
27497
+ var RSVP_QUESTION_PRESETS = ["diet", "message"];
27498
+ var rsvpQuestionPresetSchema = _enum2(RSVP_QUESTION_PRESETS);
27499
+ var RSVP_SHOW_IF_MAX = 5;
27500
+ var rsvpTextSchema = (max) => object({
27501
+ nl: string2().trim().min(1).max(max).optional(),
27502
+ pt: string2().trim().min(1).max(max).optional()
27503
+ }).strict().refine((t) => t.nl !== undefined || t.pt !== undefined, "give it in NL or PT");
27504
+ var keySchema = string2().regex(/^[a-z][a-z0-9_]{0,31}$/, "expected a short lower-case word (vegan, civil)");
27505
+ var rsvpQuestionOptionSchema = object({ key: keySchema, label: rsvpTextSchema(120) }).strict();
27506
+ var rsvpShowIfConditionSchema = union([
27507
+ object({
27508
+ questionId: ulidSchema,
27509
+ equals: union([string2().max(200), number2(), boolean3()])
27510
+ }).strict(),
27511
+ object({ eventKey: keySchema.optional(), rsvp: literal("yes") }).strict()
27512
+ ]);
27513
+ var rsvpAnswerValueSchema = union([
27514
+ string2().max(2000),
27515
+ array(string2().max(32)).max(20),
27516
+ number2(),
27517
+ boolean3()
27518
+ ]);
27519
+ var rsvpQuestionSchema = createSelectSchema(rsvpQuestions, {
27520
+ label: object({ nl: string2().optional(), pt: string2().optional() }),
27521
+ options: array(object({
27522
+ key: string2(),
27523
+ label: object({ nl: string2().optional(), pt: string2().optional() })
27524
+ })),
27525
+ showIf: array(rsvpShowIfConditionSchema),
27526
+ preset: rsvpQuestionPresetSchema.nullable(),
27527
+ createdAt: exports_coerce.date(),
27528
+ updatedAt: exports_coerce.date()
27529
+ });
27530
+ var rsvpAnswerSchema = createSelectSchema(rsvpAnswers, {
27531
+ value: rsvpAnswerValueSchema,
27532
+ createdAt: exports_coerce.date(),
27533
+ updatedAt: exports_coerce.date()
27534
+ });
27535
+ function rsvpOptionsProblem(kind, options) {
27536
+ if (kind === "choice" || kind === "multi") {
27537
+ if (options.length < 2)
27538
+ return `a ${kind} question needs two options or more`;
27539
+ if (new Set(options.map((o) => o.key)).size !== options.length)
27540
+ return "each option needs its own key";
27541
+ return null;
27542
+ }
27543
+ return options.length > 0 ? `a ${kind} question has no options` : null;
27544
+ }
27545
+ var questionFields = {
27546
+ label: rsvpTextSchema(300),
27547
+ options: array(rsvpQuestionOptionSchema).max(30).optional(),
27548
+ scope: rsvpQuestionScopeSchema.optional(),
27549
+ eventKey: keySchema.nullish(),
27550
+ required: boolean3().optional(),
27551
+ sort: number2().int().min(0).max(1e4).optional(),
27552
+ showIf: array(rsvpShowIfConditionSchema).max(RSVP_SHOW_IF_MAX).optional()
27553
+ };
27554
+ var createRsvpQuestionInputSchema = object({
27555
+ id: ulidSchema,
27556
+ guestListId: ulidSchema,
27557
+ kind: rsvpQuestionKindSchema,
27558
+ ...questionFields
27559
+ }).strict().superRefine((q, ctx) => {
27560
+ const problem = rsvpOptionsProblem(q.kind, q.options ?? []);
27561
+ if (problem)
27562
+ ctx.addIssue({ code: "custom", message: problem, path: ["options"] });
27563
+ if (q.showIf?.some((c) => ("questionId" in c) && c.questionId === q.id))
27564
+ ctx.addIssue({
27565
+ code: "custom",
27566
+ message: "a question can't depend on itself",
27567
+ path: ["showIf"]
27568
+ });
27569
+ });
27570
+ var updateRsvpQuestionInputSchema = object(questionFields).partial().strict();
27571
+ var rsvpQuestionsQuerySchema = object({ guestListId: ulidSchema });
27572
+ var rsvpAnswerInputSchema = object({
27573
+ questionId: ulidSchema,
27574
+ guestId: ulidSchema.nullish(),
27575
+ value: rsvpAnswerValueSchema.nullable()
27576
+ }).strict();
27577
+ var setRsvpAnswersInputSchema = object({
27578
+ guestListId: ulidSchema,
27579
+ householdId: ulidSchema,
27580
+ answers: array(rsvpAnswerInputSchema).min(1).max(200)
27581
+ }).strict();
27582
+ var rsvpAnswersQuerySchema = object({
27583
+ guestListId: ulidSchema,
27584
+ householdId: ulidSchema.optional()
27585
+ });
27586
+
27292
27587
  // ../../packages/schema/src/contracts/calendar.ts
27293
27588
  var calendarEventLinkSchema = createSelectSchema(calendarEventLinks, {
27294
27589
  createdAt: exports_coerce.date(),
@@ -28881,7 +29176,7 @@ function parseCapture2(input, now) {
28881
29176
 
28882
29177
  // src/meta.ts
28883
29178
  var CLI_NAME = "do";
28884
- var VERSION = "1.294.0";
29179
+ var VERSION = "1.296.0";
28885
29180
  var API_PREFIX = "/api/v1";
28886
29181
  var DEFAULT_API_URL = "http://localhost:3001";
28887
29182
 
@@ -29596,6 +29891,24 @@ var ENTITIES = [
29596
29891
  canDelete: true
29597
29892
  }
29598
29893
  },
29894
+ {
29895
+ name: "guest-list-events",
29896
+ path: "/guest-list-events",
29897
+ readScope: "contacts:read",
29898
+ returns: "GuestListEvent",
29899
+ canGet: true,
29900
+ listFlags: [
29901
+ { name: "list", description: "the guest list's id (required)", query: "guestListId" }
29902
+ ],
29903
+ write: {
29904
+ scope: "contacts:write",
29905
+ createSchema: createGuestListEventInputSchema,
29906
+ createInput: "CreateGuestListEventInput",
29907
+ updateSchema: updateGuestListEventInputSchema,
29908
+ updateInput: "UpdateGuestListEventInput",
29909
+ canDelete: true
29910
+ }
29911
+ },
29599
29912
  {
29600
29913
  name: "guest-tables",
29601
29914
  path: "/guest-tables",
@@ -29614,6 +29927,24 @@ var ENTITIES = [
29614
29927
  canDelete: true
29615
29928
  }
29616
29929
  },
29930
+ {
29931
+ name: "rsvp-questions",
29932
+ path: "/rsvp-questions",
29933
+ readScope: "contacts:read",
29934
+ returns: "RsvpQuestion",
29935
+ canGet: true,
29936
+ listFlags: [
29937
+ { name: "list", description: "the guest list's id (required)", query: "guestListId" }
29938
+ ],
29939
+ write: {
29940
+ scope: "contacts:write",
29941
+ createSchema: createRsvpQuestionInputSchema,
29942
+ createInput: "CreateRsvpQuestionInput",
29943
+ updateSchema: updateRsvpQuestionInputSchema,
29944
+ updateInput: "UpdateRsvpQuestionInput",
29945
+ canDelete: true
29946
+ }
29947
+ },
29617
29948
  {
29618
29949
  name: "guests",
29619
29950
  path: "/guests",
@@ -30329,6 +30660,24 @@ var HEALTH_COMMAND_SPECS = [
30329
30660
  scopes: READ,
30330
30661
  returns: "HealthLabOverview"
30331
30662
  },
30663
+ {
30664
+ path: ["health", "google", "status"],
30665
+ summary: "Whether your Google Health is connected, the last sync, and what stops it.",
30666
+ args: [],
30667
+ flags: [],
30668
+ scopes: READ,
30669
+ returns: "GoogleHealthStatus",
30670
+ notes: "GET /api/v1/health/google-health. Connecting needs Google's consent in a browser: Settings \u2192 Health in do. `failure` (reauth, not_linked, unavailable, other) says what stops the sync, with a title and a hint."
30671
+ },
30672
+ {
30673
+ path: ["health", "google", "sync"],
30674
+ summary: "Sync your Google Health now (sleep, resting heart rate, HRV, steps, workouts), as the morning sync does.",
30675
+ args: [],
30676
+ flags: [],
30677
+ scopes: WRITE2,
30678
+ returns: "GoogleHealthSyncResult",
30679
+ notes: "POST /api/v1/health/google-health/sync. Written and skipped per resource, and the status after it; a sync that stops answers 200 with `status.failure`. 409 before Google Health is connected, 403 while the server has it switched off."
30680
+ },
30332
30681
  {
30333
30682
  path: ["health", "analyte"],
30334
30683
  summary: "One analyte's trend across draws (do health analyte vitamin_d).",
@@ -30715,6 +31064,33 @@ var COMMAND_SPECS = [
30715
31064
  returns: "JournalSearch",
30716
31065
  notes: "GET /api/v1/journal/search?q= \u2014 matches the one-liner, highlights, the summary and entries of your own days (at most 30). journal:read is a sensitive scope."
30717
31066
  },
31067
+ {
31068
+ path: ["journal", "recall"],
31069
+ summary: "Recall your Journal: the days that answer some words or a window (or the last 7 days), the milestones near them and the same date in earlier years.",
31070
+ args: [{ name: "words", required: false, description: "A topic to look for (2+ characters)." }],
31071
+ flags: [
31072
+ {
31073
+ name: "around",
31074
+ type: "string",
31075
+ description: "A day, YYYY-MM-DD: reads 3 days either side."
31076
+ },
31077
+ { name: "from", type: "string", description: "The window's first day, YYYY-MM-DD." },
31078
+ { name: "to", type: "string", description: "The window's last day, YYYY-MM-DD." },
31079
+ { name: "limit", type: "string", description: "At most this many days (1\u201330)." }
31080
+ ],
31081
+ scopes: ["journal:read"],
31082
+ returns: "JournalRecall",
31083
+ notes: "GET /api/v1/journal/recall (journal_recall) \u2014 your own days only, dated so an answer can cite them; another member's days never answer. --around can't be combined with --from/--to. journal:read is a sensitive scope."
31084
+ },
31085
+ {
31086
+ path: ["journal", "digest"],
31087
+ summary: "The last 7 days of your Journal as a short digest: each day's line and rating, and the milestones a week either side.",
31088
+ args: [],
31089
+ flags: [],
31090
+ scopes: ["journal:read"],
31091
+ returns: "JournalDigest",
31092
+ notes: "GET /api/v1/journal/digest \u2014 `text` is the few plain lines an agent keeps in its private session. journal:read is a sensitive scope."
31093
+ },
30718
31094
  {
30719
31095
  path: ["journal", "week"],
30720
31096
  summary: "One week of the Journal: its recap (prose, best moments, a pattern), its days, the usual rating and the numbers from Progress.",
@@ -31736,6 +32112,44 @@ var COMMAND_SPECS = [
31736
32112
  returns: "DoccleStatus",
31737
32113
  notes: "PATCH /api/v1/documents/doccle with { archiveAfterImport }."
31738
32114
  },
32115
+ {
32116
+ path: ["documents", "ask"],
32117
+ summary: "Ask the papers a question (doc_ask): the passages that answer, best first, each with its paper, file and page.",
32118
+ args: [
32119
+ {
32120
+ name: "question",
32121
+ required: true,
32122
+ description: '"Is water damage from a burst pipe covered?"'
32123
+ }
32124
+ ],
32125
+ flags: [
32126
+ { name: "document", type: "string", description: "Ask this paper only (document id)." },
32127
+ {
32128
+ name: "limit",
32129
+ type: "string",
32130
+ description: "How many passages at most (default 5, up to 20)."
32131
+ }
32132
+ ],
32133
+ scopes: ["documents:read"],
32134
+ returns: "DocumentAnswer",
32135
+ notes: "POST /api/v1/documents/ask with { question, documentId?, limit? }. Reads the papers' key fields and their files' text (a page per form feed). do writes no prose: word the answer from the passages, quote the pages, and say it is not legal advice. `withoutText` names the papers with no file text to read. An Only me paper answers only its owner's key with documents:private."
32136
+ },
32137
+ {
32138
+ path: ["documents", "search"],
32139
+ summary: "Search the papers' words (key fields and file text), a passage per hit.",
32140
+ args: [{ name: "words", required: true, description: "The words to find." }],
32141
+ flags: [
32142
+ { name: "document", type: "string", description: "Only this paper (document id)." },
32143
+ {
32144
+ name: "limit",
32145
+ type: "string",
32146
+ description: "How many hits at most (default 20, up to 50)."
32147
+ }
32148
+ ],
32149
+ scopes: ["documents:read"],
32150
+ returns: "{ q, hits: DocumentPassage[] }",
32151
+ notes: "GET /api/v1/documents/search?q=&documentId=&limit=."
32152
+ },
31739
32153
  {
31740
32154
  path: ["documents", "unmatched"],
31741
32155
  summary: "List the papers that ask for money (an amount due) and have no payment linked yet, oldest due first.",
@@ -34310,6 +34724,31 @@ var COMMAND_SPECS = [
34310
34724
  scopes: ["contacts:write"],
34311
34725
  returns: "GuestView"
34312
34726
  },
34727
+ {
34728
+ path: ["rsvp-answers", "list"],
34729
+ summary: "A guest list's answers to its RSVP questions, or one household's.",
34730
+ args: [],
34731
+ flags: [
34732
+ { name: "list", type: "string", description: "The guest list's id (required)." },
34733
+ { name: "household", type: "string", description: "Only this household's answers." }
34734
+ ],
34735
+ scopes: ["contacts:read"],
34736
+ returns: "RsvpAnswer"
34737
+ },
34738
+ {
34739
+ path: ["rsvp-answers", "set"],
34740
+ summary: "Set a household's RSVP answers (null clears one); an answer to a hidden question is dropped.",
34741
+ args: [],
34742
+ flags: [
34743
+ {
34744
+ name: "data",
34745
+ type: "string",
34746
+ description: 'JSON SetRsvpAnswersInput, e.g. {"guestListId":"01\u2026","householdId":"01\u2026","answers":[{"questionId":"01\u2026","value":true},{"questionId":"01\u2026","guestId":"01\u2026","value":["vegan"]}]}'
34747
+ }
34748
+ ],
34749
+ scopes: ["contacts:write"],
34750
+ returns: "RsvpAnswer"
34751
+ },
34313
34752
  {
34314
34753
  path: ["seating-proposals", "list"],
34315
34754
  summary: "A guest list's proposed seating plans, newest first.",
@@ -34415,18 +34854,23 @@ var COMMAND_SPECS = [
34415
34854
  },
34416
34855
  {
34417
34856
  path: ["guest-lists", "export"],
34418
- summary: "Export for the caterer (CSV), the venue (CSV) or the floor plan (PDF) to stdout.",
34857
+ summary: "Export for the caterer (CSV) or the venue (CSV) to stdout, or save the floor plan (PDF) to a file.",
34419
34858
  args: [{ name: "id", required: true, description: "The guest list's id." }],
34420
34859
  flags: [
34421
34860
  {
34422
34861
  name: "kind",
34423
34862
  type: "string",
34424
34863
  description: "caterer (meals and diets per table), venue (who sits where) or floor-plan (PDF)."
34864
+ },
34865
+ {
34866
+ name: "out",
34867
+ type: "string",
34868
+ description: "Where to save the floor plan (default: the name the API gives it)."
34425
34869
  }
34426
34870
  ],
34427
34871
  scopes: ["contacts:read"],
34428
34872
  returns: "string",
34429
- notes: "Redirect to a file: `do guest-lists export <id> --kind floor-plan > plan.pdf`."
34873
+ notes: "A CSV goes to stdout (`> tables.csv`); the floor plan is saved: `do guest-lists export <id> --kind floor-plan --out plan.pdf`."
34430
34874
  },
34431
34875
  {
34432
34876
  path: ["rsvp-links", "list"],
@@ -35927,6 +36371,33 @@ function renderJournalSearch(data, mode) {
35927
36371
  return paint(mode, "dim", "(no journal days mention that)");
35928
36372
  return renderJournalDays({ days: d.days }, mode);
35929
36373
  }
36374
+ function renderJournalRecall(data, mode) {
36375
+ const d = data;
36376
+ const head = [
36377
+ d.q ? `\u201C${d.q}\u201D` : null,
36378
+ d.from && d.to ? `${d.from} \u2013 ${d.to}` : null,
36379
+ d.average != null ? `avg ${d.average} over ${d.rated} rated` : null
36380
+ ].filter(Boolean).join(" \xB7 ");
36381
+ const out = head ? [paint(mode, "dim", head)] : [];
36382
+ out.push((d.days ?? []).length === 0 ? paint(mode, "dim", "(no journal day answers that)") : renderJournalDays({ days: d.days }, mode));
36383
+ if ((d.milestones ?? []).length > 0) {
36384
+ out.push("", paint(mode, "dim", "Milestones near"));
36385
+ for (const m of d.milestones ?? [])
36386
+ out.push(` \u25C6 ${m.title}${paint(mode, "dim", ` \xB7 ${m.happenedOn}${m.planned ? " (planned)" : ""}`)}`);
36387
+ }
36388
+ if ((d.earlier ?? []).length > 0) {
36389
+ out.push("", paint(mode, "dim", "On this day"));
36390
+ for (const e of d.earlier ?? []) {
36391
+ const rating = e.rating == null ? "" : paint(mode, "dim", ` ${e.rating}/10`);
36392
+ out.push(` ${e.date} ${e.title ?? paint(mode, "dim", "(no entry)")}${rating}`);
36393
+ }
36394
+ }
36395
+ return out.join(`
36396
+ `);
36397
+ }
36398
+ function renderJournalDigest(data) {
36399
+ return data.text ?? "";
36400
+ }
35930
36401
  function renderJournalImportScan(data, mode) {
35931
36402
  const d = data;
35932
36403
  const items = d.items;
@@ -37055,6 +37526,27 @@ var HANDLERS = {
37055
37526
  render: renderJournalSearch
37056
37527
  };
37057
37528
  },
37529
+ "journal recall": async ({ args, flags, requireClient }) => {
37530
+ const q = args.join(" ").trim() || undefined;
37531
+ if (q !== undefined && q.length < 2)
37532
+ throw usageError("journal recall needs 2+ characters");
37533
+ const query = {
37534
+ q,
37535
+ around: flagString(flags, "around"),
37536
+ from: flagString(flags, "from"),
37537
+ to: flagString(flags, "to"),
37538
+ limit: flagString(flags, "limit")
37539
+ };
37540
+ parseInput(journalRecallQuerySchema, Object.fromEntries(Object.entries(query).filter(([, v]) => v !== undefined)));
37541
+ return {
37542
+ data: await requireClient().get("/journal/recall", { query }),
37543
+ render: renderJournalRecall
37544
+ };
37545
+ },
37546
+ "journal digest": async ({ requireClient }) => ({
37547
+ data: await requireClient().get("/journal/digest"),
37548
+ render: renderJournalDigest
37549
+ }),
37058
37550
  "journal week": async ({ args, requireClient }) => {
37059
37551
  const week = journalWeekArg(args, "week");
37060
37552
  return {
@@ -37531,6 +38023,32 @@ var HANDLERS = {
37531
38023
  render: renderRecord
37532
38024
  };
37533
38025
  },
38026
+ "documents ask": async ({ args, flags, requireClient }) => {
38027
+ const question = args.join(" ").trim();
38028
+ if (!question)
38029
+ throw usageError('documents ask needs a question ("Is water damage covered?")');
38030
+ const limit = flagString(flags, "limit");
38031
+ const body = Object.fromEntries(Object.entries({
38032
+ question,
38033
+ documentId: flagString(flags, "document"),
38034
+ limit: limit === undefined ? undefined : Number(limit)
38035
+ }).filter(([, value]) => value !== undefined));
38036
+ return { data: await requireClient().post("/documents/ask", body), render: renderRecord };
38037
+ },
38038
+ "documents search": async ({ args, flags, requireClient }) => {
38039
+ const q = args.join(" ").trim();
38040
+ if (!q)
38041
+ throw usageError("documents search needs the words to find");
38042
+ const query = Object.fromEntries(Object.entries({
38043
+ q,
38044
+ documentId: flagString(flags, "document"),
38045
+ limit: flagString(flags, "limit")
38046
+ }).filter(([, value]) => value !== undefined));
38047
+ return {
38048
+ data: await requireClient().get("/documents/search", { query }),
38049
+ render: renderRecord
38050
+ };
38051
+ },
37534
38052
  "documents unmatched": async ({ requireClient }) => ({
37535
38053
  data: await requireClient().get("/documents/unmatched"),
37536
38054
  render: renderList
@@ -39266,6 +39784,14 @@ var HANDLERS = {
39266
39784
  data: await requireClient().delete(`/health/shares/${requireArg(args, 0, "id")}`),
39267
39785
  render: renderRecord
39268
39786
  }),
39787
+ "health google status": async ({ requireClient }) => ({
39788
+ data: await requireClient().get("/health/google-health"),
39789
+ render: renderRecord
39790
+ }),
39791
+ "health google sync": async ({ requireClient }) => ({
39792
+ data: await requireClient().post("/health/google-health/sync", {}),
39793
+ render: renderRecord
39794
+ }),
39269
39795
  "health consent": async ({ requireClient }) => ({
39270
39796
  data: await requireClient().get("/health/consent"),
39271
39797
  render: renderRecord
@@ -39605,6 +40131,21 @@ var HANDLERS = {
39605
40131
  const body = parseInput(markGuestsSentInputSchema, readDataFlag(flags));
39606
40132
  return { data: await requireClient().post("/guests/sent", body), render: renderRecord };
39607
40133
  },
40134
+ "rsvp-answers list": async ({ flags, requireClient }) => {
40135
+ const list = flagString(flags, "list");
40136
+ if (!list)
40137
+ throw usageError("rsvp-answers list needs --list <guest list id>");
40138
+ const household = flagString(flags, "household");
40139
+ const query = parseInput(rsvpAnswersQuerySchema, {
40140
+ guestListId: list,
40141
+ ...household ? { householdId: household } : {}
40142
+ });
40143
+ return { data: await requireClient().get("/rsvp-answers", { query }), render: renderRecord };
40144
+ },
40145
+ "rsvp-answers set": async ({ flags, requireClient }) => {
40146
+ const body = parseInput(setRsvpAnswersInputSchema, readDataFlag(flags));
40147
+ return { data: await requireClient().put("/rsvp-answers", body), render: renderRecord };
40148
+ },
39608
40149
  "seating-proposals list": async ({ flags, requireClient }) => {
39609
40150
  const list = flagString(flags, "list");
39610
40151
  if (!list)
@@ -39682,7 +40223,17 @@ var HANDLERS = {
39682
40223
  "guest-lists export": async ({ args, flags, requireClient }) => {
39683
40224
  const id = requireArg(args, 0, "guest list id");
39684
40225
  const kind = parseInput(guestExportKindSchema, flagString(flags, "kind") ?? "venue");
39685
- const raw = await requireClient().getText(`/guest-lists/${id}/exports/${kind}`);
40226
+ const path = `/guest-lists/${id}/exports/${kind}`;
40227
+ if (kind === "floor-plan") {
40228
+ const file = await requireClient().getBytes(path);
40229
+ const out = flagString(flags, "out") ?? basename2(file.name ?? "floor-plan.pdf");
40230
+ writeFileSync2(out, file.bytes);
40231
+ return {
40232
+ data: { id, kind, saved: out, size: file.bytes.byteLength, type: file.contentType },
40233
+ render: renderRecord
40234
+ };
40235
+ }
40236
+ const raw = await requireClient().getText(path);
39686
40237
  return { raw, data: undefined, render: () => raw };
39687
40238
  },
39688
40239
  "rsvp-links list": async ({ flags, requireClient }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.294.0",
3
+ "version": "1.296.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "homepage": "https://github.com/robbeverhelst/do/tree/main/apps/cli",