@go-labs-sg/rsvp 0.16.0 → 1.0.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 (3) hide show
  1. package/README.md +337 -27
  2. package/dist/index.js +32 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -135,6 +135,76 @@ rsvp event flow versions list --organization-id org_123 --event-id evt_123
135
135
  rsvp event flow resources get --organization-id org_123 --event-id evt_123
136
136
  ```
137
137
 
138
+ ### Registration-flow admission policy
139
+
140
+ `event flow draft get` returns normalized legacy invitation forms as
141
+ `authentication.mode: "EMAIL_OTP"` with `definition.admissionPolicy: "ROSTER_ONLY"`.
142
+ Carry that independent policy through `event flow draft save`, `event flow publish`,
143
+ and `event flow version activate`; none of those operations silently opens signup.
144
+ The participant-safe `event flow published get` admission manifest also reports
145
+ `admissionPolicy` (`OPEN` for modern definitions without an explicit policy).
146
+
147
+ Save the complete returned definition with its current `expectedRevision`.
148
+ Omitting `admissionPolicy` preserves the stored draft/publication policy for older
149
+ clients. To intentionally broaden admission, explicitly set it to `"OPEN"` in the
150
+ modern email-auth definition, save, then publish; this remains subject to event
151
+ permissions, audience restrictions, and email-domain policy for new attendees.
152
+ Existing eligible roster attendees keep their existing eligibility rules.
153
+
154
+ ```sh
155
+ rsvp event flow draft get --payload '{"organizationId":"org_123","eventId":"evt_123","flowId":"flow_123"}'
156
+ # Save the complete JSON definition and expectedRevision in flow-draft.json.
157
+ # Keep admissionPolicy: "ROSTER_ONLY" unless the organiser approves open signup.
158
+ rsvp event flow draft save --allow-state-change --target evt_123 --payload "$(cat flow-draft.json)"
159
+ rsvp event flow publish --allow-state-change --target evt_123 --payload '{"organizationId":"org_123","eventId":"evt_123","flowId":"flow_123","expectedDraftRevision":2}'
160
+ rsvp event flow version activate --allow-state-change --target evt_123 --payload '{"organizationId":"org_123","eventId":"evt_123","flowId":"flow_123","flowVersionId":"version_123"}'
161
+ ```
162
+
163
+ Replace illustrative IDs and revisions with current readback values. Deploy API
164
+ policy enforcement before clients; an old API does not understand this field.
165
+ No authentication, consent flags, command names, or credential protocol changes.
166
+
167
+ ### Registration-flow date-limit messages
168
+
169
+ `DATE` and `DATETIME` custom fields accept optional
170
+ `field.validation.minDateMessage` and `maxDateMessage`: trimmed, non-empty plain
171
+ text of at most 500 characters. Each replaces only its corresponding out-of-range
172
+ explanation. Inclusive `minDate`/`maxDate` bounds, malformed-date errors and
173
+ required-field errors are unchanged; omitted messages keep the existing defaults.
174
+
175
+ An illustrative DOB validation object is:
176
+
177
+ ```json
178
+ {
179
+ "maxDate": "2008-01-01",
180
+ "maxDateMessage": "You must be at least 18 to register for this event."
181
+ }
182
+ ```
183
+
184
+ Age wording is organizer-authored, not a dynamic age rule. Choose the cutoff for
185
+ the event; do not reuse the illustrative date blindly.
186
+
187
+ Read the full current definition/revision using `event flow draft get`, edit the
188
+ intended field, then pass the complete definition to `event flow preview` and
189
+ `event flow draft save --allow-state-change --target <eventId> --payload <JSON>`.
190
+ Preview/save payloads include `organizationId`, `eventId`, optional `flowId`, and
191
+ `definition`; save also requires `expectedRevision`. Publish separately using
192
+ `event flow publish --allow-state-change --target <eventId> --payload <JSON>` with
193
+ `expectedDraftRevision`. Read back the saved definition with `event flow draft get`
194
+ and verify its compiled participant projection using the authenticated organizer
195
+ `event flow preview` command. Use `event flow versions list` to check immutable
196
+ version/publication identity; `event flow published get` provides public admission
197
+ and publication metadata in normal CLI execution, not the full participant fields.
198
+ It has no participant-session forwarding; the API returns `flow.participant` as
199
+ `null` without matching participant access. Verify the actual published date limits
200
+ and messages through the event's registration flow with a valid participant
201
+ session.
202
+
203
+ Existing commands forward the definition unchanged to the authenticated shared
204
+ API; no new flags or CLI-only validation are needed. Deploy matching API/web
205
+ support before saving these keys via the CLI; older APIs strip unrecognized
206
+ message keys. No event or published version is updated automatically.
207
+
138
208
  ### Registration-flow image upload lifecycle
139
209
 
140
210
  Registration-flow images are **named, event-local assets**. Create one with an
@@ -175,6 +245,163 @@ protocol. Reference the finalized asset from the appropriate form choice, then
175
245
  save and publish the flow separately when the complete draft is approved.
176
246
 
177
247
 
248
+ ## Stripe payments and external refunds
249
+
250
+ `payment checkout create` uses the same server-enforced finite reservation policy
251
+ as the web app. New Checkout sessions exclude known slow-settling methods while
252
+ retaining dynamic immediate methods such as cards and PayNow. Existing sessions
253
+ and late-success safeguards are unchanged.
254
+
255
+ `payment event list` reports completed refund totals including separately
256
+ recorded, verified refunds issued outside RSVP. Pending/failed provider refunds
257
+ do not count, and an external refund never consumes an unrelated app refund
258
+ request. `payment refund` still requires the existing financial/external-write
259
+ gates and original account binding. `payment exception list` remains the place
260
+ to inspect unresolved payment exceptions. Existing command inputs and gates are
261
+ unchanged. `payment refund recover` checks an existing local or external refund
262
+ adjustment through its stored provider reference; it never creates a new refund:
263
+
264
+ ```sh
265
+ rsvp payment refund recover \
266
+ --ack-sensitive-data --allow-external-write --allow-financial-write \
267
+ --allow-state-change --target adjustment_123 \
268
+ --payload '{"organizationId":"org_123","eventId":"evt_123","paymentAttemptId":"attempt_123","adjustmentId":"adjustment_123"}'
269
+ ```
270
+
271
+ The organization/event/attempt and adjustment must belong together, and the
272
+ caller must retain payment-adjustment permission through the provider lookup;
273
+ current actor authority and event lifecycle are rechecked transactionally before
274
+ any financial or audit writes. Repeated checks reconcile the
275
+ same adjustment safely; a missing provider reference requires separate operator
276
+ reconciliation, not a new key or refund request.
277
+
278
+ ### Tax configuration and breakdowns
279
+
280
+ `payment configure` accepts optional `tax` JSON. Use `{"mode":"NONE"}` for
281
+ no tax. `INCLUSIVE` extracts tax from the configured price; `EXCLUSIVE` adds
282
+ tax to it. Both taxable modes require a trimmed `label` (1–50 characters), an
283
+ integer `rateBasisPoints` from 0 to 10000, and explicit `donationTaxable`.
284
+ **100 basis points = 1%.** The example below is illustrative, not a recommended
285
+ rate or a decision about whether donations are taxable.
286
+
287
+ Read the current policy and `configurationVersion` before configuring, and
288
+ replace the IDs, version, amounts and tax settings for the intended event:
289
+
290
+ ```sh
291
+ rsvp payment event get --allow-financial-write \
292
+ --payload '{"organizationId":"org_123","eventId":"evt_123"}'
293
+
294
+ rsvp payment configure \
295
+ --allow-external-write --allow-financial-write --allow-state-change \
296
+ --target evt_123 \
297
+ --payload '{"organizationId":"org_123","eventId":"evt_123","eventVersion":7,"enabled":true,"currency":"SGD","amountMinor":1000,"pricingMode":"PER_REGISTRATION","donationMaximumMinor":500,"checkoutExpiryMinutes":30,"reservationPolicy":"HOLD_UNTIL_EXPIRY","tax":{"mode":"EXCLUSIVE","rateBasisPoints":900,"label":"Tax","donationTaxable":false}}'
298
+ ```
299
+
300
+ This is a complete configuration write, not a tax-only patch: preserve the
301
+ intended payment policy and credential selection. Add `flowId` for a named
302
+ form override; `useEventDefaults: true` makes that form inherit the event policy,
303
+ including tax. Omitted tax remains compatible with legacy no-tax configuration.
304
+ Read `payment event get` back with the same scope to verify the saved policy.
305
+
306
+ `payment mine get` exposes the attempt's saved tax quote and subtotal/tax/final
307
+ total. `payment event list` exposes each attempt's original `quote` plus
308
+ `refundBreakdown` (cumulative refunded subtotal, tax and total). Pending refunds
309
+ are not confirmed refunded amounts. These are payment breakdowns, not statutory
310
+ tax invoices. Organizers choose applicable settings; configuration does not
311
+ establish legal approval or activate an event. Deploy matching API/web/CLI
312
+ support before using these fields.
313
+
314
+ ### Pending Stripe connection preparation
315
+
316
+ Organization managers can persist separate named Sandbox and Live connections
317
+ without either secret. Generate one webhook UUID for each request, keep it for
318
+ retries, and replace all illustrative IDs below:
319
+
320
+ ```sh
321
+ rsvp stripe credential prepare \
322
+ --allow-external-write --allow-state-change --target org_123 \
323
+ --payload '{"organizationId":"org_123","name":"Client Sandbox","livemode":false,"webhookSourceId":"9dd0ba39-b55e-4e65-aa3e-d08242c45b8f"}'
324
+
325
+ rsvp stripe credential preparation list \
326
+ --payload '{"organizationId":"org_123"}'
327
+ ```
328
+
329
+ The response includes the saved preparation ID, webhook path, environment,
330
+ endpoint API version and required events. Confirm the deployment/hosting origin
331
+ before constructing the full client URL. Pending records survive reload but are
332
+ not usable payment credentials or defaults and cannot verify webhooks. Pending
333
+ names are also protected from legacy credential creation and rename operations;
334
+ unreserved names remain supported.
335
+
336
+ Prefer **Complete setup** in the encrypted integrations UI for secret entry.
337
+ For approved private automation, `stripe credential complete` calls the same
338
+ API with `organizationId`, the unchanged `name`, saved `preparationId` and
339
+ `webhookSourceId`, plus `secretKey` and `webhookSecret`. It requires
340
+ `--allow-external-write --allow-state-change --target <organizationId>`.
341
+ Both restricted `rk_` and secret `sk_` server keys are supported. Never place
342
+ secrets in chat, payment payloads, committed files, or shared shell history.
343
+ The complete command returns only public credential metadata.
344
+
345
+ Then `stripe credential test` takes `organizationId` and `credentialId` with
346
+ `--allow-external-write --allow-state-change --target <credentialId>`.
347
+ Its bounded account/Balance/Checkout/PaymentIntent/Refund reads verify the
348
+ prepared mode and read access, not write permission or webhook delivery. Only a
349
+ tested usable connection can be selected for payments; perform a separate sandbox
350
+ payment and webhook acceptance check before launch. These commands use the
351
+ normal authenticated API and never bypass organization-management permission.
352
+
353
+ Deploy with the schema/client and webhook prerequisites in
354
+ [the application guide](../../../apps/rsvp/README.md#refunds). Local generated
355
+ clients can be refreshed with `bun db:generate`; that is not a database push or
356
+ proof of deployed schema state.
357
+
358
+ ## Lucky Draw check-in cutoff
359
+
360
+ The event manager can save an optional cutoff in **Check-in → Settings → Lucky
361
+ draw eligibility**, in the event's configured timezone. The API stores an absolute
362
+ ISO timestamp with an explicit offset (`Z` or `±HH:MM`), not a browser-local time.
363
+ There is no fixed cutoff or 8 PM default. Unconfigured events return
364
+ `luckyDrawEligibility.cutoffAt: null`; malformed stored values also return `null`
365
+ rather than being interpreted in the browser's timezone.
366
+
367
+ Read the event first:
368
+
369
+ ```sh
370
+ rsvp event get --organization-id org_123 --event-id evt_123
371
+ ```
372
+
373
+ Copy its current `configurationVersion` into the setup payload's `version`.
374
+ Replace all example IDs, version `7`, and the illustrative timestamp below with
375
+ values for the intended event; the date, time, and offset are not defaults:
376
+
377
+ ```sh
378
+ rsvp event setup step update \
379
+ --allow-state-change \
380
+ --target evt_123 \
381
+ --payload '{"organizationId":"org_123","eventId":"evt_123","version":7,"section":"LUCKY_DRAW","cutoffAt":"2030-06-15T13:30:00+02:00"}'
382
+ ```
383
+
384
+ To clear the cutoff, read the event again and use its fresh version with
385
+ `cutoffAt: null` (not a blank string):
386
+
387
+ ```sh
388
+ rsvp event setup step update \
389
+ --allow-state-change \
390
+ --target evt_123 \
391
+ --payload '{"organizationId":"org_123","eventId":"evt_123","version":7,"section":"LUCKY_DRAW","cutoffAt":null}'
392
+ ```
393
+
394
+ Both writes require `event.manage`, the explicit state-change acknowledgement,
395
+ and a `--target` matching the payload's `eventId`. A stale version returns
396
+ `CONFLICT`: fetch the event, review the latest settings, then retry with its
397
+ current version rather than replaying an old payload. Read the event back after
398
+ a successful write to verify the cutoff and configuration version.
399
+
400
+ This configuration step only stores the cutoff and preserves unrelated event
401
+ metadata. It does not choose eligible people, apply category/exclusion rules,
402
+ import a draw roster, or reconcile prizes/vouchers. Deploy the updated API before
403
+ saving the new setup section; no database schema migration is required.
404
+
178
405
  ## Per-form confirmation presentation
179
406
 
180
407
  In the form builder's **Flow settings → Confirmation and check-in pass**, set
@@ -302,40 +529,24 @@ rsvp check-in by-reference \
302
529
 
303
530
  High-impact payment and retention commands remain protected by their matching financial, deletion, external-write, and sensitive-data gates. The API always re-evaluates the authenticated user's current Registration role, organization membership, event assignment, and resource permissions.
304
531
 
305
- ## Operational report exports
306
-
307
- `export request` accepts `format: "XLSX"` (the default when omitted) or `"CSV"`.
308
- Use `filters.view` to name the population rather than relying on a dataset label:
309
-
310
- - RSVP: `MASTER` (all invitees, grouped employee/guest responses; default),
311
- `CURRENT` (current response rows per person), `HISTORY` (all registration
312
- records, not every edit revision), or `CLIENT_REGISTRATION` (current completed
313
- employee/guest responses, including declines; excludes nonrespondents/drafts).
314
- The client XLSX has **Attending**, **Not Attending**, **Master**, and **Metadata**
315
- sheets; its CSV contains Master data only. Guest staff/department fields
316
- describe the host, while menu and dietary answers remain person-scoped.
317
- - SEATING: `CURRENT` (active bookings; default) or `HISTORY` (active and released).
318
- - ATTENDANCE: `CURRENT` (expected people; default) or `CHECKED_IN` (expected people
319
- with a check-in that has not been undone), or `ALL_INVITEES` (roster invitees,
320
- including nonrespondents/declines, plus current guests). Registered, submitted,
321
- attending, and active check-in states remain separate. None defines draw
322
- eligibility.
532
+ ## Registration master list export
323
533
 
324
- Form, registration-status and submission-date filters apply only to RSVP.
325
- Other datasets do not accept `view`. These combinations are validated by the API.
534
+ `export request` queues the single **Registration master list** Excel workbook
535
+ (one **Master List** sheet; question columns follow the event's form). The
536
+ payload is `organizationId`, `eventId` and an audit `reason`; the old `dataset`,
537
+ `format` and `filters` fields are ignored. See the RSVP app README for columns.
326
538
 
327
539
  ```sh
