@37signals/basecamp 0.12.0 → 0.14.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 +114 -17
- package/dist/.build-complete +0 -0
- package/dist/client.d.ts +53 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +132 -150
- 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 +138 -14
- package/dist/errors.js.map +1 -1
- package/dist/generated/metadata.d.ts.map +1 -1
- package/dist/generated/metadata.js +291 -141
- package/dist/generated/metadata.js.map +1 -1
- package/dist/generated/metadata.ts +291 -141
- package/dist/generated/openapi-stripped.json +5954 -3523
- package/dist/generated/path-mapping.d.ts.map +1 -1
- package/dist/generated/path-mapping.js +28 -16
- package/dist/generated/path-mapping.js.map +1 -1
- package/dist/generated/path-mapping.ts +28 -16
- package/dist/generated/schema.d.ts +4390 -2357
- package/dist/generated/services/bookmarks.d.ts +1 -1
- package/dist/generated/services/bookmarks.d.ts.map +1 -1
- package/dist/generated/services/bookmarks.ts +1 -1
- 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/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 +1 -1
- package/dist/generated/services/drafts.d.ts.map +1 -1
- package/dist/generated/services/drafts.ts +1 -1
- 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 +14 -14
- package/dist/generated/services/everything.d.ts.map +1 -1
- package/dist/generated/services/everything.ts +14 -14
- 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 +7 -4
- package/dist/generated/services/index.d.ts.map +1 -1
- package/dist/generated/services/index.js +7 -4
- package/dist/generated/services/index.js.map +1 -1
- package/dist/generated/services/index.ts +7 -4
- 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-notes.d.ts +1 -1
- package/dist/generated/services/my-notes.js +1 -1
- package/dist/generated/services/my-notes.ts +1 -1
- 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 +2 -12
- package/dist/generated/services/todos.d.ts.map +1 -1
- package/dist/generated/services/todos.js +1 -25
- package/dist/generated/services/todos.js.map +1 -1
- package/dist/generated/services/todos.ts +3 -30
- package/dist/generated/services/uploads.d.ts +41 -2
- package/dist/generated/services/uploads.d.ts.map +1 -1
- package/dist/generated/services/uploads.js +41 -1
- package/dist/generated/services/uploads.js.map +1 -1
- package/dist/generated/services/uploads.ts +71 -2
- 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/index.d.ts +16 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -8
- package/dist/index.js.map +1 -1
- package/dist/oauth/authorization-document.d.ts +201 -0
- package/dist/oauth/authorization-document.d.ts.map +1 -0
- package/dist/oauth/authorization-document.js +130 -0
- package/dist/oauth/authorization-document.js.map +1 -0
- package/dist/oauth/identity.d.ts +7 -1
- package/dist/oauth/identity.d.ts.map +1 -1
- package/dist/oauth/identity.js +8 -19
- package/dist/oauth/identity.js.map +1 -1
- package/dist/oauth/index.d.ts +1 -0
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +2 -0
- package/dist/oauth/index.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 +11 -0
- package/dist/pagination.d.ts.map +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/authorization.d.ts +22 -52
- package/dist/services/authorization.d.ts.map +1 -1
- package/dist/services/authorization.js +14 -32
- package/dist/services/authorization.js.map +1 -1
- 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 +6 -5
- package/src/generated/metadata.ts +291 -141
- package/src/generated/openapi-stripped.json +5954 -3523
- package/src/generated/path-mapping.ts +28 -16
- package/src/generated/schema.d.ts +4390 -2357
- package/src/generated/services/bookmarks.ts +1 -1
- package/src/generated/services/boosts.ts +12 -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 +1 -1
- package/src/generated/services/events.ts +6 -0
- package/src/generated/services/everything.ts +14 -14
- 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 +7 -4
- package/src/generated/services/messages.ts +3 -1
- package/src/generated/services/my-notes.ts +1 -1
- 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 +3 -30
- package/src/generated/services/uploads.ts +71 -2
- package/src/generated/services/vaults.ts +6 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response guards shared by the merge-safe composites.
|
|
3
|
+
*
|
|
4
|
+
* A merge-safe `update`/`edit` GETs a record, reads each writable field, and
|
|
5
|
+
* PUTs the **full** representation back. The endpoint is full-replace, so every
|
|
6
|
+
* value read here is written — including one the caller never mentioned. If the
|
|
7
|
+
* read step coerces or forwards a malformed value instead of refusing it, that
|
|
8
|
+
* value lands on the record.
|
|
9
|
+
*
|
|
10
|
+
* Two failure modes, the same defect wearing different clothes:
|
|
11
|
+
*
|
|
12
|
+
* - **erasure** — a falsey value dropped or coalesced away, wiping the field;
|
|
13
|
+
* - **corruption** — a non-string forwarded verbatim, writing a number,
|
|
14
|
+
* boolean, array or object where a string belongs.
|
|
15
|
+
*
|
|
16
|
+
* `?? ""` catches only `null` and `undefined`, so it rules out erasure while
|
|
17
|
+
* leaving corruption wide open — every one of `false`, `0`, `[]`, `{}`, `42`,
|
|
18
|
+
* `true`, `["x"]` and `{a:1}` rides through unchanged. Testing only for erasure
|
|
19
|
+
* is what let this class survive five review passes, so both are refused here.
|
|
20
|
+
*
|
|
21
|
+
* **The rule: a composite is safe exactly when a decoder *rejects* a
|
|
22
|
+
* wrong-typed field at runtime — not when a type merely claims one.** Go
|
|
23
|
+
* (`json.Unmarshal`), Swift (`Codable`) and Kotlin (kotlinx.serialization)
|
|
24
|
+
* genuinely refuse. TypeScript's `schema.d.ts` is erased at build time, so the
|
|
25
|
+
* type on a GET result is a compile-time claim nothing validates; structurally
|
|
26
|
+
* this sits with Python and Ruby, not with Go and Swift (#576).
|
|
27
|
+
*
|
|
28
|
+
* Todolists carries its own copy of these guards (#574). #544 flattened the
|
|
29
|
+
* shape those guards read — dropping the envelope-arm rung, not the guards —
|
|
30
|
+
* but did not unify them here. A generated validating layer (#578) is the
|
|
31
|
+
* intended end state for all of them.
|
|
32
|
+
*/
|
|
33
|
+
import { type BasecampError } from "../errors.js";
|
|
34
|
+
/**
|
|
35
|
+
* Renders a value for an error message without ever throwing.
|
|
36
|
+
*
|
|
37
|
+
* The guard's own error path must not fail while explaining a failure.
|
|
38
|
+
* `JSON.stringify` raises `TypeError` on a circular structure, and a value can
|
|
39
|
+
* carry a `toJSON` that throws — either would replace a clean `api_error` with
|
|
40
|
+
* an incidental `TypeError` and lose the diagnosis. The type name is always
|
|
41
|
+
* available; the rendering is a bonus, capped per SPEC §9 and dropped if it
|
|
42
|
+
* fails.
|
|
43
|
+
*/
|
|
44
|
+
export declare function describeValue(value: unknown): string;
|
|
45
|
+
/**
|
|
46
|
+
* Builds the malformed-response error, with the message capped per SPEC §9.
|
|
47
|
+
*
|
|
48
|
+
* `api_error`, not `usage`: the value arrived in a successful API response, so
|
|
49
|
+
* nothing the caller passed is at fault. Statusless — the transport succeeded,
|
|
50
|
+
* there is no HTTP status to attribute — and non-retryable, because
|
|
51
|
+
* re-requesting cannot repair a malformed body.
|
|
52
|
+
*/
|
|
53
|
+
export declare function malformedResponse(message: string, hint: string): BasecampError;
|
|
54
|
+
/**
|
|
55
|
+
* The response must be a JSON object before any field is read.
|
|
56
|
+
*
|
|
57
|
+
* One level up from the malformed-*field* guards: a successful GET can return a
|
|
58
|
+
* scalar, an array, or null. Reading a property off `null` throws a raw
|
|
59
|
+
* `TypeError`, and off an array or a string it silently yields `undefined`,
|
|
60
|
+
* which the field guards would then read as "genuinely empty" and write back —
|
|
61
|
+
* so the envelope needs checking before the fields.
|
|
62
|
+
*/
|
|
63
|
+
export declare function requireRecord(body: unknown, opts: {
|
|
64
|
+
record: string;
|
|
65
|
+
operation: string;
|
|
66
|
+
escape: string;
|
|
67
|
+
}): Record<string, unknown>;
|
|
68
|
+
/**
|
|
69
|
+
* Reads a writable string field, refusing to pass a malformed one through.
|
|
70
|
+
*
|
|
71
|
+
* An absent key or an explicit `null` is genuinely empty — there is nothing to
|
|
72
|
+
* preserve and `""` is what the server already holds. An actual string passes
|
|
73
|
+
* verbatim. Anything else is a malformed response and is refused **before** the
|
|
74
|
+
* PUT, naming the offending field.
|
|
75
|
+
*/
|
|
76
|
+
export declare function writableString(body: Record<string, unknown>, key: string, opts: {
|
|
77
|
+
record: string;
|
|
78
|
+
escape: string;
|
|
79
|
+
}): string;
|
|
80
|
+
/**
|
|
81
|
+
* Reads a writable string the record is *required* to carry.
|
|
82
|
+
*
|
|
83
|
+
* {@link writableString} treats an absent key or an explicit `null` as
|
|
84
|
+
* genuinely empty, which is right for an optional field — `""` is what the
|
|
85
|
+
* server already holds. It is wrong for a required one. Where the spec marks a
|
|
86
|
+
* response member `@required` and BC3 can never render it blank, an absent,
|
|
87
|
+
* null or blank value in a 2xx body is a **malformed response**, not an empty
|
|
88
|
+
* field. Coalescing it to `""` and sending that in the full-replace PUT would
|
|
89
|
+
* blank the real value on a call that never mentioned it — #576's defect
|
|
90
|
+
* exactly.
|
|
91
|
+
*
|
|
92
|
+
* Two records rely on this today and for the same reason: `Document#title` is
|
|
93
|
+
* `super.presence || "Untitled"` and `Schedule::Entry#summary` is
|
|
94
|
+
* `super.presence || "Untitled"`, so neither can come back blank from a healthy
|
|
95
|
+
* server.
|
|
96
|
+
*
|
|
97
|
+
* The wrong-type branch is delegated to {@link writableString}, so a required
|
|
98
|
+
* field and an optional one report a non-string identically.
|
|
99
|
+
*/
|
|
100
|
+
export declare function requiredWritableString(body: Record<string, unknown>, key: string, opts: {
|
|
101
|
+
record: string;
|
|
102
|
+
escape: string;
|
|
103
|
+
}): string;
|
|
104
|
+
/**
|
|
105
|
+
* Reads a writable boolean the record is *required* to carry.
|
|
106
|
+
*
|
|
107
|
+
* The boolean analogue of {@link requiredWritableString}, and it cannot be
|
|
108
|
+
* expressed with a truthiness test: the value this guard most needs to admit is
|
|
109
|
+
* `false`, which every `||`/`if (!x)` idiom would treat as missing and replace
|
|
110
|
+
* with a default. `ScheduleEntry.all_day` is `NOT NULL` with a `false` default
|
|
111
|
+
* in BC3 and every partial emits it, so absent or null is a malformed
|
|
112
|
+
* response — and defaulting it to `false` would silently convert an all-day
|
|
113
|
+
* event into a midnight-to-midnight timed one on a call that only changed the
|
|
114
|
+
* summary.
|
|
115
|
+
*
|
|
116
|
+
* `0`/`1` are refused rather than coerced, for the same reason
|
|
117
|
+
* {@link writableString} refuses `42`: JSON has a boolean type and the server
|
|
118
|
+
* uses it.
|
|
119
|
+
*/
|
|
120
|
+
export declare function requiredWritableBoolean(body: Record<string, unknown>, key: string, opts: {
|
|
121
|
+
record: string;
|
|
122
|
+
escape: string;
|
|
123
|
+
}): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Reads an *optional* writable boolean, refusing to coerce a malformed one.
|
|
126
|
+
*
|
|
127
|
+
* {@link writableString}'s boolean sibling, standing in the same relation to
|
|
128
|
+
* {@link requiredWritableBoolean} that `writableString` does to
|
|
129
|
+
* {@link requiredWritableString}: an absent key or an explicit `null` is
|
|
130
|
+
* genuinely "not set" and returns `false`, because that is what the server
|
|
131
|
+
* already holds.
|
|
132
|
+
*
|
|
133
|
+
* `ScheduleEntry.highlighted` is the case it exists for. The entry partial
|
|
134
|
+
* emits it unconditionally, but the reduced calendar partial behind
|
|
135
|
+
* `GetUpcomingSchedule` does not, and both render through the same schema — so
|
|
136
|
+
* the member is optional and absence is legitimate rather than malformed.
|
|
137
|
+
*
|
|
138
|
+
* What still cannot be tolerated is the *wrong type*: a `"yes"` or a `1` must be
|
|
139
|
+
* refused, not coerced, because a caller who assigns the seeded value straight
|
|
140
|
+
* back sends whatever it was seeded with. That branch is delegated to
|
|
141
|
+
* {@link requiredWritableBoolean}, so an optional boolean and a required one
|
|
142
|
+
* report a non-boolean identically.
|
|
143
|
+
*/
|
|
144
|
+
export declare function writableBoolean(body: Record<string, unknown>, key: string, opts: {
|
|
145
|
+
record: string;
|
|
146
|
+
escape: string;
|
|
147
|
+
}): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Reads a list of person records and projects it to their integer IDs.
|
|
150
|
+
*
|
|
151
|
+
* The analogue of {@link writableString} for the ID-list fields. The `.map()`
|
|
152
|
+
* it replaces (`(body[key] ?? []).map((p) => p.id)`) has three ways to go wrong
|
|
153
|
+
* on malformed data: a non-array has no `.map` (a raw `TypeError`), a
|
|
154
|
+
* non-object element yields `undefined`, and a non-integer `id` rides through
|
|
155
|
+
* verbatim into the full-replace PUT — the same corruption as a wrong-typed
|
|
156
|
+
* string, one level down.
|
|
157
|
+
*
|
|
158
|
+
* `Number.isInteger` is the test rather than `typeof === "number"`: `1.5` and
|
|
159
|
+
* `NaN` are numbers and neither is a person ID. Booleans fail it outright,
|
|
160
|
+
* which is what we want — JavaScript would happily coerce `true` to `1`
|
|
161
|
+
* downstream.
|
|
162
|
+
*/
|
|
163
|
+
export declare function writableIdList(body: Record<string, unknown>, key: string, opts: {
|
|
164
|
+
record: string;
|
|
165
|
+
escape: string;
|
|
166
|
+
}): number[];
|
|
167
|
+
//# sourceMappingURL=merge-safe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-safe.d.ts","sourceRoot":"","sources":["../../src/services/merge-safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAMhF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQpD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAE9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC,MAAM,CAUR;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC,MAAM,CAUR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC,OAAO,CAgBT;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC,OAAO,CAIT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC,MAAM,EAAE,CA+BV"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response guards shared by the merge-safe composites.
|
|
3
|
+
*
|
|
4
|
+
* A merge-safe `update`/`edit` GETs a record, reads each writable field, and
|
|
5
|
+
* PUTs the **full** representation back. The endpoint is full-replace, so every
|
|
6
|
+
* value read here is written — including one the caller never mentioned. If the
|
|
7
|
+
* read step coerces or forwards a malformed value instead of refusing it, that
|
|
8
|
+
* value lands on the record.
|
|
9
|
+
*
|
|
10
|
+
* Two failure modes, the same defect wearing different clothes:
|
|
11
|
+
*
|
|
12
|
+
* - **erasure** — a falsey value dropped or coalesced away, wiping the field;
|
|
13
|
+
* - **corruption** — a non-string forwarded verbatim, writing a number,
|
|
14
|
+
* boolean, array or object where a string belongs.
|
|
15
|
+
*
|
|
16
|
+
* `?? ""` catches only `null` and `undefined`, so it rules out erasure while
|
|
17
|
+
* leaving corruption wide open — every one of `false`, `0`, `[]`, `{}`, `42`,
|
|
18
|
+
* `true`, `["x"]` and `{a:1}` rides through unchanged. Testing only for erasure
|
|
19
|
+
* is what let this class survive five review passes, so both are refused here.
|
|
20
|
+
*
|
|
21
|
+
* **The rule: a composite is safe exactly when a decoder *rejects* a
|
|
22
|
+
* wrong-typed field at runtime — not when a type merely claims one.** Go
|
|
23
|
+
* (`json.Unmarshal`), Swift (`Codable`) and Kotlin (kotlinx.serialization)
|
|
24
|
+
* genuinely refuse. TypeScript's `schema.d.ts` is erased at build time, so the
|
|
25
|
+
* type on a GET result is a compile-time claim nothing validates; structurally
|
|
26
|
+
* this sits with Python and Ruby, not with Go and Swift (#576).
|
|
27
|
+
*
|
|
28
|
+
* Todolists carries its own copy of these guards (#574). #544 flattened the
|
|
29
|
+
* shape those guards read — dropping the envelope-arm rung, not the guards —
|
|
30
|
+
* but did not unify them here. A generated validating layer (#578) is the
|
|
31
|
+
* intended end state for all of them.
|
|
32
|
+
*/
|
|
33
|
+
import { Errors, truncateErrorMessage } from "../errors.js";
|
|
34
|
+
const resendHint = (escape) => "The merge-safe update/edit resend this field verbatim, so a coerced or empty value " +
|
|
35
|
+
`would overwrite the current one. Use ${escape} to write the record deliberately.`;
|
|
36
|
+
/**
|
|
37
|
+
* Renders a value for an error message without ever throwing.
|
|
38
|
+
*
|
|
39
|
+
* The guard's own error path must not fail while explaining a failure.
|
|
40
|
+
* `JSON.stringify` raises `TypeError` on a circular structure, and a value can
|
|
41
|
+
* carry a `toJSON` that throws — either would replace a clean `api_error` with
|
|
42
|
+
* an incidental `TypeError` and lose the diagnosis. The type name is always
|
|
43
|
+
* available; the rendering is a bonus, capped per SPEC §9 and dropped if it
|
|
44
|
+
* fails.
|
|
45
|
+
*/
|
|
46
|
+
export function describeValue(value) {
|
|
47
|
+
const kind = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
48
|
+
try {
|
|
49
|
+
const rendered = JSON.stringify(value);
|
|
50
|
+
return rendered === undefined ? kind : `${kind} ${truncateErrorMessage(rendered)}`;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return kind;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Builds the malformed-response error, with the message capped per SPEC §9.
|
|
58
|
+
*
|
|
59
|
+
* `api_error`, not `usage`: the value arrived in a successful API response, so
|
|
60
|
+
* nothing the caller passed is at fault. Statusless — the transport succeeded,
|
|
61
|
+
* there is no HTTP status to attribute — and non-retryable, because
|
|
62
|
+
* re-requesting cannot repair a malformed body.
|
|
63
|
+
*/
|
|
64
|
+
export function malformedResponse(message, hint) {
|
|
65
|
+
return Errors.apiError(truncateErrorMessage(message), undefined, { hint, retryable: false });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The response must be a JSON object before any field is read.
|
|
69
|
+
*
|
|
70
|
+
* One level up from the malformed-*field* guards: a successful GET can return a
|
|
71
|
+
* scalar, an array, or null. Reading a property off `null` throws a raw
|
|
72
|
+
* `TypeError`, and off an array or a string it silently yields `undefined`,
|
|
73
|
+
* which the field guards would then read as "genuinely empty" and write back —
|
|
74
|
+
* so the envelope needs checking before the fields.
|
|
75
|
+
*/
|
|
76
|
+
export function requireRecord(body, opts) {
|
|
77
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
|
78
|
+
throw malformedResponse(`${opts.operation} returned ${describeValue(body)} where a ${opts.record.toLowerCase()} object was expected`, "The merge-safe update/edit read this record's fields before rewriting them, so a " +
|
|
79
|
+
`non-object body cannot be used. Use ${opts.escape} to write the record deliberately.`);
|
|
80
|
+
}
|
|
81
|
+
return body;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Reads a writable string field, refusing to pass a malformed one through.
|
|
85
|
+
*
|
|
86
|
+
* An absent key or an explicit `null` is genuinely empty — there is nothing to
|
|
87
|
+
* preserve and `""` is what the server already holds. An actual string passes
|
|
88
|
+
* verbatim. Anything else is a malformed response and is refused **before** the
|
|
89
|
+
* PUT, naming the offending field.
|
|
90
|
+
*/
|
|
91
|
+
export function writableString(body, key, opts) {
|
|
92
|
+
const value = body[key];
|
|
93
|
+
if (value === undefined || value === null)
|
|
94
|
+
return "";
|
|
95
|
+
if (typeof value !== "string") {
|
|
96
|
+
throw malformedResponse(`${opts.record} field "${key}" is not a string: ${describeValue(value)}`, resendHint(opts.escape));
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Reads a writable string the record is *required* to carry.
|
|
102
|
+
*
|
|
103
|
+
* {@link writableString} treats an absent key or an explicit `null` as
|
|
104
|
+
* genuinely empty, which is right for an optional field — `""` is what the
|
|
105
|
+
* server already holds. It is wrong for a required one. Where the spec marks a
|
|
106
|
+
* response member `@required` and BC3 can never render it blank, an absent,
|
|
107
|
+
* null or blank value in a 2xx body is a **malformed response**, not an empty
|
|
108
|
+
* field. Coalescing it to `""` and sending that in the full-replace PUT would
|
|
109
|
+
* blank the real value on a call that never mentioned it — #576's defect
|
|
110
|
+
* exactly.
|
|
111
|
+
*
|
|
112
|
+
* Two records rely on this today and for the same reason: `Document#title` is
|
|
113
|
+
* `super.presence || "Untitled"` and `Schedule::Entry#summary` is
|
|
114
|
+
* `super.presence || "Untitled"`, so neither can come back blank from a healthy
|
|
115
|
+
* server.
|
|
116
|
+
*
|
|
117
|
+
* The wrong-type branch is delegated to {@link writableString}, so a required
|
|
118
|
+
* field and an optional one report a non-string identically.
|
|
119
|
+
*/
|
|
120
|
+
export function requiredWritableString(body, key, opts) {
|
|
121
|
+
const value = body[key];
|
|
122
|
+
if (value === undefined || value === null || (typeof value === "string" && value.trim() === "")) {
|
|
123
|
+
throw malformedResponse(`${opts.record} field "${key}" is required but the response carried ${describeValue(value)}`, `The merge-safe update/edit resend this field verbatim, so a missing or blank value would ` +
|
|
124
|
+
`blank the current one. Use ${opts.escape} to write the record deliberately.`);
|
|
125
|
+
}
|
|
126
|
+
return writableString(body, key, opts);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Reads a writable boolean the record is *required* to carry.
|
|
130
|
+
*
|
|
131
|
+
* The boolean analogue of {@link requiredWritableString}, and it cannot be
|
|
132
|
+
* expressed with a truthiness test: the value this guard most needs to admit is
|
|
133
|
+
* `false`, which every `||`/`if (!x)` idiom would treat as missing and replace
|
|
134
|
+
* with a default. `ScheduleEntry.all_day` is `NOT NULL` with a `false` default
|
|
135
|
+
* in BC3 and every partial emits it, so absent or null is a malformed
|
|
136
|
+
* response — and defaulting it to `false` would silently convert an all-day
|
|
137
|
+
* event into a midnight-to-midnight timed one on a call that only changed the
|
|
138
|
+
* summary.
|
|
139
|
+
*
|
|
140
|
+
* `0`/`1` are refused rather than coerced, for the same reason
|
|
141
|
+
* {@link writableString} refuses `42`: JSON has a boolean type and the server
|
|
142
|
+
* uses it.
|
|
143
|
+
*/
|
|
144
|
+
export function requiredWritableBoolean(body, key, opts) {
|
|
145
|
+
const value = body[key];
|
|
146
|
+
if (value === undefined || value === null) {
|
|
147
|
+
throw malformedResponse(`${opts.record} field "${key}" is required but the response carried ${describeValue(value)}`, `The merge-safe update/edit resend this field verbatim, so a missing value would replace ` +
|
|
148
|
+
`the current one with a default. Use ${opts.escape} to write the record deliberately.`);
|
|
149
|
+
}
|
|
150
|
+
if (typeof value !== "boolean") {
|
|
151
|
+
throw malformedResponse(`${opts.record} field "${key}" is not a boolean: ${describeValue(value)}`, resendHint(opts.escape));
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Reads an *optional* writable boolean, refusing to coerce a malformed one.
|
|
157
|
+
*
|
|
158
|
+
* {@link writableString}'s boolean sibling, standing in the same relation to
|
|
159
|
+
* {@link requiredWritableBoolean} that `writableString` does to
|
|
160
|
+
* {@link requiredWritableString}: an absent key or an explicit `null` is
|
|
161
|
+
* genuinely "not set" and returns `false`, because that is what the server
|
|
162
|
+
* already holds.
|
|
163
|
+
*
|
|
164
|
+
* `ScheduleEntry.highlighted` is the case it exists for. The entry partial
|
|
165
|
+
* emits it unconditionally, but the reduced calendar partial behind
|
|
166
|
+
* `GetUpcomingSchedule` does not, and both render through the same schema — so
|
|
167
|
+
* the member is optional and absence is legitimate rather than malformed.
|
|
168
|
+
*
|
|
169
|
+
* What still cannot be tolerated is the *wrong type*: a `"yes"` or a `1` must be
|
|
170
|
+
* refused, not coerced, because a caller who assigns the seeded value straight
|
|
171
|
+
* back sends whatever it was seeded with. That branch is delegated to
|
|
172
|
+
* {@link requiredWritableBoolean}, so an optional boolean and a required one
|
|
173
|
+
* report a non-boolean identically.
|
|
174
|
+
*/
|
|
175
|
+
export function writableBoolean(body, key, opts) {
|
|
176
|
+
const value = body[key];
|
|
177
|
+
if (value === undefined || value === null)
|
|
178
|
+
return false;
|
|
179
|
+
return requiredWritableBoolean(body, key, opts);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Reads a list of person records and projects it to their integer IDs.
|
|
183
|
+
*
|
|
184
|
+
* The analogue of {@link writableString} for the ID-list fields. The `.map()`
|
|
185
|
+
* it replaces (`(body[key] ?? []).map((p) => p.id)`) has three ways to go wrong
|
|
186
|
+
* on malformed data: a non-array has no `.map` (a raw `TypeError`), a
|
|
187
|
+
* non-object element yields `undefined`, and a non-integer `id` rides through
|
|
188
|
+
* verbatim into the full-replace PUT — the same corruption as a wrong-typed
|
|
189
|
+
* string, one level down.
|
|
190
|
+
*
|
|
191
|
+
* `Number.isInteger` is the test rather than `typeof === "number"`: `1.5` and
|
|
192
|
+
* `NaN` are numbers and neither is a person ID. Booleans fail it outright,
|
|
193
|
+
* which is what we want — JavaScript would happily coerce `true` to `1`
|
|
194
|
+
* downstream.
|
|
195
|
+
*/
|
|
196
|
+
export function writableIdList(body, key, opts) {
|
|
197
|
+
const value = body[key];
|
|
198
|
+
if (value === undefined || value === null)
|
|
199
|
+
return [];
|
|
200
|
+
if (!Array.isArray(value)) {
|
|
201
|
+
throw malformedResponse(`${opts.record} field "${key}" is not an array: ${describeValue(value)}`, resendHint(opts.escape));
|
|
202
|
+
}
|
|
203
|
+
return value.map((element, index) => {
|
|
204
|
+
if (typeof element !== "object" || element === null || Array.isArray(element)) {
|
|
205
|
+
throw malformedResponse(`${opts.record} field "${key}"[${index}] is not an object: ${describeValue(element)}`, resendHint(opts.escape));
|
|
206
|
+
}
|
|
207
|
+
const id = element["id"];
|
|
208
|
+
if (id === undefined || id === null) {
|
|
209
|
+
throw malformedResponse(`${opts.record} field "${key}"[${index}] has no "id"`, resendHint(opts.escape));
|
|
210
|
+
}
|
|
211
|
+
if (typeof id !== "number" || !Number.isInteger(id)) {
|
|
212
|
+
throw malformedResponse(`${opts.record} field "${key}"[${index}].id is not an integer: ${describeValue(id)}`, resendHint(opts.escape));
|
|
213
|
+
}
|
|
214
|
+
return id;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=merge-safe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-safe.js","sourceRoot":"","sources":["../../src/services/merge-safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAsB,MAAM,cAAc,CAAC;AAEhF,MAAM,UAAU,GAAG,CAAC,MAAc,EAAU,EAAE,CAC5C,qFAAqF;IACrF,wCAAwC,MAAM,oCAAoC,CAAC;AAErF;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,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;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,IAAY;IAC7D,OAAO,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAa,EACb,IAA2D;IAE3D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,SAAS,aAAa,aAAa,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,sBAAsB,EAC5G,mFAAmF;YACjF,uCAAuC,IAAI,CAAC,MAAM,oCAAoC,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,IAA+B,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,IAA6B,EAC7B,GAAW,EACX,IAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,sBAAsB,aAAa,CAAC,KAAK,CAAC,EAAE,EACxE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAA6B,EAC7B,GAAW,EACX,IAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAChG,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,0CAA0C,aAAa,CAAC,KAAK,CAAC,EAAE,EAC5F,2FAA2F;YACzF,8BAA8B,IAAI,CAAC,MAAM,oCAAoC,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAA6B,EAC7B,GAAW,EACX,IAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,0CAA0C,aAAa,CAAC,KAAK,CAAC,EAAE,EAC5F,0FAA0F;YACxF,uCAAuC,IAAI,CAAC,MAAM,oCAAoC,CACzF,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,uBAAuB,aAAa,CAAC,KAAK,CAAC,EAAE,EACzE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,eAAe,CAC7B,IAA6B,EAC7B,GAAW,EACX,IAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,uBAAuB,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAC5B,IAA6B,EAC7B,GAAW,EACX,IAAwC;IAExC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,sBAAsB,aAAa,CAAC,KAAK,CAAC,EAAE,EACxE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,OAAgB,EAAE,KAAa,EAAE,EAAE;QACnD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9E,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,KAAK,KAAK,uBAAuB,aAAa,CAAC,OAAO,CAAC,EAAE,EACrF,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAC;QACJ,CAAC;QACD,MAAM,EAAE,GAAI,OAAmC,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACpC,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,KAAK,KAAK,eAAe,EACrD,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACpD,MAAM,iBAAiB,CACrB,GAAG,IAAI,CAAC,MAAM,WAAW,GAAG,KAAK,KAAK,2BAA2B,aAAa,CAAC,EAAE,CAAC,EAAE,EACpF,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { SchedulesService as GeneratedSchedulesService } from "../generated/services/schedules.js";
|
|
2
|
+
import type { ScheduleEntry } from "../generated/services/schedules.js";
|
|
3
|
+
/**
|
|
4
|
+
* Request parameters for `updateEntry`.
|
|
5
|
+
*
|
|
6
|
+
* **Addressedness is property presence, not value.** Every field is optional;
|
|
7
|
+
* a key you leave off the object is not addressed, and a key you set is —
|
|
8
|
+
* including `{ highlighted: false }`, `{ url: "" }` and
|
|
9
|
+
* `{ participantIds: [] }`, each of which is a deliberate clear and reaches the
|
|
10
|
+
* wire. That distinction is the whole point of the carve-out contract below,
|
|
11
|
+
* so it is tested with `Object.prototype.hasOwnProperty.call`, never with a
|
|
12
|
+
* truthiness test.
|
|
13
|
+
*
|
|
14
|
+
* The fields split in two, and the split is not cosmetic:
|
|
15
|
+
*
|
|
16
|
+
* - **full state** — `summary`, `startsAt`, `endsAt`, `description`, `allDay`.
|
|
17
|
+
* Always sent, seeded from the read-back when you do not address them,
|
|
18
|
+
* because BC3 rebuilds the entry from the submitted params and clears every
|
|
19
|
+
* writable field the body omits.
|
|
20
|
+
* - **addressed-only** — `participantIds`, `url`, `highlighted`, `notify`.
|
|
21
|
+
* Sent only when you address them, and never seeded from the read-back. BC3
|
|
22
|
+
* preserves the first three on omission (`PRESERVED_ON_OMISSION`), so
|
|
23
|
+
* resending them is redundant at best and wrong if the read raced a
|
|
24
|
+
* concurrent change. `notify` is a directive rather than state: sending it
|
|
25
|
+
* makes BC3 recompute a drafted entry's subscriber list.
|
|
26
|
+
*/
|
|
27
|
+
export interface UpdateScheduleEntryRequest {
|
|
28
|
+
/** Summary text. Omit to leave unchanged; `""` clears it. */
|
|
29
|
+
summary?: string;
|
|
30
|
+
/**
|
|
31
|
+
* When the entry starts — a bare date (`"2026-06-05"`) for an all-day entry,
|
|
32
|
+
* a timestamp otherwise. Omit to leave unchanged.
|
|
33
|
+
*/
|
|
34
|
+
startsAt?: string;
|
|
35
|
+
/** When the entry ends, in the same spelling as `startsAt`. Omit to leave unchanged. */
|
|
36
|
+
endsAt?: string;
|
|
37
|
+
/** Rich text description (HTML). Omit to leave unchanged; `""` clears it. */
|
|
38
|
+
description?: string;
|
|
39
|
+
/** Whether the entry occupies whole days. Omit to leave unchanged. */
|
|
40
|
+
allDay?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Replaces the entry's participants. Omit to leave them alone — BC3 preserves
|
|
43
|
+
* them when the request does not address them — or pass `[]` to remove
|
|
44
|
+
* everyone.
|
|
45
|
+
*
|
|
46
|
+
* Never resent on your behalf: the read-back's `participants` are people the
|
|
47
|
+
* *reader* can see, and echoing them would rewrite the list from a stale view.
|
|
48
|
+
*/
|
|
49
|
+
participantIds?: number[];
|
|
50
|
+
/**
|
|
51
|
+
* The entry's join link — a video-call URL or similar. Omit to leave it
|
|
52
|
+
* alone; `""` clears it.
|
|
53
|
+
*
|
|
54
|
+
* Read back as `join_url`, never as `url`: an entry's `url` is its own
|
|
55
|
+
* Basecamp API URL. The composite therefore never seeds this from the
|
|
56
|
+
* response, which would write the API URL into the join link.
|
|
57
|
+
*/
|
|
58
|
+
url?: string;
|
|
59
|
+
/** Whether the entry is highlighted on the schedule. Omit to leave it alone; `false` removes it. */
|
|
60
|
+
highlighted?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Whether to notify the relevant people. A directive, not state: it is sent
|
|
63
|
+
* only when you address it, and it is never read back.
|
|
64
|
+
*/
|
|
65
|
+
notify?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A schedule entry's full writable state, handed to the `editEntry` callback.
|
|
69
|
+
*
|
|
70
|
+
* The five full-state members are seeded from the read-back and **always** PUT
|
|
71
|
+
* back, so clearing one means setting it empty (`""`) — there is no third
|
|
72
|
+
* state.
|
|
73
|
+
*
|
|
74
|
+
* The four carve-outs behave differently and deliberately: they are seeded for
|
|
75
|
+
* *reading* (so you can inspect the current join link, highlight and
|
|
76
|
+
* participants before deciding), but they reach the wire **only if you assign
|
|
77
|
+
* to them**. Assignment is what marks them, not the value you assign — setting
|
|
78
|
+
* `e.url = e.url` sends the join link, because intent is not recoverable from a
|
|
79
|
+
* value comparison.
|
|
80
|
+
*/
|
|
81
|
+
export interface ScheduleEntryFields {
|
|
82
|
+
/** Summary text. Set `""` to clear — the entry then reads back as "Untitled". */
|
|
83
|
+
summary: string;
|
|
84
|
+
/** Start date or timestamp, exactly as the server spelled it. */
|
|
85
|
+
startsAt: string;
|
|
86
|
+
/** End date or timestamp, exactly as the server spelled it. */
|
|
87
|
+
endsAt: string;
|
|
88
|
+
/** Rich text description (HTML). Set `""` to clear. */
|
|
89
|
+
description: string;
|
|
90
|
+
/** Whether the entry occupies whole days. */
|
|
91
|
+
allDay: boolean;
|
|
92
|
+
/** Current participants, by person ID. Assign to replace them; `[]` removes everyone. */
|
|
93
|
+
participantIds: number[];
|
|
94
|
+
/** The current join link (the response's `join_url`), or `""`. Assign to change it. */
|
|
95
|
+
url: string;
|
|
96
|
+
/** Whether the entry is currently highlighted. Assign to change it. */
|
|
97
|
+
highlighted: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Whether to notify the relevant people. Write-only — the API reports no such
|
|
100
|
+
* field, so this reads back `undefined` until you assign to it.
|
|
101
|
+
*/
|
|
102
|
+
notify?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* SchedulesService with merge-safe `updateEntry` and read-modify-write
|
|
106
|
+
* `editEntry` on top of the generated surface (`getEntry`, `replaceEntry`,
|
|
107
|
+
* `createEntry`, `listEntries`, ...).
|
|
108
|
+
*
|
|
109
|
+
* `PUT /{accountId}/schedule_entries/{entryId}` is a full replace:
|
|
110
|
+
* `Schedules::EntriesController#update` rebuilds the recordable from the
|
|
111
|
+
* submitted params, so a sparse PUT that omits `description` erases it and one
|
|
112
|
+
* that omits `all_day` turns an all-day event into a midnight-to-midnight timed
|
|
113
|
+
* one. Neither is a 422 — both are a `200` that quietly clears, which is what
|
|
114
|
+
* these composites exist to prevent.
|
|
115
|
+
*
|
|
116
|
+
* Three writable fields are exempt. BC3 seeds `participant_ids`, `url` and
|
|
117
|
+
* `highlighted` from the existing recordable when the request does not address
|
|
118
|
+
* them, so the SDK's job for those is the opposite one: keep them **off** the
|
|
119
|
+
* wire unless the caller asked for them.
|
|
120
|
+
*
|
|
121
|
+
* Both composites compose the public `getEntry` and `replaceEntry`, so hooks
|
|
122
|
+
* observe the two wire operations under their native identities
|
|
123
|
+
* (`GetScheduleEntry` then `ReplaceScheduleEntry`) rather than a synthetic
|
|
124
|
+
* composite.
|
|
125
|
+
*
|
|
126
|
+
* **Non-recurring entries only.** `ensure_non_recurring_event` 302-redirects
|
|
127
|
+
* both `show` and `update` for a recurring entry, and the SDK follows neither:
|
|
128
|
+
* the GET surfaces as an unexpected body and the PUT as a redirect. Recurrence
|
|
129
|
+
* itself (`recurrence_schedule`, `recurs_until`, `time_zone_name`) is unmodeled
|
|
130
|
+
* — BC3 forces all three to nil for a non-recurring entry.
|
|
131
|
+
*/
|
|
132
|
+
export declare class SchedulesService extends GeneratedSchedulesService {
|
|
133
|
+
/**
|
|
134
|
+
* Sets the given fields on a schedule entry and preserves everything else:
|
|
135
|
+
* GETs the current entry, overlays the fields you addressed, and PUTs the
|
|
136
|
+
* full representation back.
|
|
137
|
+
*
|
|
138
|
+
* A key you leave off the request is not addressed. For the five full-state
|
|
139
|
+
* fields that means "keep what the server has"; for the four carve-outs it
|
|
140
|
+
* means the key never appears in the body, so BC3's own preservation applies.
|
|
141
|
+
* An explicitly-passed `""`, `false` or `[]` is an address and clears.
|
|
142
|
+
*
|
|
143
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
144
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
145
|
+
* for the full-state fields. The window is one round-trip. Use `replaceEntry`
|
|
146
|
+
* to overwrite deliberately.
|
|
147
|
+
*
|
|
148
|
+
* @param entryId - The entry ID
|
|
149
|
+
* @param req - Fields to set; unaddressed fields are preserved
|
|
150
|
+
* @returns The updated ScheduleEntry
|
|
151
|
+
* @throws {BasecampError} If the request fails, or if the read-back is malformed
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* // Rename without erasing the description, times or all-day flag.
|
|
156
|
+
* await client.schedules.updateEntry(123, { summary: "Team Meeting & Kickoff" });
|
|
157
|
+
*
|
|
158
|
+
* // Drop the join link and the highlight; participants are left alone.
|
|
159
|
+
* await client.schedules.updateEntry(123, { url: "", highlighted: false });
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
updateEntry(entryId: number, req: UpdateScheduleEntryRequest): Promise<ScheduleEntry>;
|
|
163
|
+
/**
|
|
164
|
+
* Applies a read-modify-write callback to a schedule entry: GETs the current
|
|
165
|
+
* entry, hands the callback its writable state, and PUTs the whole thing
|
|
166
|
+
* back. If the callback throws (or rejects), the edit aborts and nothing is
|
|
167
|
+
* written.
|
|
168
|
+
*
|
|
169
|
+
* The five full-state fields always ride back, so clearing one means setting
|
|
170
|
+
* it empty and an untouched one keeps its current value. The four carve-outs
|
|
171
|
+
* (`participantIds`, `url`, `highlighted`, `notify`) are seeded so you can
|
|
172
|
+
* read them, but they reach the wire only if the callback **assigns** to
|
|
173
|
+
* them. Assignment is tracked by invocation, not by comparing values:
|
|
174
|
+
* `e.url = e.url` is a write, because a caller who reassigns the value the
|
|
175
|
+
* read returned still means "write this", and no diff can recover that
|
|
176
|
+
* intent.
|
|
177
|
+
*
|
|
178
|
+
* Not atomic: there is no conditional-update signal on this endpoint, so a
|
|
179
|
+
* concurrent write between the GET and PUT is overwritten — last write wins
|
|
180
|
+
* for the full-state fields. The window is one round-trip. Use `replaceEntry`
|
|
181
|
+
* to overwrite deliberately.
|
|
182
|
+
*
|
|
183
|
+
* @param entryId - The entry ID
|
|
184
|
+
* @param fn - Callback that mutates the entry's writable fields in place
|
|
185
|
+
* @returns The updated ScheduleEntry
|
|
186
|
+
* @throws {BasecampError} If the request fails, or if the read-back is malformed
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* await client.schedules.editEntry(123, (e) => {
|
|
191
|
+
* e.summary = `🚨 ${e.summary}`;
|
|
192
|
+
* e.description = ""; // clearing = setting empty on a full object
|
|
193
|
+
* // participants, join link and highlight are untouched, so they stay off
|
|
194
|
+
* // the wire and BC3 keeps them.
|
|
195
|
+
* });
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
editEntry(entryId: number, fn: (e: ScheduleEntryFields) => void | Promise<void>): Promise<ScheduleEntry>;
|
|
199
|
+
/**
|
|
200
|
+
* Fetches the entry and derives the full-state half of its writable state.
|
|
201
|
+
*
|
|
202
|
+
* Every value here is resent in the full-replace PUT, so every value is
|
|
203
|
+
* validated before it is read. `?? ""` coalesces only `null` and `undefined`,
|
|
204
|
+
* leaving corruption wide open: `false`, `0`, `[]`, `{}`, `42`, `true` and
|
|
205
|
+
* friends would all be forwarded **verbatim** and written to the entry on a
|
|
206
|
+
* call that never mentioned the field. Nothing below this rejects them —
|
|
207
|
+
* `schema.d.ts` is erased at build time, so `ScheduleEntry` is a compile-time
|
|
208
|
+
* claim about runtime data, which puts this composite with Python and Ruby
|
|
209
|
+
* rather than with Go and Swift. See `merge-safe.ts` and #576.
|
|
210
|
+
*
|
|
211
|
+
* The guards differ per field because the spec models the fields
|
|
212
|
+
* differently:
|
|
213
|
+
*
|
|
214
|
+
* - `summary` is `@required` and `Schedule::Entry#summary` is
|
|
215
|
+
* `super.presence || "Untitled"`, so blank is impossible from a healthy
|
|
216
|
+
* server and absent/null/blank is malformed.
|
|
217
|
+
* - `starts_at`/`ends_at` are `@required` and `NOT NULL`. The value is a bare
|
|
218
|
+
* date for an all-day entry and a timestamp otherwise; it round-trips
|
|
219
|
+
* verbatim rather than through a `Date`, which would normalise the two
|
|
220
|
+
* spellings into one and rewrite the entry's shape.
|
|
221
|
+
* - `all_day` is `@required`, `NOT NULL DEFAULT false`. It needs the boolean
|
|
222
|
+
* guard specifically because a truthiness test would read the legitimate
|
|
223
|
+
* `false` as missing — and defaulting a missing value to `false` converts
|
|
224
|
+
* an all-day event into a midnight-to-midnight timed one.
|
|
225
|
+
* - `description` is optional and nullable both ways, so absent or null is a
|
|
226
|
+
* genuinely empty description.
|
|
227
|
+
*/
|
|
228
|
+
private currentState;
|
|
229
|
+
/**
|
|
230
|
+
* The full-state fields plus the carve-out seeds the `editEntry` callback can
|
|
231
|
+
* read.
|
|
232
|
+
*
|
|
233
|
+
* `url` is seeded from the response's **`join_url`**, not its `url`: the
|
|
234
|
+
* latter is the entry's own Basecamp API URL, written by
|
|
235
|
+
* `recordings/_recording` before the entry partial renders. Echoing it into
|
|
236
|
+
* the request's `url` would write the API URL into the join link.
|
|
237
|
+
*
|
|
238
|
+
* `updateEntry` deliberately does not come through here. It never seeds a
|
|
239
|
+
* carve-out, so validating `participants` for it would refuse a write that
|
|
240
|
+
* does not depend on them.
|
|
241
|
+
*/
|
|
242
|
+
private currentFields;
|
|
243
|
+
/**
|
|
244
|
+
* GETs the entry and checks the envelope before any field is read.
|
|
245
|
+
*
|
|
246
|
+
* A successful GET can still return a scalar, an array or null; reading a
|
|
247
|
+
* property off `null` throws a raw `TypeError` instead of the documented
|
|
248
|
+
* statusless `api_error`, and reading one off an array or string silently
|
|
249
|
+
* yields `undefined`, which the field guards would then take at face value.
|
|
250
|
+
*/
|
|
251
|
+
private readEntry;
|
|
252
|
+
/**
|
|
253
|
+
* PUTs the full writable state via `replaceEntry`, plus whichever carve-outs
|
|
254
|
+
* the caller addressed.
|
|
255
|
+
*
|
|
256
|
+
* The five full-state fields are always named, empties included: on a
|
|
257
|
+
* full-replace endpoint `""` is how a clear is expressed — never JSON null
|
|
258
|
+
* (SPEC §18 body compaction), and never by omission, which would leave the
|
|
259
|
+
* field to BC3's clear-by-default and read as an accident rather than an
|
|
260
|
+
* intent. The carve-outs are the mirror image: an unaddressed one is absent
|
|
261
|
+
* from `carveOuts`, so the generated request builder leaves it `undefined`
|
|
262
|
+
* and it never reaches the wire.
|
|
263
|
+
*/
|
|
264
|
+
private putEntry;
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=schedules-extensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedules-extensions.d.ts","sourceRoot":"","sources":["../../src/services/schedules-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,IAAI,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,KAAK,EAEV,aAAa,EACd,MAAM,oCAAoC,CAAC;AAuB5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,0BAA0B;IACzC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oGAAoG;IACpG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,yFAAyF;IACzF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,gBAAiB,SAAQ,yBAAyB;IAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,0BAA0B,GAAG,OAAO,CAAC,aAAa,CAAC;IAgC3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACG,SAAS,CACb,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,CAAC,CAAC,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACnD,OAAO,CAAC,aAAa,CAAC;IA0BzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;YACW,YAAY;IAW1B;;;;;;;;;;;;OAYG;YACW,aAAa;IAc3B;;;;;;;OAOG;YACW,SAAS;IAQvB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ;CAcjB"}
|