@go-labs-sg/rsvp 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -7
- package/dist/index.js +90 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ rsvp event seating draft preview --organization-id org_123 --event-id evt_123 \
|
|
|
108
108
|
--expected-version 1 --ack-sensitive-data
|
|
109
109
|
rsvp event seating draft publish --organization-id org_123 --event-id evt_123 \
|
|
110
110
|
--expected-version 1 --request-id 123e4567-e89b-42d3-a456-426614174000 \
|
|
111
|
-
--reason 'Seat the confirmed guest' --allow-state-change --target evt_123
|
|
111
|
+
--reason 'Seat the confirmed guest' --allow-state-change --allow-email --target evt_123
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
Save and Publish require the latest `expectedVersion`. A successful Publish applies
|
|
@@ -116,12 +116,37 @@ all changed guests atomically and clears only that version of your draft. Resolv
|
|
|
116
116
|
preview issues before publishing; conflicts leave all bookings and the draft intact.
|
|
117
117
|
For an unknown outcome, retry the same UUID `requestId`, version, reason and email
|
|
118
118
|
choice: the durable receipt returns the previous counts without sending again or
|
|
119
|
-
clearing a newer draft. A different intent needs a new request ID.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
clearing a newer draft. A different intent needs a new request ID. Preserve the
|
|
120
|
+
exact `seatChangeEmailRegistrationIds` set on retry (order is immaterial); changing
|
|
121
|
+
it, including changing between omitted and `[]`, rejects reuse of the request ID.
|
|
122
|
+
|
|
123
|
+
Preview returns `noticeRecipients`: one row per affected registration, with a
|
|
124
|
+
registration ID, a grant-scoped name, `eligible`, and an `ineligibleReason` when
|
|
125
|
+
unavailable. No email addresses are exposed by this seating response. Review both
|
|
126
|
+
eligible and ineligible rows before choosing who receives mail; several moving
|
|
127
|
+
party members still produce only one recipient registration.
|
|
128
|
+
|
|
129
|
+
Use `seatChangeEmailRegistrationIds` in a full `--payload` to choose the exact
|
|
130
|
+
eligible affected registrations. Leave a registration out to publish its seat
|
|
131
|
+
changes without email. `[]` explicitly sends no email, **including when the event
|
|
132
|
+
has automatic seat-change notifications**. Selecting an unaffected registration,
|
|
133
|
+
an ineligible recipient or duplicate IDs is rejected before publishing. The API
|
|
134
|
+
rechecks permissions and eligibility under the publish transaction, not just in
|
|
135
|
+
preview. Sending requires notice permission. An explicit empty array does not.
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
rsvp event seating draft publish --allow-state-change --allow-email --target evt_123 \
|
|
139
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","expectedVersion":1,"requestId":"123e4567-e89b-42d3-a456-426614174000","reason":"Seat the confirmed party","sendSeatChangeEmail":true,"seatChangeEmailRegistrationIds":["reg_123"]}'
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Do not combine `--payload` with command-local version/reason/request flags. If the
|
|
143
|
+
recipient field is omitted, existing behavior remains: `sendSeatChangeEmail: true`
|
|
144
|
+
requests all affected registrations, otherwise the automatic event policy still
|
|
145
|
+
applies. Every draft publish conservatively requires CLI `--allow-email` (or the
|
|
146
|
+
interactive effects confirmation), even for explicit `[]`; it approves an effect
|
|
147
|
+
but does not enable sending. Use the matching server/client revision for explicit
|
|
148
|
+
recipient choices; older servers reject the new field rather than silently
|
|
149
|
+
ignoring it. Preview counts recipient registrations, not guests. On realtime-enabled servers,
|
|
125
150
|
free new targets require validated per-registration holds at Publish, supplied as
|
|
126
151
|
`seatHolds: [{registrationId, holds: [{seatMapId, clientId, holdToken}]}]` in the
|
|
127
152
|
payload. Acquire these through the existing organizer hold-token flow; Stage never
|
|
@@ -537,6 +562,56 @@ it for the active registration/edit definition. Deploy the updated server before
|
|
|
537
562
|
saving these settings because older validators discard unsupported fields.
|
|
538
563
|
No database schema migration or attendee backfill is required.
|
|
539
564
|
|
|
565
|
+
## Confirmation email layout, name, QR codes and attachments
|
|
566
|
+
|
|
567
|
+
In the form builder's **Flow settings → Confirmation email layout**, or through
|
|
568
|
+
`definition.communications.participant.email` with `event flow draft save`,
|
|
569
|
+
customise the participant confirmation email for every outcome of the form:
|
|
570
|
+
|
|
571
|
+
```json
|
|
572
|
+
{
|
|
573
|
+
"communications": {
|
|
574
|
+
"participant": {
|
|
575
|
+
"email": {
|
|
576
|
+
"heading": "Hello {{name}}!",
|
|
577
|
+
"eventLine": null,
|
|
578
|
+
"confirmationButtonLabel": "View my Quest pass",
|
|
579
|
+
"supportFooter": "Questions? Email quest@example.com",
|
|
580
|
+
"showEventDetails": false,
|
|
581
|
+
"includeCheckInQrCodes": true,
|
|
582
|
+
"attachments": []
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
- `{{name}}` works in template `subject`, `body`, `bodyRichText` and every
|
|
590
|
+
text field above. It uses the primary attendee's name, then the invitee's
|
|
591
|
+
name, and falls back to `there`.
|
|
592
|
+
- `heading`, `eventLine`, `confirmationButtonLabel` and `supportFooter`: omit to
|
|
593
|
+
keep the built-in text, `null` to hide that part, or a string to replace it.
|
|
594
|
+
A custom or hidden `supportFooter` also replaces the resubmission footer.
|
|
595
|
+
- `showEventDetails` (default `true`) controls the When/Where lines.
|
|
596
|
+
- `includeCheckInQrCodes` (default `false`) embeds each attendee's check-in QR
|
|
597
|
+
image once the event's check-in QR codes are released; before release the
|
|
598
|
+
email keeps the release message.
|
|
599
|
+
- `attachments`: up to 5 PDF, PNG or JPEG files, 10 MB each and 15 MB total.
|
|
600
|
+
Create an upload with `event flow confirmation attachment upload create`
|
|
601
|
+
(`organizationId`, `eventId`, `filename`, `contentType`, `byteSize`), `PUT`
|
|
602
|
+
the file unchanged to the returned `uploadUrl` with its `Content-Type`, then
|
|
603
|
+
add the returned `attachment` object to the list. Preview/publish reject keys
|
|
604
|
+
not uploaded for the same event, and publish verifies each uploaded object's
|
|
605
|
+
type and size.
|
|
606
|
+
|
|
607
|
+
Merge these fields into the complete current definition and publish as for
|
|
608
|
+
other form settings. Omitting `email` keeps existing emails unchanged. Deploy
|
|
609
|
+
the updated server before saving these settings. No database migration is
|
|
610
|
+
required.
|
|
611
|
+
|
|
612
|
+
`event flow preview` sends the definition as a POST body so large formatted
|
|
613
|
+
emails no longer exceed URL limits; it requires a server with this change.
|
|
614
|
+
|
|
540
615
|
## Administrator registration for an invitee
|
|
541
616
|
|
|
542
617
|
Organizers can register someone on their behalf at any time, including after
|
package/dist/index.js
CHANGED
|
@@ -7195,7 +7195,8 @@ var logoutCliSession = async ({
|
|
|
7195
7195
|
// src/api-client.ts
|
|
7196
7196
|
import {
|
|
7197
7197
|
createTRPCProxyClient,
|
|
7198
|
-
httpBatchLink
|
|
7198
|
+
httpBatchLink,
|
|
7199
|
+
splitLink
|
|
7199
7200
|
} from "@trpc/client";
|
|
7200
7201
|
import SuperJSON from "superjson";
|
|
7201
7202
|
|
|
@@ -7213,18 +7214,28 @@ var normalizeApiBaseUrl = (value) => {
|
|
|
7213
7214
|
var getApiBaseUrl = (override) => normalizeApiBaseUrl(override ?? process.env.RSVP_API_URL ?? DEFAULT_RSVP_API_BASE_URL);
|
|
7214
7215
|
|
|
7215
7216
|
// src/api-client.ts
|
|
7216
|
-
var
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
}
|
|
7217
|
+
var POST_QUERY_PATHS = new Set([
|
|
7218
|
+
"eventFlow.preview"
|
|
7219
|
+
]);
|
|
7220
|
+
var createRsvpClient = (apiBaseUrl = getApiBaseUrl(), accessToken) => {
|
|
7221
|
+
const linkOptions = {
|
|
7222
|
+
headers: () => accessToken ? {
|
|
7223
|
+
authorization: `Bearer ${accessToken}`,
|
|
7224
|
+
"x-trpc-source": "rsvp-cli"
|
|
7225
|
+
} : { "x-trpc-source": "rsvp-cli" },
|
|
7226
|
+
transformer: SuperJSON,
|
|
7227
|
+
url: `${apiBaseUrl}/api/trpc`
|
|
7228
|
+
};
|
|
7229
|
+
return createTRPCProxyClient({
|
|
7230
|
+
links: [
|
|
7231
|
+
splitLink({
|
|
7232
|
+
condition: (op) => POST_QUERY_PATHS.has(op.path),
|
|
7233
|
+
true: httpBatchLink({ ...linkOptions, methodOverride: "POST" }),
|
|
7234
|
+
false: httpBatchLink(linkOptions)
|
|
7235
|
+
})
|
|
7236
|
+
]
|
|
7237
|
+
});
|
|
7238
|
+
};
|
|
7228
7239
|
|
|
7229
7240
|
// ../validators/src/assign-guests.ts
|
|
7230
7241
|
var assignGuestFiltersInputSchema = object({
|
|
@@ -8030,7 +8041,53 @@ var confirmationBodyRichTextSchema = unknown().superRefine((value, ctx) => {
|
|
|
8030
8041
|
message: "Confirmation body must contain 1\u201310,000 characters"
|
|
8031
8042
|
});
|
|
8032
8043
|
});
|
|
8044
|
+
var eventFlowConfirmationAttachmentContentTypes = [
|
|
8045
|
+
"application/pdf",
|
|
8046
|
+
"image/jpeg",
|
|
8047
|
+
"image/png"
|
|
8048
|
+
];
|
|
8049
|
+
var maxEventFlowConfirmationAttachmentCount = 5;
|
|
8050
|
+
var maxEventFlowConfirmationAttachmentSize = 10 * 1024 * 1024;
|
|
8051
|
+
var maxEventFlowConfirmationAttachmentTotalSize = 15 * 1024 * 1024;
|
|
8052
|
+
var eventFlowConfirmationAttachmentFilenameSchema = string2().trim().min(1).max(120).regex(/^[^\\/:*?"<>|\r\n\t]+$/, "Attachment file names cannot contain path or control characters");
|
|
8053
|
+
var eventFlowConfirmationAttachmentSchema = strictObject({
|
|
8054
|
+
byteSize: number2().int().positive().max(maxEventFlowConfirmationAttachmentSize),
|
|
8055
|
+
contentType: _enum(eventFlowConfirmationAttachmentContentTypes),
|
|
8056
|
+
filename: eventFlowConfirmationAttachmentFilenameSchema,
|
|
8057
|
+
key: string2().trim().min(1).max(300)
|
|
8058
|
+
});
|
|
8059
|
+
var editableEmailCopySchema = (maxLength) => string2().trim().min(1).max(maxLength).nullable().optional();
|
|
8060
|
+
var eventFlowParticipantConfirmationEmailSchema = strictObject({
|
|
8061
|
+
attachments: array(eventFlowConfirmationAttachmentSchema).max(maxEventFlowConfirmationAttachmentCount).default([]),
|
|
8062
|
+
confirmationButtonLabel: editableEmailCopySchema(60),
|
|
8063
|
+
eventLine: editableEmailCopySchema(500),
|
|
8064
|
+
heading: editableEmailCopySchema(120),
|
|
8065
|
+
includeCheckInQrCodes: boolean2().default(false),
|
|
8066
|
+
showEventDetails: boolean2().default(true),
|
|
8067
|
+
supportFooter: editableEmailCopySchema(1000)
|
|
8068
|
+
}).superRefine((email, ctx) => {
|
|
8069
|
+
const totalSize = email.attachments.reduce((total, attachment) => total + attachment.byteSize, 0);
|
|
8070
|
+
if (totalSize > maxEventFlowConfirmationAttachmentTotalSize) {
|
|
8071
|
+
ctx.addIssue({
|
|
8072
|
+
code: "custom",
|
|
8073
|
+
message: "Confirmation attachments must total 15 MB or less",
|
|
8074
|
+
path: ["attachments"]
|
|
8075
|
+
});
|
|
8076
|
+
}
|
|
8077
|
+
const keys = new Set;
|
|
8078
|
+
for (const [index, attachment] of email.attachments.entries()) {
|
|
8079
|
+
if (keys.has(attachment.key)) {
|
|
8080
|
+
ctx.addIssue({
|
|
8081
|
+
code: "custom",
|
|
8082
|
+
message: "Each confirmation attachment can be added only once",
|
|
8083
|
+
path: ["attachments", index, "key"]
|
|
8084
|
+
});
|
|
8085
|
+
}
|
|
8086
|
+
keys.add(attachment.key);
|
|
8087
|
+
}
|
|
8088
|
+
});
|
|
8033
8089
|
var participantConfirmationSchema = object({
|
|
8090
|
+
email: eventFlowParticipantConfirmationEmailSchema.optional(),
|
|
8034
8091
|
enabled: boolean2(),
|
|
8035
8092
|
includeResponseDetails: boolean2().default(false),
|
|
8036
8093
|
replyTo: email2().optional(),
|
|
@@ -10390,7 +10447,10 @@ var eventTablePaginationSchema = object({
|
|
|
10390
10447
|
var listEventInviteesInputSchema = organizationEventByIdInputSchema.extend({
|
|
10391
10448
|
...eventTablePaginationSchema.shape,
|
|
10392
10449
|
audienceTierId: string2().min(1).nullable().optional(),
|
|
10393
|
-
status: _enum(InviteeStatus).optional()
|
|
10450
|
+
status: _enum(InviteeStatus).optional(),
|
|
10451
|
+
statuses: array(_enum(InviteeStatus)).min(1).max(7).optional(),
|
|
10452
|
+
cursor: string2().min(1).max(64).optional(),
|
|
10453
|
+
direction: _enum(["NEXT", "PREVIOUS"]).default("NEXT")
|
|
10394
10454
|
});
|
|
10395
10455
|
var listEventRegistrationsInputSchema = organizationEventByIdInputSchema.extend({
|
|
10396
10456
|
archive: _enum(["ACTIVE", "ARCHIVED", "ALL"]).default("ACTIVE"),
|
|
@@ -10865,6 +10925,11 @@ var createEventFlowImageAssetUploadInputSchema = organizationEventByIdInputSchem
|
|
|
10865
10925
|
byteSize: number2().int().positive().max(maxEventFlowImageAssetSize),
|
|
10866
10926
|
mimeType: eventFlowImageAssetMimeTypeSchema
|
|
10867
10927
|
});
|
|
10928
|
+
var createEventFlowConfirmationAttachmentUploadInputSchema = organizationEventByIdInputSchema.extend({
|
|
10929
|
+
byteSize: number2().int().positive().max(maxEventFlowConfirmationAttachmentSize),
|
|
10930
|
+
contentType: _enum(eventFlowConfirmationAttachmentContentTypes),
|
|
10931
|
+
filename: eventFlowConfirmationAttachmentFilenameSchema
|
|
10932
|
+
});
|
|
10868
10933
|
var finalizeEventFlowImageAssetInputSchema = organizationEventByIdInputSchema.extend({
|
|
10869
10934
|
assetId: eventFlowImageAssetIdSchema
|
|
10870
10935
|
});
|
|
@@ -10968,6 +11033,10 @@ var createEventFlowConsentVersionInputSchema = organizationEventByIdInputSchema.
|
|
|
10968
11033
|
var setEventFlowGroupPasscodeInputSchema = organizationEventFlowInputSchema.extend({
|
|
10969
11034
|
passcode: string2().trim().min(8).max(160)
|
|
10970
11035
|
});
|
|
11036
|
+
var getEventFlowSeatCategoryColorsInputSchema = eventBySlugInputSchema.extend({
|
|
11037
|
+
flowVersionId: flowVersionIdSchema,
|
|
11038
|
+
seatMapId: flowVersionIdSchema
|
|
11039
|
+
}).strict();
|
|
10971
11040
|
var issueEventFlowSeatHoldTokenInputSchema = eventBySlugInputSchema.extend({
|
|
10972
11041
|
clientId: uuid2(),
|
|
10973
11042
|
flowVersionId: flowVersionIdSchema,
|
|
@@ -11961,6 +12030,9 @@ var publishSeatingDraftInputSchema = seatingDraftScopeSchema.extend({
|
|
|
11961
12030
|
requestId: uuid2(),
|
|
11962
12031
|
reason: string2().trim().min(1).max(500),
|
|
11963
12032
|
sendSeatChangeEmail: boolean2().default(false),
|
|
12033
|
+
seatChangeEmailRegistrationIds: array(string2().min(1)).max(100).refine((ids) => new Set(ids).size === ids.length, {
|
|
12034
|
+
message: "Each recipient registration may appear only once."
|
|
12035
|
+
}).optional(),
|
|
11964
12036
|
seatHolds: array(object({
|
|
11965
12037
|
registrationId: string2().min(1),
|
|
11966
12038
|
holds: array(object({
|
|
@@ -12048,7 +12120,7 @@ var commands = {
|
|
|
12048
12120
|
effects: [],
|
|
12049
12121
|
operation: (api, input) => api.eventFlow.preview.query(input),
|
|
12050
12122
|
payloadOnly: true,
|
|
12051
|
-
summary: "Validate and preview an event flow, including date-limit messages (--payload JSON)."
|
|
12123
|
+
summary: "Validate and preview an event flow, including date-limit messages; the definition is sent as a POST body (--payload JSON)."
|
|
12052
12124
|
},
|
|
12053
12125
|
"event flow draft get": {
|
|
12054
12126
|
effects: [],
|
|
@@ -12403,6 +12475,7 @@ Object.assign(commands, {
|
|
|
12403
12475
|
"audit list": payloadCommand((api, input) => api.audit.listEventActivity.query(input), "List event audit activity (--payload JSON).", ["sensitive-data"]),
|
|
12404
12476
|
"event check-in qr": payloadCommand((api, input) => api.event.checkInByQr.mutate(input), "Check in using a QR token (--payload JSON).", ["state-change"]),
|
|
12405
12477
|
"event flow image upload create": payloadCommand((api, input) => api.eventFlow.createImageAssetUpload.mutate(input), "Create an event-local registration-flow image upload URL (--payload JSON).", ["external-write", "state-change"], "eventId"),
|
|
12478
|
+
"event flow confirmation attachment upload create": payloadCommand((api, input) => api.eventFlow.createConfirmationAttachmentUpload.mutate(input), "Create a confirmation-email attachment upload URL for a PDF, PNG or JPEG up to 10 MB (filename, contentType, byteSize); PUT the file there, then add the returned attachment to communications.participant.email.attachments (--payload JSON).", ["external-write", "state-change"], "eventId"),
|
|
12406
12479
|
"event flow image finalize": payloadCommand((api, input) => api.eventFlow.finalizeImageAsset.mutate(input), "Finalize an event-local registration-flow image after the external upload (--payload JSON).", ["state-change"], "assetId"),
|
|
12407
12480
|
"event logo upload create": payloadCommand((api, input) => api.event.createLogoUpload.mutate(input), "Create an event-logo upload URL (--payload JSON).", ["external-write", "state-change"], "eventId"),
|
|
12408
12481
|
"event logo update": payloadCommand((api, input) => api.event.updateLogo.mutate(input), "Set or clear the event logo (--payload JSON).", ["state-change"], "eventId"),
|
|
@@ -12541,7 +12614,7 @@ Object.assign(commands, {
|
|
|
12541
12614
|
"--expected-version": { key: "expectedVersion", type: "number" }
|
|
12542
12615
|
},
|
|
12543
12616
|
operation: (api, input) => api.event.getSeatingDraftPreview.query(getSeatingDraftPreviewInputSchema.parse(input)),
|
|
12544
|
-
summary: "Check
|
|
12617
|
+
summary: "Check fresh seats, grants and notification policy. Returns noticeRecipients: one eligible/ineligible row per affected registration with grant-scoped names, never email addresses; --expected-version is required."
|
|
12545
12618
|
},
|
|
12546
12619
|
"event seating draft publish": {
|
|
12547
12620
|
scope: "event",
|
|
@@ -12557,7 +12630,7 @@ Object.assign(commands, {
|
|
|
12557
12630
|
},
|
|
12558
12631
|
validateInput: (input) => validateSeatingDraftInput(publishSeatingDraftInputSchema, input),
|
|
12559
12632
|
operation: (api, input) => api.event.publishSeatingDraft.mutate(publishSeatingDraftInputSchema.parse(input)),
|
|
12560
|
-
summary: "Atomically publish
|
|
12633
|
+
summary: "Atomically publish with expectedVersion, UUID requestId and reason. Optional seatChangeEmailRegistrationIds in --payload chooses exact affected eligible registration recipients; [] sends none, including automatic policy. Omit to retain sendSeatChangeEmail/event-policy behavior. Requires --allow-email. Retry the same requestId and exact recipient set. Realtime free targets need per-registration seatHolds."
|
|
12561
12634
|
}
|
|
12562
12635
|
});
|
|
12563
12636
|
var getCommandManifest = () => ({
|