328
540
  rsvp export request \
329
541
  --ack-sensitive-data \
330
542
  --allow-state-change \
331
543
  --target evt_123 \
332
- --payload '{"organizationId":"org_123","eventId":"evt_123","dataset":"RSVP","format":"XLSX","filters":{"view":"MASTER"},"reason":"Organizer master list"}'
544
+ --payload '{"organizationId":"org_123","eventId":"evt_123","reason":"Organizer master list"}'
333
545
  ```
334
546
 
335
547
  Use `export list` to check the requester-scoped job, then `export download` for
336
- its current authorized download URL. For the client workbook, replace `MASTER`
337
- in the example with `CLIENT_REGISTRATION`. For the all-invitee attendance report,
338
- set `dataset` to `ATTENDANCE` and `filters.view` to `ALL_INVITEES`.
548
+ its current authorized download URL. `event registrations export` and
549
+ `event check-ins export` were removed; use `export request` instead.
339
550
 
340
551
  Operational check-in accepts `ATTENDING` and legacy `SUBMITTED`; roster/detail,
341
552
  manual/QR/reference entry, `report overview`, and `event people overview` use that same
@@ -343,9 +554,8 @@ population. Non-operational statuses remain excluded, and permissions, payment
343
554
  gates, duplicate protection, and undo behavior are unchanged. The web panel's
344
555
  realtime connection and manual refresh controls do not add a CLI subscription.
345
556
 
346
- Deploy matching API and export-worker versions before requesting the new views;
347
- old servers/workers do not understand them. Existing CLI commands pass the JSON
348
- payload to the shared typed API, so no new command or client-only rules are needed.
557
+ Deploy matching API and export-worker versions together; an old worker would build the retired
558
+ registration-records report for master-list jobs.
349
559
 
350
560
  ## Seating updates
351
561
 
@@ -368,6 +578,106 @@ rsvp event seating table update \
368
578
  --payload '{"organizationId":"org_123","eventId":"evt_123","tableId":"table_123","label":"Table 1"}'
369
579
  ```
