@37signals/basecamp 0.11.0 → 0.13.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 +112 -16
- package/dist/.build-complete +0 -0
- package/dist/client.d.ts +65 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +235 -215
- package/dist/client.js.map +1 -1
- package/dist/download.d.ts +7 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/download.js +82 -22
- package/dist/download.js.map +1 -1
- package/dist/errors.d.ts +19 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +128 -15
- package/dist/errors.js.map +1 -1
- package/dist/generated/metadata.d.ts.map +1 -1
- package/dist/generated/metadata.js +443 -133
- package/dist/generated/metadata.js.map +1 -1
- package/dist/generated/metadata.ts +443 -133
- package/dist/generated/openapi-stripped.json +8999 -4907
- package/dist/generated/path-mapping.d.ts.map +1 -1
- package/dist/generated/path-mapping.js +40 -16
- package/dist/generated/path-mapping.js.map +1 -1
- package/dist/generated/path-mapping.ts +40 -16
- package/dist/generated/schema.d.ts +5508 -2346
- package/dist/generated/services/bookmarks.d.ts +76 -0
- package/dist/generated/services/bookmarks.d.ts.map +1 -0
- package/dist/generated/services/bookmarks.js +114 -0
- package/dist/generated/services/bookmarks.js.map +1 -0
- package/dist/generated/services/bookmarks.ts +158 -0
- package/dist/generated/services/boosts.d.ts +10 -0
- package/dist/generated/services/boosts.d.ts.map +1 -1
- package/dist/generated/services/boosts.js +8 -0
- package/dist/generated/services/boosts.js.map +1 -1
- package/dist/generated/services/boosts.ts +12 -0
- package/dist/generated/services/calendars.d.ts +47 -0
- package/dist/generated/services/calendars.d.ts.map +1 -0
- package/dist/generated/services/calendars.js +73 -0
- package/dist/generated/services/calendars.js.map +1 -0
- package/dist/generated/services/calendars.ts +102 -0
- package/dist/generated/services/campfires.d.ts +54 -40
- package/dist/generated/services/campfires.d.ts.map +1 -1
- package/dist/generated/services/campfires.js +82 -65
- package/dist/generated/services/campfires.js.map +1 -1
- package/dist/generated/services/campfires.ts +107 -85
- package/dist/generated/services/cards.d.ts +5 -0
- package/dist/generated/services/cards.d.ts.map +1 -1
- package/dist/generated/services/cards.js +4 -0
- package/dist/generated/services/cards.js.map +1 -1
- package/dist/generated/services/cards.ts +6 -0
- package/dist/generated/services/checkins.d.ts +14 -0
- package/dist/generated/services/checkins.d.ts.map +1 -1
- package/dist/generated/services/checkins.js +14 -1
- package/dist/generated/services/checkins.js.map +1 -1
- package/dist/generated/services/checkins.ts +20 -0
- package/dist/generated/services/client-approvals.d.ts +6 -3
- package/dist/generated/services/client-approvals.d.ts.map +1 -1
- package/dist/generated/services/client-approvals.js +8 -5
- package/dist/generated/services/client-approvals.js.map +1 -1
- package/dist/generated/services/client-approvals.ts +10 -5
- package/dist/generated/services/client-correspondences.d.ts +6 -3
- package/dist/generated/services/client-correspondences.d.ts.map +1 -1
- package/dist/generated/services/client-correspondences.js +8 -5
- package/dist/generated/services/client-correspondences.js.map +1 -1
- package/dist/generated/services/client-correspondences.ts +10 -5
- package/dist/generated/services/client-replies.d.ts +11 -4
- package/dist/generated/services/client-replies.d.ts.map +1 -1
- package/dist/generated/services/client-replies.js +16 -8
- package/dist/generated/services/client-replies.js.map +1 -1
- package/dist/generated/services/client-replies.ts +18 -8
- package/dist/generated/services/cloud-files.d.ts +92 -0
- package/dist/generated/services/cloud-files.d.ts.map +1 -0
- package/dist/generated/services/cloud-files.js +121 -0
- package/dist/generated/services/cloud-files.js.map +1 -0
- package/dist/generated/services/cloud-files.ts +188 -0
- package/dist/generated/services/comments.d.ts +5 -0
- package/dist/generated/services/comments.d.ts.map +1 -1
- package/dist/generated/services/comments.js +4 -0
- package/dist/generated/services/comments.js.map +1 -1
- package/dist/generated/services/comments.ts +6 -0
- package/dist/generated/services/documents.d.ts +12 -7
- package/dist/generated/services/documents.d.ts.map +1 -1
- package/dist/generated/services/documents.js +10 -6
- package/dist/generated/services/documents.js.map +1 -1
- package/dist/generated/services/documents.ts +14 -8
- package/dist/generated/services/drafts.d.ts +38 -0
- package/dist/generated/services/drafts.d.ts.map +1 -0
- package/dist/generated/services/drafts.js +40 -0
- package/dist/generated/services/drafts.js.map +1 -0
- package/dist/generated/services/drafts.ts +67 -0
- package/dist/generated/services/events.d.ts +5 -0
- package/dist/generated/services/events.d.ts.map +1 -1
- package/dist/generated/services/events.js +4 -0
- package/dist/generated/services/events.js.map +1 -1
- package/dist/generated/services/events.ts +6 -0
- package/dist/generated/services/everything.d.ts +83 -16
- package/dist/generated/services/everything.d.ts.map +1 -1
- package/dist/generated/services/everything.js +23 -13
- package/dist/generated/services/everything.js.map +1 -1
- package/dist/generated/services/everything.ts +100 -25
- package/dist/generated/services/folders.d.ts +96 -0
- package/dist/generated/services/folders.d.ts.map +1 -0
- package/dist/generated/services/folders.js +140 -0
- package/dist/generated/services/folders.js.map +1 -0
- package/dist/generated/services/folders.ts +201 -0
- package/dist/generated/services/forwards.d.ts +7 -20
- package/dist/generated/services/forwards.d.ts.map +1 -1
- package/dist/generated/services/forwards.js +6 -35
- package/dist/generated/services/forwards.js.map +1 -1
- package/dist/generated/services/forwards.ts +10 -48
- package/dist/generated/services/gauges.d.ts +7 -0
- package/dist/generated/services/gauges.d.ts.map +1 -1
- package/dist/generated/services/gauges.js +5 -1
- package/dist/generated/services/gauges.js.map +1 -1
- package/dist/generated/services/gauges.ts +9 -1
- package/dist/generated/services/google-documents.d.ts +98 -0
- package/dist/generated/services/google-documents.d.ts.map +1 -0
- package/dist/generated/services/google-documents.js +123 -0
- package/dist/generated/services/google-documents.js.map +1 -0
- package/dist/generated/services/google-documents.ts +196 -0
- package/dist/generated/services/index.d.ts +12 -5
- package/dist/generated/services/index.d.ts.map +1 -1
- package/dist/generated/services/index.js +12 -5
- package/dist/generated/services/index.js.map +1 -1
- package/dist/generated/services/index.ts +12 -5
- package/dist/generated/services/messages.d.ts +2 -0
- package/dist/generated/services/messages.d.ts.map +1 -1
- package/dist/generated/services/messages.js +1 -1
- package/dist/generated/services/messages.js.map +1 -1
- package/dist/generated/services/messages.ts +3 -1
- package/dist/generated/services/my-assignments.d.ts +52 -0
- package/dist/generated/services/my-assignments.d.ts.map +1 -1
- package/dist/generated/services/my-assignments.js +71 -0
- package/dist/generated/services/my-assignments.js.map +1 -1
- package/dist/generated/services/my-assignments.ts +104 -0
- package/dist/generated/services/my-notes.d.ts +45 -0
- package/dist/generated/services/my-notes.d.ts.map +1 -0
- package/dist/generated/services/my-notes.js +62 -0
- package/dist/generated/services/my-notes.js.map +1 -0
- package/dist/generated/services/my-notes.ts +92 -0
- package/dist/generated/services/my-notifications.d.ts +4 -2
- package/dist/generated/services/my-notifications.d.ts.map +1 -1
- package/dist/generated/services/my-notifications.js.map +1 -1
- package/dist/generated/services/my-notifications.ts +4 -2
- package/dist/generated/services/people.d.ts +11 -1
- package/dist/generated/services/people.d.ts.map +1 -1
- package/dist/generated/services/people.js +13 -2
- package/dist/generated/services/people.js.map +1 -1
- package/dist/generated/services/people.ts +15 -1
- package/dist/generated/services/projects.d.ts +26 -0
- package/dist/generated/services/projects.d.ts.map +1 -1
- package/dist/generated/services/projects.js +49 -1
- package/dist/generated/services/projects.js.map +1 -1
- package/dist/generated/services/projects.ts +61 -1
- package/dist/generated/services/recordings.d.ts +2 -12
- package/dist/generated/services/recordings.d.ts.map +1 -1
- package/dist/generated/services/recordings.js +1 -26
- package/dist/generated/services/recordings.js.map +1 -1
- package/dist/generated/services/recordings.ts +3 -31
- package/dist/generated/services/reports.d.ts +8 -12
- package/dist/generated/services/reports.d.ts.map +1 -1
- package/dist/generated/services/reports.js +11 -5
- package/dist/generated/services/reports.js.map +1 -1
- package/dist/generated/services/reports.ts +13 -14
- package/dist/generated/services/schedules.d.ts +94 -15
- package/dist/generated/services/schedules.d.ts.map +1 -1
- package/dist/generated/services/schedules.js +19 -8
- package/dist/generated/services/schedules.js.map +1 -1
- package/dist/generated/services/schedules.ts +107 -17
- package/dist/generated/services/search.d.ts +2 -0
- package/dist/generated/services/search.d.ts.map +1 -1
- package/dist/generated/services/search.js +1 -1
- package/dist/generated/services/search.js.map +1 -1
- package/dist/generated/services/search.ts +3 -1
- package/dist/generated/services/templates.d.ts +2 -0
- package/dist/generated/services/templates.d.ts.map +1 -1
- package/dist/generated/services/templates.js +1 -1
- package/dist/generated/services/templates.js.map +1 -1
- package/dist/generated/services/templates.ts +3 -1
- package/dist/generated/services/timeline.d.ts +2 -0
- package/dist/generated/services/timeline.d.ts.map +1 -1
- package/dist/generated/services/timeline.js +1 -0
- package/dist/generated/services/timeline.js.map +1 -1
- package/dist/generated/services/timeline.ts +3 -0
- package/dist/generated/services/timesheets.d.ts +16 -0
- package/dist/generated/services/timesheets.d.ts.map +1 -1
- package/dist/generated/services/timesheets.js +26 -2
- package/dist/generated/services/timesheets.js.map +1 -1
- package/dist/generated/services/timesheets.ts +35 -2
- package/dist/generated/services/todolist-groups.d.ts +11 -6
- package/dist/generated/services/todolist-groups.d.ts.map +1 -1
- package/dist/generated/services/todolist-groups.js +7 -3
- package/dist/generated/services/todolist-groups.js.map +1 -1
- package/dist/generated/services/todolist-groups.ts +13 -7
- package/dist/generated/services/todolists.d.ts +15 -13
- package/dist/generated/services/todolists.d.ts.map +1 -1
- package/dist/generated/services/todolists.js +11 -8
- package/dist/generated/services/todolists.js.map +1 -1
- package/dist/generated/services/todolists.ts +19 -14
- package/dist/generated/services/todos.d.ts +35 -12
- package/dist/generated/services/todos.d.ts.map +1 -1
- package/dist/generated/services/todos.js +47 -25
- package/dist/generated/services/todos.js.map +1 -1
- package/dist/generated/services/todos.ts +74 -30
- package/dist/generated/services/uploads.d.ts +5 -0
- package/dist/generated/services/uploads.d.ts.map +1 -1
- package/dist/generated/services/uploads.js +4 -0
- package/dist/generated/services/uploads.js.map +1 -1
- package/dist/generated/services/uploads.ts +6 -0
- package/dist/generated/services/vaults.d.ts +5 -0
- package/dist/generated/services/vaults.d.ts.map +1 -1
- package/dist/generated/services/vaults.js +4 -0
- package/dist/generated/services/vaults.js.map +1 -1
- package/dist/generated/services/vaults.ts +6 -0
- package/dist/hooks/otel.d.ts +1 -1
- package/dist/hooks/otel.js +1 -1
- package/dist/index.d.ts +21 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -9
- package/dist/index.js.map +1 -1
- package/dist/oauth/discovery.d.ts.map +1 -1
- package/dist/oauth/discovery.js +1 -0
- package/dist/oauth/discovery.js.map +1 -1
- package/dist/pagination-utils.d.ts +46 -0
- package/dist/pagination-utils.d.ts.map +1 -1
- package/dist/pagination-utils.js +85 -2
- package/dist/pagination-utils.js.map +1 -1
- package/dist/pagination.d.ts +12 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +1 -1
- package/dist/pagination.js.map +1 -1
- package/dist/retry.d.ts +110 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +253 -0
- package/dist/retry.js.map +1 -0
- package/dist/services/base.d.ts.map +1 -1
- package/dist/services/base.js +59 -5
- package/dist/services/base.js.map +1 -1
- package/dist/services/cards-extensions.d.ts +26 -21
- package/dist/services/cards-extensions.d.ts.map +1 -1
- package/dist/services/cards-extensions.js +28 -31
- package/dist/services/cards-extensions.js.map +1 -1
- package/dist/services/documents-extensions.d.ts +122 -0
- package/dist/services/documents-extensions.d.ts.map +1 -0
- package/dist/services/documents-extensions.js +125 -0
- package/dist/services/documents-extensions.js.map +1 -0
- package/dist/services/merge-safe.d.ts +167 -0
- package/dist/services/merge-safe.d.ts.map +1 -0
- package/dist/services/merge-safe.js +217 -0
- package/dist/services/merge-safe.js.map +1 -0
- package/dist/services/schedules-extensions.d.ts +266 -0
- package/dist/services/schedules-extensions.d.ts.map +1 -0
- package/dist/services/schedules-extensions.js +268 -0
- package/dist/services/schedules-extensions.js.map +1 -0
- package/dist/services/todolists-extensions.d.ts +110 -0
- package/dist/services/todolists-extensions.d.ts.map +1 -0
- package/dist/services/todolists-extensions.js +262 -0
- package/dist/services/todolists-extensions.js.map +1 -0
- package/dist/services/todos-extensions.d.ts +12 -1
- package/dist/services/todos-extensions.d.ts.map +1 -1
- package/dist/services/todos-extensions.js +27 -8
- package/dist/services/todos-extensions.js.map +1 -1
- package/package.json +5 -4
- package/src/generated/metadata.ts +443 -133
- package/src/generated/openapi-stripped.json +8999 -4907
- package/src/generated/path-mapping.ts +40 -16
- package/src/generated/schema.d.ts +5508 -2346
- package/src/generated/services/bookmarks.ts +158 -0
- package/src/generated/services/boosts.ts +12 -0
- package/src/generated/services/calendars.ts +102 -0
- package/src/generated/services/campfires.ts +107 -85
- package/src/generated/services/cards.ts +6 -0
- package/src/generated/services/checkins.ts +20 -0
- package/src/generated/services/client-approvals.ts +10 -5
- package/src/generated/services/client-correspondences.ts +10 -5
- package/src/generated/services/client-replies.ts +18 -8
- package/src/generated/services/cloud-files.ts +188 -0
- package/src/generated/services/comments.ts +6 -0
- package/src/generated/services/documents.ts +14 -8
- package/src/generated/services/drafts.ts +67 -0
- package/src/generated/services/events.ts +6 -0
- package/src/generated/services/everything.ts +100 -25
- package/src/generated/services/folders.ts +201 -0
- package/src/generated/services/forwards.ts +10 -48
- package/src/generated/services/gauges.ts +9 -1
- package/src/generated/services/google-documents.ts +196 -0
- package/src/generated/services/index.ts +12 -5
- package/src/generated/services/messages.ts +3 -1
- package/src/generated/services/my-assignments.ts +104 -0
- package/src/generated/services/my-notes.ts +92 -0
- package/src/generated/services/my-notifications.ts +4 -2
- package/src/generated/services/people.ts +15 -1
- package/src/generated/services/projects.ts +61 -1
- package/src/generated/services/recordings.ts +3 -31
- package/src/generated/services/reports.ts +13 -14
- package/src/generated/services/schedules.ts +107 -17
- package/src/generated/services/search.ts +3 -1
- package/src/generated/services/templates.ts +3 -1
- package/src/generated/services/timeline.ts +3 -0
- package/src/generated/services/timesheets.ts +35 -2
- package/src/generated/services/todolist-groups.ts +13 -7
- package/src/generated/services/todolists.ts +19 -14
- package/src/generated/services/todos.ts +74 -30
- package/src/generated/services/uploads.ts +6 -0
- package/src/generated/services/vaults.ts +6 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { SchedulesService as GeneratedSchedulesService } from "../generated/services/schedules.js";
|
|
2
|
+
import { requireRecord, requiredWritableBoolean, requiredWritableString, writableBoolean, writableIdList, writableString, } from "./merge-safe.js";
|
|
3
|
+
/** The deliberate-overwrite escape hatch named in this composite's error hints. */
|
|
4
|
+
const ESCAPE = "replaceEntry()";
|
|
5
|
+
/**
|
|
6
|
+
* How the guards name this record in their messages. Spelled with a space
|
|
7
|
+
* because {@link requireRecord} lowercases it into a sentence ("...where a
|
|
8
|
+
* schedule entry object was expected"); "ScheduleEntry" would read as
|
|
9
|
+
* "scheduleentry" there.
|
|
10
|
+
*/
|
|
11
|
+
const RECORD = "Schedule entry";
|
|
12
|
+
const guardOptions = { record: RECORD, escape: ESCAPE };
|
|
13
|
+
/** The carve-out members, keyed as they appear on {@link ScheduleEntryFields}. */
|
|
14
|
+
const CARVE_OUTS = ["participantIds", "url", "highlighted", "notify"];
|
|
15
|
+
/**
|
|
16
|
+
* SchedulesService with merge-safe `updateEntry` and read-modify-write
|
|
17
|
+
* `editEntry` on top of the generated surface (`getEntry`, `replaceEntry`,
|
|
18
|
+
* `createEntry`, `listEntries`, ...).
|
|
19
|
+
*
|
|
20
|
+
* `PUT /{accountId}/schedule_entries/{entryId}` is a full replace:
|
|
21
|
+
* `Schedules::EntriesController#update` rebuilds the recordable from the
|
|
22
|
+
* submitted params, so a sparse PUT that omits `description` erases it and one
|
|
23
|
+
* that omits `all_day` turns an all-day event into a midnight-to-midnight timed
|
|
24
|
+
* one. Neither is a 422 — both are a `200` that quietly clears, which is what
|
|
25
|
+
* these composites exist to prevent.
|
|
26
|
+
*
|
|
27
|
+
* Three writable fields are exempt. BC3 seeds `participant_ids`, `url` and
|
|
28
|
+
* `highlighted` from the existing recordable when the request does not address
|
|
29
|
+
* them, so the SDK's job for those is the opposite one: keep them **off** the
|
|
30
|
+
* wire unless the caller asked for them.
|
|
31
|
+
*
|
|
32
|
+
* Both composites compose the public `getEntry` and `replaceEntry`, so hooks
|
|
33
|
+
* observe the two wire operations under their native identities
|
|
34
|
+
* (`GetScheduleEntry` then `ReplaceScheduleEntry`) rather than a synthetic
|
|
35
|
+
* composite.
|
|
36
|
+
*
|
|
37
|
+
* **Non-recurring entries only.** `ensure_non_recurring_event` 302-redirects
|
|
38
|
+
* both `show` and `update` for a recurring entry, and the SDK follows neither:
|
|
39
|
+
* the GET surfaces as an unexpected body and the PUT as a redirect. Recurrence
|
|
40
|
+
* itself (`recurrence_schedule`, `recurs_until`, `time_zone_name`) is unmodeled
|
|
41
|
+
* — BC3 forces all three to nil for a non-recurring entry.
|
|
42
|
+
*/
|
|
43
|
+
export class SchedulesService extends GeneratedSchedulesService {
|
|
44
|
+
/**
|
|
45
|
+
* Sets the given fields on a schedule entry and preserves everything else:
|
|
46
|
+
* GETs the current entry, overlays the fields you addressed, and PUTs the
|
|
47
|
+
* full representation back.
|
|
48
|
+
*
|
|
49
|
+
* A key you leave off the request is not addressed. For the five full-state
|
|
50
|
+
* fields that means "keep what the server has"; for the four carve-outs it
|
|
51
|
+
* means the key never appears in the body, so BC3's own preservation applies.
|
|
52
|
+
* An explicitly-passed `""`, `false` or `[]` is an address and clears.
|
|
53
|
+
*
|
|
54
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
55
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
56
|
+
* for the full-state fields. The window is one round-trip. Use `replaceEntry`
|
|
57
|
+
* to overwrite deliberately.
|
|
58
|
+
*
|
|
59
|
+
* @param entryId - The entry ID
|
|
60
|
+
* @param req - Fields to set; unaddressed fields are preserved
|
|
61
|
+
* @returns The updated ScheduleEntry
|
|
62
|
+
* @throws {BasecampError} If the request fails, or if the read-back is malformed
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* // Rename without erasing the description, times or all-day flag.
|
|
67
|
+
* await client.schedules.updateEntry(123, { summary: "Team Meeting & Kickoff" });
|
|
68
|
+
*
|
|
69
|
+
* // Drop the join link and the highlight; participants are left alone.
|
|
70
|
+
* await client.schedules.updateEntry(123, { url: "", highlighted: false });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
async updateEntry(entryId, req) {
|
|
74
|
+
const current = await this.currentState(entryId);
|
|
75
|
+
const addressed = (key) => Object.prototype.hasOwnProperty.call(req, key);
|
|
76
|
+
// Full state: always sent. `??` covers the one degenerate presence case —
|
|
77
|
+
// a key set to `undefined` (or, from untyped JS, `null`). Neither can ride
|
|
78
|
+
// the wire, so honouring it as an address would drop the key and hand BC3
|
|
79
|
+
// the clear-by-omission this composite exists to prevent; the read-back
|
|
80
|
+
// value is the answer instead.
|
|
81
|
+
const state = {
|
|
82
|
+
summary: addressed("summary") ? (req.summary ?? current.summary) : current.summary,
|
|
83
|
+
startsAt: addressed("startsAt") ? (req.startsAt ?? current.startsAt) : current.startsAt,
|
|
84
|
+
endsAt: addressed("endsAt") ? (req.endsAt ?? current.endsAt) : current.endsAt,
|
|
85
|
+
description: addressed("description")
|
|
86
|
+
? (req.description ?? current.description)
|
|
87
|
+
: current.description,
|
|
88
|
+
allDay: addressed("allDay") ? (req.allDay ?? current.allDay) : current.allDay,
|
|
89
|
+
};
|
|
90
|
+
// Addressed-only: never seeded, so presence alone decides. `false`, `""`
|
|
91
|
+
// and `[]` all pass, which is the point — they are clears, and BC3 would
|
|
92
|
+
// preserve rather than clear if the key went missing.
|
|
93
|
+
const carveOuts = {};
|
|
94
|
+
if (addressed("participantIds"))
|
|
95
|
+
carveOuts.participantIds = req.participantIds;
|
|
96
|
+
if (addressed("url"))
|
|
97
|
+
carveOuts.url = req.url;
|
|
98
|
+
if (addressed("highlighted"))
|
|
99
|
+
carveOuts.highlighted = req.highlighted;
|
|
100
|
+
if (addressed("notify"))
|
|
101
|
+
carveOuts.notify = req.notify;
|
|
102
|
+
return this.putEntry(entryId, state, carveOuts);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Applies a read-modify-write callback to a schedule entry: GETs the current
|
|
106
|
+
* entry, hands the callback its writable state, and PUTs the whole thing
|
|
107
|
+
* back. If the callback throws (or rejects), the edit aborts and nothing is
|
|
108
|
+
* written.
|
|
109
|
+
*
|
|
110
|
+
* The five full-state fields always ride back, so clearing one means setting
|
|
111
|
+
* it empty and an untouched one keeps its current value. The four carve-outs
|
|
112
|
+
* (`participantIds`, `url`, `highlighted`, `notify`) are seeded so you can
|
|
113
|
+
* read them, but they reach the wire only if the callback **assigns** to
|
|
114
|
+
* them. Assignment is tracked by invocation, not by comparing values:
|
|
115
|
+
* `e.url = e.url` is a write, because a caller who reassigns the value the
|
|
116
|
+
* read returned still means "write this", and no diff can recover that
|
|
117
|
+
* intent.
|
|
118
|
+
*
|
|
119
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
120
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
121
|
+
* for the full-state fields. The window is one round-trip. Use `replaceEntry`
|
|
122
|
+
* to overwrite deliberately.
|
|
123
|
+
*
|
|
124
|
+
* @param entryId - The entry ID
|
|
125
|
+
* @param fn - Callback that mutates the entry's writable fields in place
|
|
126
|
+
* @returns The updated ScheduleEntry
|
|
127
|
+
* @throws {BasecampError} If the request fails, or if the read-back is malformed
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* await client.schedules.editEntry(123, (e) => {
|
|
132
|
+
* e.summary = `🚨 ${e.summary}`;
|
|
133
|
+
* e.description = ""; // clearing = setting empty on a full object
|
|
134
|
+
* // participants, join link and highlight are untouched, so they stay off
|
|
135
|
+
* // the wire and BC3 keeps them.
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
async editEntry(entryId, fn) {
|
|
140
|
+
const fields = await this.currentFields(entryId);
|
|
141
|
+
// Setter-invocation dirty tracking. A snapshot diff would be wrong, not
|
|
142
|
+
// merely different: the callback that assigns exactly what the GET returned
|
|
143
|
+
// has still asked for a write, and dropping it hands the field back to
|
|
144
|
+
// BC3's preserve-on-omission.
|
|
145
|
+
const touched = new Set();
|
|
146
|
+
const view = new Proxy(fields, {
|
|
147
|
+
set(target, property, value) {
|
|
148
|
+
if (typeof property === "string")
|
|
149
|
+
touched.add(property);
|
|
150
|
+
return Reflect.set(target, property, value);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
await fn(view);
|
|
154
|
+
const carveOuts = {};
|
|
155
|
+
if (touched.has("participantIds"))
|
|
156
|
+
carveOuts.participantIds = fields.participantIds;
|
|
157
|
+
if (touched.has("url"))
|
|
158
|
+
carveOuts.url = fields.url;
|
|
159
|
+
if (touched.has("highlighted"))
|
|
160
|
+
carveOuts.highlighted = fields.highlighted;
|
|
161
|
+
if (touched.has("notify"))
|
|
162
|
+
carveOuts.notify = fields.notify;
|
|
163
|
+
return this.putEntry(entryId, fields, carveOuts);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Fetches the entry and derives the full-state half of its writable state.
|
|
167
|
+
*
|
|
168
|
+
* Every value here is resent in the full-replace PUT, so every value is
|
|
169
|
+
* validated before it is read. `?? ""` coalesces only `null` and `undefined`,
|
|
170
|
+
* leaving corruption wide open: `false`, `0`, `[]`, `{}`, `42`, `true` and
|
|
171
|
+
* friends would all be forwarded **verbatim** and written to the entry on a
|
|
172
|
+
* call that never mentioned the field. Nothing below this rejects them —
|
|
173
|
+
* `schema.d.ts` is erased at build time, so `ScheduleEntry` is a compile-time
|
|
174
|
+
* claim about runtime data, which puts this composite with Python and Ruby
|
|
175
|
+
* rather than with Go and Swift. See `merge-safe.ts` and #576.
|
|
176
|
+
*
|
|
177
|
+
* The guards differ per field because the spec models the fields
|
|
178
|
+
* differently:
|
|
179
|
+
*
|
|
180
|
+
* - `summary` is `@required` and `Schedule::Entry#summary` is
|
|
181
|
+
* `super.presence || "Untitled"`, so blank is impossible from a healthy
|
|
182
|
+
* server and absent/null/blank is malformed.
|
|
183
|
+
* - `starts_at`/`ends_at` are `@required` and `NOT NULL`. The value is a bare
|
|
184
|
+
* date for an all-day entry and a timestamp otherwise; it round-trips
|
|
185
|
+
* verbatim rather than through a `Date`, which would normalise the two
|
|
186
|
+
* spellings into one and rewrite the entry's shape.
|
|
187
|
+
* - `all_day` is `@required`, `NOT NULL DEFAULT false`. It needs the boolean
|
|
188
|
+
* guard specifically because a truthiness test would read the legitimate
|
|
189
|
+
* `false` as missing — and defaulting a missing value to `false` converts
|
|
190
|
+
* an all-day event into a midnight-to-midnight timed one.
|
|
191
|
+
* - `description` is optional and nullable both ways, so absent or null is a
|
|
192
|
+
* genuinely empty description.
|
|
193
|
+
*/
|
|
194
|
+
async currentState(entryId) {
|
|
195
|
+
const current = await this.readEntry(entryId);
|
|
196
|
+
return {
|
|
197
|
+
summary: requiredWritableString(current, "summary", guardOptions),
|
|
198
|
+
startsAt: requiredWritableString(current, "starts_at", guardOptions),
|
|
199
|
+
endsAt: requiredWritableString(current, "ends_at", guardOptions),
|
|
200
|
+
description: writableString(current, "description", guardOptions),
|
|
201
|
+
allDay: requiredWritableBoolean(current, "all_day", guardOptions),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The full-state fields plus the carve-out seeds the `editEntry` callback can
|
|
206
|
+
* read.
|
|
207
|
+
*
|
|
208
|
+
* `url` is seeded from the response's **`join_url`**, not its `url`: the
|
|
209
|
+
* latter is the entry's own Basecamp API URL, written by
|
|
210
|
+
* `recordings/_recording` before the entry partial renders. Echoing it into
|
|
211
|
+
* the request's `url` would write the API URL into the join link.
|
|
212
|
+
*
|
|
213
|
+
* `updateEntry` deliberately does not come through here. It never seeds a
|
|
214
|
+
* carve-out, so validating `participants` for it would refuse a write that
|
|
215
|
+
* does not depend on them.
|
|
216
|
+
*/
|
|
217
|
+
async currentFields(entryId) {
|
|
218
|
+
const current = await this.readEntry(entryId);
|
|
219
|
+
return {
|
|
220
|
+
summary: requiredWritableString(current, "summary", guardOptions),
|
|
221
|
+
startsAt: requiredWritableString(current, "starts_at", guardOptions),
|
|
222
|
+
endsAt: requiredWritableString(current, "ends_at", guardOptions),
|
|
223
|
+
description: writableString(current, "description", guardOptions),
|
|
224
|
+
allDay: requiredWritableBoolean(current, "all_day", guardOptions),
|
|
225
|
+
participantIds: writableIdList(current, "participants", guardOptions),
|
|
226
|
+
url: writableString(current, "join_url", guardOptions),
|
|
227
|
+
highlighted: writableBoolean(current, "highlighted", guardOptions),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* GETs the entry and checks the envelope before any field is read.
|
|
232
|
+
*
|
|
233
|
+
* A successful GET can still return a scalar, an array or null; reading a
|
|
234
|
+
* property off `null` throws a raw `TypeError` instead of the documented
|
|
235
|
+
* statusless `api_error`, and reading one off an array or string silently
|
|
236
|
+
* yields `undefined`, which the field guards would then take at face value.
|
|
237
|
+
*/
|
|
238
|
+
async readEntry(entryId) {
|
|
239
|
+
return requireRecord(await this.getEntry(entryId), {
|
|
240
|
+
record: RECORD,
|
|
241
|
+
operation: "GetScheduleEntry",
|
|
242
|
+
escape: ESCAPE,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* PUTs the full writable state via `replaceEntry`, plus whichever carve-outs
|
|
247
|
+
* the caller addressed.
|
|
248
|
+
*
|
|
249
|
+
* The five full-state fields are always named, empties included: on a
|
|
250
|
+
* full-replace endpoint `""` is how a clear is expressed — never JSON null
|
|
251
|
+
* (SPEC §18 body compaction), and never by omission, which would leave the
|
|
252
|
+
* field to BC3's clear-by-default and read as an accident rather than an
|
|
253
|
+
* intent. The carve-outs are the mirror image: an unaddressed one is absent
|
|
254
|
+
* from `carveOuts`, so the generated request builder leaves it `undefined`
|
|
255
|
+
* and it never reaches the wire.
|
|
256
|
+
*/
|
|
257
|
+
putEntry(entryId, state, carveOuts) {
|
|
258
|
+
return this.replaceEntry(entryId, {
|
|
259
|
+
summary: state.summary,
|
|
260
|
+
startsAt: state.startsAt,
|
|
261
|
+
endsAt: state.endsAt,
|
|
262
|
+
description: state.description,
|
|
263
|
+
allDay: state.allDay,
|
|
264
|
+
...carveOuts,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=schedules-extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedules-extensions.js","sourceRoot":"","sources":["../../src/services/schedules-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,IAAI,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAKnG,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACtB,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,mFAAmF;AACnF,MAAM,MAAM,GAAG,gBAAgB,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,MAAM,GAAG,gBAAgB,CAAC;AAEhC,MAAM,YAAY,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAyGxD,kFAAkF;AAClF,MAAM,UAAU,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,CAAU,CAAC;AAM/E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,gBAAiB,SAAQ,yBAAyB;IAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,GAA+B;QAChE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,CAAC,GAAqC,EAAW,EAAE,CACnE,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAEjD,0EAA0E;QAC1E,2EAA2E;QAC3E,0EAA0E;QAC1E,wEAAwE;QACxE,+BAA+B;QAC/B,MAAM,KAAK,GAAuB;YAChC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO;YAClF,QAAQ,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ;YACvF,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM;YAC7E,WAAW,EAAE,SAAS,CAAC,aAAa,CAAC;gBACnC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;gBAC1C,CAAC,CAAC,OAAO,CAAC,WAAW;YACvB,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM;SAC9E,CAAC;QAEF,yEAAyE;QACzE,yEAAyE;QACzE,sDAAsD;QACtD,MAAM,SAAS,GAAoB,EAAE,CAAC;QACtC,IAAI,SAAS,CAAC,gBAAgB,CAAC;YAAE,SAAS,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,CAAC;QAC/E,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;QAC9C,IAAI,SAAS,CAAC,aAAa,CAAC;YAAE,SAAS,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;QACtE,IAAI,SAAS,CAAC,QAAQ,CAAC;YAAE,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAEvD,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,KAAK,CAAC,SAAS,CACb,OAAe,EACf,EAAoD;QAEpD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAEjD,wEAAwE;QACxE,4EAA4E;QAC5E,uEAAuE;QACvE,8BAA8B;QAC9B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE;YAC7B,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK;gBACzB,IAAI,OAAO,QAAQ,KAAK,QAAQ;oBAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACxD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC9C,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QAEf,MAAM,SAAS,GAAoB,EAAE,CAAC;QACtC,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;YAAE,SAAS,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;QACpF,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;QACnD,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;YAAE,SAAS,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAE5D,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACK,KAAK,CAAC,YAAY,CAAC,OAAe;QACxC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO;YACL,OAAO,EAAE,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;YACjE,QAAQ,EAAE,sBAAsB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC;YACpE,MAAM,EAAE,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;YAChE,WAAW,EAAE,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,CAAC;YACjE,MAAM,EAAE,uBAAuB,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;SAClE,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;OAYG;IACK,KAAK,CAAC,aAAa,CAAC,OAAe;QACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO;YACL,OAAO,EAAE,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;YACjE,QAAQ,EAAE,sBAAsB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC;YACpE,MAAM,EAAE,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;YAChE,WAAW,EAAE,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,CAAC;YACjE,MAAM,EAAE,uBAAuB,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC;YACjE,cAAc,EAAE,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC;YACrE,GAAG,EAAE,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC;YACtD,WAAW,EAAE,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,CAAC;SACnE,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,SAAS,CAAC,OAAe;QACrC,OAAO,aAAa,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,kBAAkB;YAC7B,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;OAWG;IACK,QAAQ,CACd,OAAe,EACf,KAAyB,EACzB,SAA0B;QAE1B,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;YAChC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,SAAS;SACb,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { TodolistsService as GeneratedTodolistsService } from "../generated/services/todolists.js";
|
|
2
|
+
import type { Todolist } from "../generated/services/todolists.js";
|
|
3
|
+
/**
|
|
4
|
+
* Request parameters for update. Both fields are optional: an omitted field
|
|
5
|
+
* is left untouched on the todolist, guaranteed. An explicitly-passed empty
|
|
6
|
+
* string is a set (clears the description).
|
|
7
|
+
*/
|
|
8
|
+
export interface UpdateTodolistRequest {
|
|
9
|
+
/** Display name. Omit to leave unchanged. */
|
|
10
|
+
name?: string;
|
|
11
|
+
/** Rich text description (HTML). Omit to leave unchanged; `""` clears it. */
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A todolist's full writable state, handed to the `edit` callback. The whole
|
|
16
|
+
* object is PUT back to the server, so clearing the description means setting
|
|
17
|
+
* it empty (`""`) — there is no third state.
|
|
18
|
+
*/
|
|
19
|
+
export interface TodolistFields {
|
|
20
|
+
/** Display name (required; the server rejects a missing one with a 422). */
|
|
21
|
+
name: string;
|
|
22
|
+
/** Rich text description (HTML). Set `""` to clear. */
|
|
23
|
+
description: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* TodolistsService with merge-safe `update` and read-modify-write `edit` on
|
|
27
|
+
* top of the generated surface (`get`, `replace`, ...).
|
|
28
|
+
*
|
|
29
|
+
* `PUT /{accountId}/todolists/{id}` is a full replace: BC3's
|
|
30
|
+
* `TodolistsController#update` rebuilds the recordable from only the permitted
|
|
31
|
+
* params, so a sparse PUT that omits `description` erases it, and one that
|
|
32
|
+
* omits `name` is a 422 — the attribute is presence-validated server-side, so
|
|
33
|
+
* omitting it is never a preserve. The writable set is exactly
|
|
34
|
+
* `{name, description}`.
|
|
35
|
+
*
|
|
36
|
+
* The route serves a to-do list and a to-do list group alike, and since #544
|
|
37
|
+
* both are the one flat `Todolist` shape — BC3's
|
|
38
|
+
* `todolists/groups/{index,show}.json.jbuilder` render
|
|
39
|
+
* `todolists/_todolist.json.jbuilder`, so a group reports `"type": "Todolist"`
|
|
40
|
+
* and carries `description`/`description_attachments` like any list. Nothing
|
|
41
|
+
* here branches on the variant; where it matters at all, discrimination is
|
|
42
|
+
* structural (`groups_url` for a Todoset parent, `group_position_url` for a
|
|
43
|
+
* Todolist parent), never the `type` string.
|
|
44
|
+
*
|
|
45
|
+
* Both methods compose the public `get` and `replace` methods, so hooks
|
|
46
|
+
* observe the two wire operations (`GetTodolistOrGroup` then
|
|
47
|
+
* `UpdateTodolistOrGroup`), not a synthetic composite.
|
|
48
|
+
*/
|
|
49
|
+
export declare class TodolistsService extends GeneratedTodolistsService {
|
|
50
|
+
/**
|
|
51
|
+
* Sets the given fields on a todolist and preserves everything else: GETs
|
|
52
|
+
* the current todolist, overlays the explicitly-set request fields, and PUTs
|
|
53
|
+
* the full representation back. An omitted (`undefined`) field is untouched,
|
|
54
|
+
* guaranteed; an explicitly-passed `""` clears the description.
|
|
55
|
+
*
|
|
56
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
57
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
58
|
+
* for the whole representation. The window is one round-trip. Use `replace`
|
|
59
|
+
* to overwrite deliberately.
|
|
60
|
+
*
|
|
61
|
+
* @param id - The todolist ID
|
|
62
|
+
* @param req - Fields to set; omitted fields are preserved
|
|
63
|
+
* @returns The updated Todolist
|
|
64
|
+
* @throws {BasecampError} If the request fails
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* // Rename without erasing the description.
|
|
69
|
+
* await client.todolists.update(123, { name: "Hardware" });
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
update(id: number, req: UpdateTodolistRequest): Promise<Todolist>;
|
|
73
|
+
/**
|
|
74
|
+
* Applies a read-modify-write callback to a todolist: GETs the current
|
|
75
|
+
* todolist, hands the callback its full writable representation, and PUTs
|
|
76
|
+
* the whole thing back. Clearing the description means setting it empty
|
|
77
|
+
* (`""`) — an untouched field keeps its current value. If the callback
|
|
78
|
+
* throws (or rejects), the edit aborts and nothing is written.
|
|
79
|
+
*
|
|
80
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
81
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
82
|
+
* for the whole representation. The window is one round-trip. Use `replace`
|
|
83
|
+
* to overwrite deliberately.
|
|
84
|
+
*
|
|
85
|
+
* @param id - The todolist ID
|
|
86
|
+
* @param fn - Callback that mutates the todolist's writable fields in place
|
|
87
|
+
* @returns The updated Todolist
|
|
88
|
+
* @throws {BasecampError} If the request fails
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* await client.todolists.edit(123, (t) => {
|
|
93
|
+
* t.name = `🚨 ${t.name}`;
|
|
94
|
+
* t.description = ""; // clearing = setting empty on a full object
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
edit(id: number, fn: (t: TodolistFields) => void | Promise<void>): Promise<Todolist>;
|
|
99
|
+
/** Fetches the todolist and derives its full writable state. */
|
|
100
|
+
private currentFields;
|
|
101
|
+
/**
|
|
102
|
+
* PUTs the full writable state via `replace`. Both fields are always sent:
|
|
103
|
+
* description included when empty, because on a full-replace endpoint `""`
|
|
104
|
+
* is how a clear is expressed — never JSON null (SPEC §18 body compaction),
|
|
105
|
+
* and never by omission, which would leave the field to the server's own
|
|
106
|
+
* clear-by-default and read as an accident rather than an intent.
|
|
107
|
+
*/
|
|
108
|
+
private putFields;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=todolists-extensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todolists-extensions.d.ts","sourceRoot":"","sources":["../../src/services/todolists-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,IAAI,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAwKnE;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,gBAAiB,SAAQ,yBAAyB;IAC7D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAOvE;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;IAM1F,gEAAgE;YAClD,aAAa;IAQ3B;;;;;;OAMG;YACW,SAAS;CAyBxB"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { TodolistsService as GeneratedTodolistsService } from "../generated/services/todolists.js";
|
|
2
|
+
import { Errors, truncateErrorMessage } from "../errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* Renders a value for an error message without ever throwing.
|
|
5
|
+
*
|
|
6
|
+
* The guard's own error path must not fail while explaining a failure.
|
|
7
|
+
* `JSON.stringify` raises `TypeError` on a circular structure, and a value can
|
|
8
|
+
* carry a `toJSON` that throws — either would replace a clean `api_error`/`usage`
|
|
9
|
+
* with an incidental `TypeError` and lose the diagnosis. The type name is always
|
|
10
|
+
* available; the rendering is a bonus, capped per §9 and dropped if it fails.
|
|
11
|
+
*/
|
|
12
|
+
function describeValue(value) {
|
|
13
|
+
const kind = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
14
|
+
try {
|
|
15
|
+
const rendered = JSON.stringify(value);
|
|
16
|
+
return rendered === undefined ? kind : `${kind} ${truncateErrorMessage(rendered)}`;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return kind;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Level 1 of the wire-to-written-value path: the response must be a JSON object
|
|
24
|
+
* before any field is probed.
|
|
25
|
+
*
|
|
26
|
+
* One level up from the malformed-*field* guards. A successful GET can still
|
|
27
|
+
* return a scalar, an array, or null, and each fails a different way without
|
|
28
|
+
* this: `null.name` throws a raw `TypeError` instead of the documented
|
|
29
|
+
* statusless `api_error`, while a scalar or an **array** yields `undefined` for
|
|
30
|
+
* every key and so gets misdiagnosed one rung down as "name is missing from the
|
|
31
|
+
* response" — a body-shape fault reported as a field fault. The array case is
|
|
32
|
+
* the one that used to fall through silently, and `Array.isArray` is what keeps
|
|
33
|
+
* it caught here.
|
|
34
|
+
*
|
|
35
|
+
* Since #544 the route is modelled as one flat `Todolist` — the `TodolistOrGroup`
|
|
36
|
+
* union and its `{ todolist } | { group }` arms are gone, and with them the
|
|
37
|
+
* middle rung of this path. It is object → scalar now: the body (here) and each
|
|
38
|
+
* writable field (`writableString`). A string has no interior, so there is no
|
|
39
|
+
* third. What is emphatically NOT gone is validation: `schema.d.ts` is erased at
|
|
40
|
+
* build time, so a flat shape gives TypeScript no decoder it did not have
|
|
41
|
+
* before. Structural safety for this SDK is #578.
|
|
42
|
+
*
|
|
43
|
+
* The cast is an assertion about the body's shape only. It claims nothing about
|
|
44
|
+
* the fields, which `writableString` still checks one by one.
|
|
45
|
+
*/
|
|
46
|
+
function requireTodolistObject(response, operation) {
|
|
47
|
+
if (typeof response !== "object" || response === null || Array.isArray(response)) {
|
|
48
|
+
throw malformedResponse(`${operation} returned ${describeValue(response)} where a todolist object was expected`, "The merge-safe update/edit read this record's fields before rewriting them, so a " +
|
|
49
|
+
"non-object body cannot be used. Use replace() to write the record deliberately.");
|
|
50
|
+
}
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validates a caller-supplied writable value, the mirror of the read step.
|
|
55
|
+
*
|
|
56
|
+
* `writableString` owns *response* provenance; this owns *caller* provenance,
|
|
57
|
+
* and the two are one rule seen from opposite ends. `edit` hands the caller a
|
|
58
|
+
* mutable `TodolistFields` and the annotation is erased at build time, so a
|
|
59
|
+
* closure assigning `42` or `[]` — trivially reachable from plain JS, or from
|
|
60
|
+
* TypeScript via `as any` — would otherwise walk straight into the full-replace
|
|
61
|
+
* PUT and write it. That is caller misuse, hence `usage`, where the same wrong
|
|
62
|
+
* type arriving from the server is `api_error`.
|
|
63
|
+
*/
|
|
64
|
+
function callerString(fields, key) {
|
|
65
|
+
const value = fields[key];
|
|
66
|
+
if (typeof value !== "string") {
|
|
67
|
+
throw Errors.usage(truncateErrorMessage(`todolist ${key} must be a string, got ${describeValue(value)}`), "The full writable state is PUT back verbatim, so a non-string would be written to the " +
|
|
68
|
+
'record. Assign a string; use "" to clear.');
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
/** Builds the malformed-response error, with the message capped per SPEC §9. */
|
|
73
|
+
function malformedResponse(message, hint) {
|
|
74
|
+
// api_error, not usage: the value arrived in a successful API response, so
|
|
75
|
+
// nothing the caller passed is at fault. Statusless — there is no HTTP status
|
|
76
|
+
// to attribute, the transport succeeded — and non-retryable, because
|
|
77
|
+
// re-requesting cannot repair a malformed body.
|
|
78
|
+
return Errors.apiError(truncateErrorMessage(message), undefined, {
|
|
79
|
+
hint,
|
|
80
|
+
retryable: false,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Reads a writable string field off a fetched todolist, refusing to pass a
|
|
85
|
+
* malformed one through.
|
|
86
|
+
*
|
|
87
|
+
* **Classification is by origin, not by value.** The same empty string is a
|
|
88
|
+
* caller error when the caller passed it and malformed response data when it
|
|
89
|
+
* came off the wire, so each provenance is checked where it is unambiguous:
|
|
90
|
+
* this read step owns the response, and `putFields` owns the caller. That is
|
|
91
|
+
* why an empty `name` here is an `api_error` while an empty `name` the caller
|
|
92
|
+
* supplied is a `UsageError` — same value, different origin, different fault.
|
|
93
|
+
*
|
|
94
|
+
* **Presence and non-emptiness are two different claims, and only one of them
|
|
95
|
+
* is per-field.** Since #544 `name` and `description` are both `@required` and
|
|
96
|
+
* never null on this shape — BC3's `format_api_content` funnels a blank rich
|
|
97
|
+
* text through `call_pipeline`, which returns `""` rather than nil — so for
|
|
98
|
+
* *both* fields an absent key and an explicit `null` are malformed, and both
|
|
99
|
+
* are refused here before any PUT. Reading either as `""` would put that `""`
|
|
100
|
+
* in the full-replace body and erase the record's real value on a call that
|
|
101
|
+
* never mentioned the field.
|
|
102
|
+
*
|
|
103
|
+
* `nonEmpty` is the *other* claim and holds for `name` alone: BC3
|
|
104
|
+
* presence-validates the attribute, so no real todolist carries an empty one
|
|
105
|
+
* and `""` off the wire is malformed too. `description` has no such validation
|
|
106
|
+
* — a description-less list carries `""`, which is the ordinary case — so an
|
|
107
|
+
* empty description is a real value, preserved and resent verbatim.
|
|
108
|
+
*
|
|
109
|
+
* Anything of the wrong type is malformed either way: `?? ""` only coalesces
|
|
110
|
+
* null and undefined, so a number, boolean, array or object would ride through
|
|
111
|
+
* **verbatim** into the full-replace PUT and overwrite the real value.
|
|
112
|
+
*
|
|
113
|
+
* The type annotation on the GET result is a compile-time claim about runtime
|
|
114
|
+
* data that nothing validates — `schema.d.ts` is erased at build time, so
|
|
115
|
+
* TypeScript has no decoder that rejects a wrong-typed field the way Go's
|
|
116
|
+
* `json.Unmarshal`, Swift's `Codable`, or kotlinx.serialization do. That puts
|
|
117
|
+
* this composite in the same position as the Python and Ruby ones: the check
|
|
118
|
+
* has to be explicit here. The shipped Todos and Cards analogues are #576.
|
|
119
|
+
*/
|
|
120
|
+
function writableString(todolist, key, opts = {}) {
|
|
121
|
+
const value = todolist[key];
|
|
122
|
+
if (!(key in todolist) || value === undefined) {
|
|
123
|
+
throw malformedResponse(`todolist ${key} is missing from the response`, `${key} is required on every todolist, so a body without one is a malformed response, ` +
|
|
124
|
+
"not an empty value to preserve. The merge-safe update/edit PUT the full writable " +
|
|
125
|
+
"state back, so reading it as empty would erase the record's real value.");
|
|
126
|
+
}
|
|
127
|
+
if (value === null) {
|
|
128
|
+
throw malformedResponse(`todolist ${key} is null in the response`, `${key} is required and never null, so a null one is a malformed response, not an empty ` +
|
|
129
|
+
"value to preserve. The merge-safe update/edit PUT the full writable state back, so " +
|
|
130
|
+
"reading it as empty would erase the record's real value.");
|
|
131
|
+
}
|
|
132
|
+
if (typeof value !== "string") {
|
|
133
|
+
throw malformedResponse(`todolist ${key} is not a string: ${describeValue(value)}`, "The merge-safe update/edit resend this field verbatim, so a malformed value would " +
|
|
134
|
+
"overwrite the current one. Use replace() to write the record deliberately.");
|
|
135
|
+
}
|
|
136
|
+
if (opts.nonEmpty && value === "") {
|
|
137
|
+
throw malformedResponse(`todolist ${key} is empty in the response`, `${key} is presence-validated server-side, so an empty one is a malformed response. ` +
|
|
138
|
+
"The caller did not ask to clear it.");
|
|
139
|
+
}
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* TodolistsService with merge-safe `update` and read-modify-write `edit` on
|
|
144
|
+
* top of the generated surface (`get`, `replace`, ...).
|
|
145
|
+
*
|
|
146
|
+
* `PUT /{accountId}/todolists/{id}` is a full replace: BC3's
|
|
147
|
+
* `TodolistsController#update` rebuilds the recordable from only the permitted
|
|
148
|
+
* params, so a sparse PUT that omits `description` erases it, and one that
|
|
149
|
+
* omits `name` is a 422 — the attribute is presence-validated server-side, so
|
|
150
|
+
* omitting it is never a preserve. The writable set is exactly
|
|
151
|
+
* `{name, description}`.
|
|
152
|
+
*
|
|
153
|
+
* The route serves a to-do list and a to-do list group alike, and since #544
|
|
154
|
+
* both are the one flat `Todolist` shape — BC3's
|
|
155
|
+
* `todolists/groups/{index,show}.json.jbuilder` render
|
|
156
|
+
* `todolists/_todolist.json.jbuilder`, so a group reports `"type": "Todolist"`
|
|
157
|
+
* and carries `description`/`description_attachments` like any list. Nothing
|
|
158
|
+
* here branches on the variant; where it matters at all, discrimination is
|
|
159
|
+
* structural (`groups_url` for a Todoset parent, `group_position_url` for a
|
|
160
|
+
* Todolist parent), never the `type` string.
|
|
161
|
+
*
|
|
162
|
+
* Both methods compose the public `get` and `replace` methods, so hooks
|
|
163
|
+
* observe the two wire operations (`GetTodolistOrGroup` then
|
|
164
|
+
* `UpdateTodolistOrGroup`), not a synthetic composite.
|
|
165
|
+
*/
|
|
166
|
+
export class TodolistsService extends GeneratedTodolistsService {
|
|
167
|
+
/**
|
|
168
|
+
* Sets the given fields on a todolist and preserves everything else: GETs
|
|
169
|
+
* the current todolist, overlays the explicitly-set request fields, and PUTs
|
|
170
|
+
* the full representation back. An omitted (`undefined`) field is untouched,
|
|
171
|
+
* guaranteed; an explicitly-passed `""` clears the description.
|
|
172
|
+
*
|
|
173
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
174
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
175
|
+
* for the whole representation. The window is one round-trip. Use `replace`
|
|
176
|
+
* to overwrite deliberately.
|
|
177
|
+
*
|
|
178
|
+
* @param id - The todolist ID
|
|
179
|
+
* @param req - Fields to set; omitted fields are preserved
|
|
180
|
+
* @returns The updated Todolist
|
|
181
|
+
* @throws {BasecampError} If the request fails
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* // Rename without erasing the description.
|
|
186
|
+
* await client.todolists.update(123, { name: "Hardware" });
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
async update(id, req) {
|
|
190
|
+
const fields = await this.currentFields(id);
|
|
191
|
+
if (req.name !== undefined)
|
|
192
|
+
fields.name = req.name;
|
|
193
|
+
if (req.description !== undefined)
|
|
194
|
+
fields.description = req.description;
|
|
195
|
+
return this.putFields(id, fields);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Applies a read-modify-write callback to a todolist: GETs the current
|
|
199
|
+
* todolist, hands the callback its full writable representation, and PUTs
|
|
200
|
+
* the whole thing back. Clearing the description means setting it empty
|
|
201
|
+
* (`""`) — an untouched field keeps its current value. If the callback
|
|
202
|
+
* throws (or rejects), the edit aborts and nothing is written.
|
|
203
|
+
*
|
|
204
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
205
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
206
|
+
* for the whole representation. The window is one round-trip. Use `replace`
|
|
207
|
+
* to overwrite deliberately.
|
|
208
|
+
*
|
|
209
|
+
* @param id - The todolist ID
|
|
210
|
+
* @param fn - Callback that mutates the todolist's writable fields in place
|
|
211
|
+
* @returns The updated Todolist
|
|
212
|
+
* @throws {BasecampError} If the request fails
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* await client.todolists.edit(123, (t) => {
|
|
217
|
+
* t.name = `🚨 ${t.name}`;
|
|
218
|
+
* t.description = ""; // clearing = setting empty on a full object
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
async edit(id, fn) {
|
|
223
|
+
const fields = await this.currentFields(id);
|
|
224
|
+
await fn(fields);
|
|
225
|
+
return this.putFields(id, fields);
|
|
226
|
+
}
|
|
227
|
+
/** Fetches the todolist and derives its full writable state. */
|
|
228
|
+
async currentFields(id) {
|
|
229
|
+
const current = requireTodolistObject(await this.get(id), "GetTodolistOrGroup");
|
|
230
|
+
return {
|
|
231
|
+
name: writableString(current, "name", { nonEmpty: true }),
|
|
232
|
+
description: writableString(current, "description"),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* PUTs the full writable state via `replace`. Both fields are always sent:
|
|
237
|
+
* description included when empty, because on a full-replace endpoint `""`
|
|
238
|
+
* is how a clear is expressed — never JSON null (SPEC §18 body compaction),
|
|
239
|
+
* and never by omission, which would leave the field to the server's own
|
|
240
|
+
* clear-by-default and read as an accident rather than an intent.
|
|
241
|
+
*/
|
|
242
|
+
async putFields(id, f) {
|
|
243
|
+
// The caller's side of the origin rule. `currentFields` already rejected an
|
|
244
|
+
// empty name coming off the wire, so by here an empty one can only have been
|
|
245
|
+
// supplied by the caller — genuine misuse, since BC3 presence-validates the
|
|
246
|
+
// attribute and a full write has no state that clears it. Guarding in the
|
|
247
|
+
// composite rather than leaning on the generated `replace()` also aligns
|
|
248
|
+
// this path with the other five SDKs; `replace()` keeps its own
|
|
249
|
+
// `Errors.validation` for direct callers, where the name really is theirs.
|
|
250
|
+
const name = callerString(f, "name");
|
|
251
|
+
const description = callerString(f, "description");
|
|
252
|
+
if (name === "") {
|
|
253
|
+
throw Errors.usage("todolist name must not be empty", "BC3 presence-validates the name, so a full write cannot clear it. Pass a non-empty " +
|
|
254
|
+
"name, or use replace() if you mean to write the record verbatim.");
|
|
255
|
+
}
|
|
256
|
+
// The PUT answers with the written record, which is handed straight back to
|
|
257
|
+
// the caller as a `Todolist`; check its shape too rather than let an array
|
|
258
|
+
// or a scalar out under that type.
|
|
259
|
+
return requireTodolistObject(await this.replace(id, { name, description }), "UpdateTodolistOrGroup");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=todolists-extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todolists-extensions.js","sourceRoot":"","sources":["../../src/services/todolists-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,IAAI,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAEnG,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE5D;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,MAAM,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC;IACrF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,qBAAqB,CAAC,QAAiB,EAAE,SAAiB;IACjE,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjF,MAAM,iBAAiB,CACrB,GAAG,SAAS,aAAa,aAAa,CAAC,QAAQ,CAAC,uCAAuC,EACvF,mFAAmF;YACjF,iFAAiF,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,QAA+B,CAAC;AACzC,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,YAAY,CAAC,MAAsB,EAAE,GAA2B;IACvE,MAAM,KAAK,GAAY,MAAM,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,CAAC,KAAK,CAChB,oBAAoB,CAAC,YAAY,GAAG,0BAA0B,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,EACrF,wFAAwF;YACtF,2CAA2C,CAC9C,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gFAAgF;AAChF,SAAS,iBAAiB,CAAC,OAAe,EAAE,IAAY;IACtD,2EAA2E;IAC3E,8EAA8E;IAC9E,qEAAqE;IACrE,gDAAgD;IAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE;QAC/D,IAAI;QACJ,SAAS,EAAE,KAAK;KACjB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,SAAS,cAAc,CACrB,QAAkB,EAClB,GAA2B,EAC3B,OAA+B,EAAE;IAEjC,MAAM,KAAK,GAAY,QAAQ,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,GAAG,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9C,MAAM,iBAAiB,CACrB,YAAY,GAAG,+BAA+B,EAC9C,GAAG,GAAG,iFAAiF;YACrF,mFAAmF;YACnF,yEAAyE,CAC5E,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,iBAAiB,CACrB,YAAY,GAAG,0BAA0B,EACzC,GAAG,GAAG,mFAAmF;YACvF,qFAAqF;YACrF,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,iBAAiB,CACrB,YAAY,GAAG,qBAAqB,aAAa,CAAC,KAAK,CAAC,EAAE,EAC1D,oFAAoF;YAClF,4EAA4E,CAC/E,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QAClC,MAAM,iBAAiB,CACrB,YAAY,GAAG,2BAA2B,EAC1C,GAAG,GAAG,+EAA+E;YACnF,qCAAqC,CACxC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AA0BD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,gBAAiB,SAAQ,yBAAyB;IAC7D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,GAA0B;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACnD,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;QACxE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,IAAI,CAAC,EAAU,EAAE,EAA+C;QACpE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,gEAAgE;IACxD,KAAK,CAAC,aAAa,CAAC,EAAU;QACpC,MAAM,OAAO,GAAG,qBAAqB,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,oBAAoB,CAAC,CAAC;QAChF,OAAO;YACL,IAAI,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACzD,WAAW,EAAE,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC;SACpD,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,SAAS,CAAC,EAAU,EAAE,CAAiB;QACnD,4EAA4E;QAC5E,6EAA6E;QAC7E,4EAA4E;QAC5E,0EAA0E;QAC1E,yEAAyE;QACzE,gEAAgE;QAChE,2EAA2E;QAC3E,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;QACnD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,MAAM,MAAM,CAAC,KAAK,CAChB,iCAAiC,EACjC,qFAAqF;gBACnF,kEAAkE,CACrE,CAAC;QACJ,CAAC;QACD,4EAA4E;QAC5E,2EAA2E;QAC3E,mCAAmC;QACnC,OAAO,qBAAqB,CAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAC7C,uBAAuB,CACxB,CAAC;IACJ,CAAC;CACF"}
|