370
580
 
581
+ ## Live seating and seat-change notifications
582
+
583
+ Deploy the matching API and worker before using these commands from an updated
584
+ CLI. They retain browser authentication, event permissions and sensitive-field
585
+ grants; the CLI does not implement separate seating rules.
586
+
587
+ ```sh
588
+ rsvp event seating live get --ack-sensitive-data \
589
+ --payload '{"organizationId":"org_123","eventId":"evt_123"}'
590
+ rsvp event seating occupants list --ack-sensitive-data \
591
+ --payload '{"organizationId":"org_123","eventId":"evt_123","seatMapId":"map_123","categoryValue":"finance","limit":25,"cursor":0}'
592
+ rsvp people registration edit get --ack-sensitive-data \
593
+ --payload '{"organizationId":"org_123","eventId":"evt_123","registrationId":"reg_123"}'
594
+ ```
595
+
596
+ Occupants supports optional `tableId`, `department`, `categoryValue` and `query`
597
+ filters. `categoryValue` is the canonical (trimmed, case-normalized) value
598
+ returned by the occupant response's category legend; it is not an arbitrary
599
+ field lookup. Follow `nextCursor` until null; counts and department/category
600
+ context contain only permitted data. The API projects all active occupants of the
601
+ selected map in batches before filtering; small pages are not database-level
602
+ filtered pagination.
603
+
604
+ ### Imported-field seating categories
605
+
606
+ Category coloring is optional and disabled by default. An authorized organizer
607
+ can read the eligible imported fields and their actual event values, then save one
608
+ selected field and a unique six-digit hex color for each canonical value. The
609
+ server includes unregistered invitees when it discovers the imported values;
610
+ guests inherit the host's imported value. It excludes identity, contact, system,
611
+ nested and multi-value data, and enforces sensitive-field grants before it returns
612
+ field keys, values, legends, filters or seat colors.
613
+
614
+ ```sh
615
+ rsvp event seating category config get --ack-sensitive-data \
616
+ --payload '{"organizationId":"org_123","eventId":"evt_123"}'
617
+
618
+ rsvp event seating category config update \
619
+ --ack-sensitive-data --allow-state-change --target evt_123 \
620
+ --payload '{"organizationId":"org_123","eventId":"evt_123","configurationVersion":3,"config":{"enabled":true,"fieldKey":"department","seed":"chosen-once","colors":[{"value":"finance","color":"#155e75"},{"value":"operations","color":"#7c2d12"}]}}'
621
+ ```
622
+
623
+ The get command is sensitive-data acknowledged. The update command additionally
624
+ requires explicit state-change confirmation and a target matching `eventId`; it
625
+ forwards `configurationVersion` for optimistic concurrency and the complete
626
+ configuration payload unchanged. It never assigns colors, interprets imported
627
+ values, or bypasses authorization in the CLI.
628
+
629
+ When categories are enabled, the API preserves saved mappings while new imported
630
+ values arrive and assigns only unused colors. Distinct groups never share a color;
631
+ there is an explicit capacity error instead of palette reuse or truncation. An
632
+ explicit auto-assign-all save is the only intentional re-randomization. Blank,
633
+ missing, unreadable and unauthorized values stay neutral. Disable the setting to
634
+ return ordinary seating status colors while retaining the selected field and
635
+ stable mappings. Clearing the selected field explicitly resets its mappings. Deploy matching web, API and CLI releases, then
636
+ perform separate live acceptance before enabling it for an event.
637
+
638
+ `people registration update` forwards the organizer-edit API. Supply the current
639
+ `expectedVersion`, audit `reason`, and complete `seats` array preserving unchanged
640
+ party assignments. Realtime-enabled changes additionally require valid exact-seat
641
+ holds through the existing realtime protocol; the CLI does not acquire holds.
642
+ Use `--ack-sensitive-data --allow-state-change --target <registration-id>`.
643
+ Saving does not send email unless the payload opts in. Re-read the revision
644
+ after conflicts.
645
+
646
+ To notify for a seat-only move, add `seatChangeNotice` to the same save. The
647
+ notice is queued in the move's database transaction, so the move and notice
648
+ request commit together or not at all; this additionally requires
649
+ `--allow-email`. It is rejected alongside identity, contact or response edits.
650
+
651
+ ```sh
652
+ rsvp people registration update \
653
+ --ack-sensitive-data --allow-state-change --allow-email --target reg_123 \
654
+ --payload '{"organizationId":"org_123","eventId":"evt_123","registrationId":"reg_123","expectedVersion":4,"reason":"Moved to finance table","seats":[{"registrationAttendeeId":"att_1","seatId":"seat_9"},{"registrationAttendeeId":"att_2","seatId":"seat_2"}],"seatChangeNotice":{"idempotencyKey":"4d68a320-013d-43c7-bfcc-d1126827d8c5"}}'
655
+ ```
656
+
657
+ A separate manual notice for an already committed revision remains available:
658
+
659
+ ```sh
660
+ rsvp people registration confirmation resend \
661
+ --allow-email --allow-state-change --target reg_123 \
662
+ --payload '{"organizationId":"org_123","eventId":"evt_123","registrationId":"reg_123","purpose":"SEAT_CHANGE","expectedRegistrationVersion":5,"idempotencyKey":"4d68a320-013d-43c7-bfcc-d1126827d8c5"}'
663
+ ```
664
+
665
+ Replace the sample UUID with a new UUID for a new request; reuse that same UUID
666
+ for retries of that request. Omitted `purpose` remains `CONFIRMATION`; seat-change
667
+ requests require the committed `expectedRegistrationVersion`. Queue acceptance
668
+ is not delivery. Stale revisions, unavailable copy and current permissions are
669
+ checked by the server, and delivery rechecks the current recipient and active
670
+ seats without including names, private answers or QR codes.
671
+
672
+ Legacy copy is `seatChangeNotification: {enabled, subject, body}` in the
673
+ `REGISTRATION_RULES` payload for `event setup step update`, with the current
674
+ configuration version. Configurable copy is
675
+ `definition.communications.participant.seatChange: {enabled, subject, body}`;
676
+ merge into the complete named draft, preview, save with its revision, read back,
677
+ then publish only with separate approval. Existing responses use their saved
678
+ published version. Both default disabled/blank; missing copy never falls back to
679
+ confirmation copy. No event-specific copy is supplied or enabled by this change.
680
+
371
681
  ## Audience tiers
372
682
 
373
683
  Audience tiers control which invitees can use a seat or table. List the tiers and their current invitee, table, and seat counts with:
package/dist/index.js CHANGED
@@ -6326,7 +6326,7 @@ var commands = {
6326
6326
  effects: [],
6327
6327
  operation: (api, input) => api.eventFlow.preview.query(input),
6328
6328
  payloadOnly: true,
6329
- summary: "Validate and preview an event flow definition (--payload JSON)."
6329
+ summary: "Validate and preview an event flow, including date-limit messages (--payload JSON)."
6330
6330
  },
6331
6331
  "event flow draft get": {
6332
6332
  effects: [],
@@ -6372,7 +6372,7 @@ var commands = {
6372
6372
  operation: (api, input) => api.eventFlow.saveDraft.mutate(input),
6373
6373
  payloadOnly: true,
6374
6374
  targetKey: "eventId",
6375
- summary: "Save a revision-checked registration flow draft (--payload JSON)."
6375
+ summary: "Save a revision-checked draft; omitted admissionPolicy preserves roster-only access, explicit OPEN permits intentional broadening (--payload JSON)."
6376
6376
  },
6377
6377
  "event flow published get": {
6378
6378
  effects: [],
@@ -6549,7 +6549,7 @@ var commands = {
6549
6549
  effects: ["sensitive-data", "state-change"],
6550
6550
  operation: (api, input) => api.export.requestExport.mutate(input),
6551
6551
  payloadOnly: true,
6552
- summary: "Request an operational report (--payload JSON; format CSV/XLSX, default XLSX; RSVP views MASTER/CURRENT/HISTORY/CLIENT_REGISTRATION; ATTENDANCE views CURRENT/CHECKED_IN/ALL_INVITEES; SEATING views CURRENT/HISTORY)."
6552
+ summary: "Request the registration master list XLSX (--payload JSON: eventId, organizationId, reason)."
6553
6553
  },
6554
6554
  "export list": {
6555
6555
  effects: ["sensitive-data"],
@@ -6613,8 +6613,8 @@ var eventScopeQueryCommand = (operation, summary) => ({
6613
6613
  summary
6614
6614
  });
6615
6615
  Object.assign(commands, {
6616
- "payment configure": payloadCommand((api, input) => api.payment.configureEventPayment.mutate(input), "Configure event payments (--payload JSON).", ["external-write", "financial-write", "state-change"]),
6617
- "payment checkout create": payloadCommand((api, input) => api.payment.createCheckout.mutate(input), "Create the caller's payment checkout (--payload JSON).", ["external-write", "financial-write", "state-change"]),
6616
+ "payment configure": payloadCommand((api, input) => api.payment.configureEventPayment.mutate(input), "Configure event or form payments with optional tax: NONE, INCLUSIVE or EXCLUSIVE; taxable modes require label, rateBasisPoints (100 = 1%) and explicit donationTaxable (--payload JSON).", ["external-write", "financial-write", "state-change"]),
6617
+ "payment checkout create": payloadCommand((api, input) => api.payment.createCheckout.mutate(input), "Create the caller's checkout; slow-settling payment methods are excluded (--payload JSON).", ["external-write", "financial-write", "state-change"]),
6618
6618
  "access mine": payloadCommand((api, input) => api.access.getMyEventAccess.query(input), "Get the caller's event access (--payload JSON)."),
6619
6619
  "access list": payloadCommand((api, input) => api.access.listEventAccess.query(input), "List event access grants (--payload JSON).", ["sensitive-data"]),
6620
6620
  "access assignment list": payloadCommand((api, input) => api.access.listEventAssignments.query(input), "List event assignments (--payload JSON)."),
@@ -6647,13 +6647,23 @@ Object.assign(commands, {
6647
6647
  "event seating seat tier update": payloadCommand((api, input) => api.event.setSeatAudienceTier.mutate(input), "Set or inherit an audience tier for seating seats (--payload JSON).", ["state-change"], "eventId"),
6648
6648
  "event field deactivate": payloadCommand((api, input) => api.event.deactivateFieldDefinition.mutate(input), "Deactivate an event field (--payload JSON).", ["delete", "state-change"]),
6649
6649
  "event field upsert": payloadCommand((api, input) => api.event.upsertFieldDefinition.mutate(input), "Create or update an event field (--payload JSON).", ["sensitive-data", "state-change"]),
6650
- "event check-ins export": payloadCommand((api, input) => api.event.exportCheckIns.mutate(input), "Export check-ins (--payload JSON).", ["sensitive-data", "state-change"]),
6651
- "event registrations export": payloadCommand((api, input) => api.event.exportRegistrations.mutate(input), "Export registrations (--payload JSON).", ["sensitive-data", "state-change"]),
6652
6650
  "event rsvp mine": payloadCommand((api, input) => api.event.getMyRsvp.query(input), "Get the caller's registration (--payload JSON).", ["sensitive-data"]),
6653
6651
  "event people overview": payloadCommand((api, input) => api.event.getPeopleOverview.query(input), "Get people operations overview (--payload JSON).", ["sensitive-data"]),
6654
6652
  "event reminder counts": payloadCommand((api, input) => api.event.getReminderSegmentCounts.query(input), "Get reminder segment counts (--payload JSON).", ["sensitive-data"]),
6655
6653
  "event rsvp manifest": payloadCommand((api, input) => api.event.getRsvpManifest.query(input), "Get the caller's registration manifest (--payload JSON).", ["sensitive-data"]),
6656
6654
  "event seating availability": payloadCommand((api, input) => api.event.getSeatAvailability.query(input), "Get seat availability (--payload JSON)."),
6655
+ "event seating live get": payloadCommand((api, input) => api.event.getLiveSeating.query(input), "Get the selected live seating map (--payload JSON).", ["sensitive-data"]),
6656
+ "event seating category config get": payloadCommand((api, input) => api.event.getSeatingCategoryConfig.query(input), "Get authorized imported-field seating category settings (--payload JSON).", ["sensitive-data"]),
6657
+ "event seating category config update": payloadCommand((api, input) => api.event.updateSeatingCategoryConfig.mutate(input), "Save imported-field seating category settings with configurationVersion (--payload JSON).", ["sensitive-data", "state-change"], "eventId"),
6658
+ "event seating occupants list": payloadCommand((api, input) => api.event.getLiveSeatingOccupants.query(input), "List permitted occupants by seatMapId, tableId, department, categoryValue and query; follow nextCursor (--payload JSON).", ["sensitive-data"]),
6659
+ "people registration edit get": payloadCommand((api, input) => api.event.getRegistrationForEdit.query(input), "Get current revision, permitted attendees, eligible seats and notification readiness (--payload JSON).", ["sensitive-data"]),
6660
+ "people registration update": {
6661
+ ...payloadCommand((api, input) => api.event.updateRegistration.mutate(input), "Save organizer edits with expectedVersion and reason; seating requires full-party seats and configured holds. Optional seatChangeNotice queues a seat-change notice atomically with a seat-only save (--payload JSON).", ["sensitive-data", "state-change"], "registrationId"),
6662
+ conditionalEffects: [
6663
+ { effect: "email", whenPayloadField: "seatChangeNotice" }
6664
+ ]
6665
+ },
6666
+ "people registration confirmation resend": payloadCommand((api, input) => api.event.resendRegistrationConfirmation.mutate(input), "Queue CONFIRMATION (default) or SEAT_CHANGE with expectedRegistrationVersion and idempotencyKey; retries reuse that key (--payload JSON).", ["email", "state-change"], "registrationId"),
6657
6667
  "event realtime-token issue": payloadCommand((api, input) => api.event.issueRealtimeToken.mutate(input), "Issue a realtime token (--payload JSON).", ["sensitive-data", "state-change"]),
6658
6668
  "event mine list": payloadCommand((api, input) => api.event.listMyEvents.query(input), "List the caller's events (--payload JSON)."),
6659
6669
  "event reminder list": payloadCommand((api, input) => api.event.listReminderSegments.query(input), "List reminder segments (--payload JSON).", ["sensitive-data"]),
@@ -6670,12 +6680,17 @@ Object.assign(commands, {
6670
6680
  });
6671
6681
  Object.assign(commands, {
6672
6682
  "payment event get": payloadCommand((api, input) => api.payment.getEventPayment.query(input), "Get event payment configuration (--payload JSON).", ["financial-write"]),
6673
- "payment mine get": payloadCommand((api, input) => api.payment.getMyPayment.query(input), "Get the caller's payment (--payload JSON).", ["financial-write", "sensitive-data"]),
6674
- "payment event list": payloadCommand((api, input) => api.payment.listEventPayments.query(input), "List event payments (--payload JSON).", ["financial-write", "sensitive-data"]),
6683
+ "payment mine get": payloadCommand((api, input) => api.payment.getMyPayment.query(input), "Get the caller's payment with its saved subtotal, tax and final total breakdown (--payload JSON).", ["financial-write", "sensitive-data"]),
6684
+ "payment event list": payloadCommand((api, input) => api.payment.listEventPayments.query(input), "List event payments with original quote and cumulative refunded subtotal/tax/total, including verified external refunds (--payload JSON).", ["financial-write", "sensitive-data"]),
6675
6685
  "payment exception list": payloadCommand((api, input) => api.payment.listPaymentExceptions.query(input), "List payment exceptions (--payload JSON).", ["financial-write", "sensitive-data"]),
6676
6686
  "payment exception resolve": payloadCommand((api, input) => api.payment.resolvePaymentException.mutate(input), "Resolve a payment exception (--payload JSON).", ["financial-write", "sensitive-data", "state-change"]),
6677
6687
  "payment reconcile": payloadCommand((api, input) => api.payment.reconcilePayment.query(input), "Compare a payment attempt with its provider (--payload JSON).", ["external-write", "financial-write"]),
6678
6688
  "payment refund": payloadCommand((api, input) => api.payment.refundPayment.mutate(input), "Refund a payment (--payload JSON).", ["external-write", "financial-write", "state-change"]),
6689
+ "stripe credential prepare": payloadCommand((api, input) => api.organization.prepareStripeCredential.mutate(input), "Persist a named pending Sandbox/Live Stripe connection and stable webhook URL without secrets (--payload JSON).", ["external-write", "state-change"], "organizationId"),
6690
+ "stripe credential complete": payloadCommand((api, input) => api.organization.createStripeCredential.mutate(input), "Complete a saved Stripe preparation using its exact IDs/name and private credentials (--payload JSON); secrets remain write-only.", ["external-write", "state-change"], "organizationId"),
6691
+ "stripe credential test": payloadCommand((api, input) => api.organization.testStripeCredential.mutate(input), "Run bounded account/Balance/Checkout/PaymentIntent/Refund read checks for a saved connection (--payload JSON).", ["external-write", "state-change"], "credentialId"),
6692
+ "stripe credential preparation list": payloadCommand((api, input) => api.organization.listStripeCredentialPreparations.query(input), "List organization-scoped pending Stripe connections and webhook setup instructions (--payload JSON)."),
6693
+ "payment refund recover": payloadCommand((api, input) => api.payment.recoverRefund.mutate(input), "Check an existing scoped refund adjustment without creating a provider refund (--payload JSON).", ["external-write", "financial-write", "state-change"], "adjustmentId"),
6679
6694
  "retention overview": payloadCommand((api, input) => api.retention.getRetentionOverview.query(input), "Get retention overview (--payload JSON).", ["sensitive-data"]),
6680
6695
  "retention hold create": payloadCommand((api, input) => api.retention.createLegalHold.mutate(input), "Create a legal hold (--payload JSON).", ["sensitive-data", "state-change"]),
6681
6696
  "retention hold release": payloadCommand((api, input) => api.retention.releaseLegalHold.mutate(input), "Release a legal hold (--payload JSON).", ["delete", "sensitive-data", "state-change"]),
@@ -6727,6 +6742,7 @@ var getCommandManifest = () => ({
6727
6742
  summary: "Show the CLI version and configured API base URL."
6728
6743
  },
6729
6744
  ...Object.entries(commands).map(([path, command]) => ({
6745
+ ...command.conditionalEffects ? { conditionalEffects: command.conditionalEffects } : {},
6730
6746
  effects: command.effects,
6731
6747
  path,
6732
6748
  summary: command.summary
@@ -6968,6 +6984,7 @@ var collectTargets = (input, targetKey) => {
6968
6984
  }
6969
6985
  return [];
6970
6986
  };
6987
+ var getConditionalEffects = (definition, input) => (definition.conditionalEffects ?? []).flatMap(({ effect, whenPayloadField }) => input && Object.hasOwn(input, whenPayloadField) && Reflect.get(input, whenPayloadField) != null ? [effect] : []);
6971
6988
  var requireEffects = async (effects, flags, confirm, input, targetKey) => {
6972
6989
  if (effects.length === 0)
6973
6990
  return;
@@ -7131,7 +7148,12 @@ Waiting for approval\u2026
7131
7148
  if (!definition)
7132
7149
  throw new CliUsageError("Unknown command. Run 'rsvp help' to list commands.");
7133
7150
  const input = getInput(parsed.flags, definition.payloadOnly, definition.scope);
7134
- await requireEffects(definition.effects, parsed.flags, dependencies.confirm, input, definition.targetKey);
7151
+ await requireEffects([
7152
+ ...new Set([
7153
+ ...definition.effects,
7154
+ ...getConditionalEffects(definition, input)
7155
+ ])
7156
+ ], parsed.flags, dependencies.confirm, input, definition.targetKey);
7135
7157
  const accessToken = !dependencies.apiClient && !definition.public ? await getAuthenticatedAccessToken({
7136
7158
  apiBaseUrl,
7137
7159
  store: getCredentialStore()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/rsvp",
3
- "version": "0.16.0",
3
+ "version": "1.0.0",
4
4
  "description": "RSVP CLI for authorized event operations.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",