allsquare 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +234 -0
  4. package/dist/api/client.d.ts +46 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +162 -0
  7. package/dist/api/client.js.map +1 -0
  8. package/dist/api/contract.d.ts +549 -0
  9. package/dist/api/contract.d.ts.map +1 -0
  10. package/dist/api/contract.js +49 -0
  11. package/dist/api/contract.js.map +1 -0
  12. package/dist/api/model.d.ts +627 -0
  13. package/dist/api/model.d.ts.map +1 -0
  14. package/dist/api/model.js +378 -0
  15. package/dist/api/model.js.map +1 -0
  16. package/dist/auth/auth-service.d.ts +34 -0
  17. package/dist/auth/auth-service.d.ts.map +1 -0
  18. package/dist/auth/auth-service.js +221 -0
  19. package/dist/auth/auth-service.js.map +1 -0
  20. package/dist/auth/browser.d.ts +12 -0
  21. package/dist/auth/browser.d.ts.map +1 -0
  22. package/dist/auth/browser.js +50 -0
  23. package/dist/auth/browser.js.map +1 -0
  24. package/dist/auth/credential-store.d.ts +28 -0
  25. package/dist/auth/credential-store.d.ts.map +1 -0
  26. package/dist/auth/credential-store.js +75 -0
  27. package/dist/auth/credential-store.js.map +1 -0
  28. package/dist/auth/installation-store.d.ts +14 -0
  29. package/dist/auth/installation-store.d.ts.map +1 -0
  30. package/dist/auth/installation-store.js +122 -0
  31. package/dist/auth/installation-store.js.map +1 -0
  32. package/dist/auth/model.d.ts +54 -0
  33. package/dist/auth/model.d.ts.map +1 -0
  34. package/dist/auth/model.js +24 -0
  35. package/dist/auth/model.js.map +1 -0
  36. package/dist/auth/oauth-callback.d.ts +17 -0
  37. package/dist/auth/oauth-callback.d.ts.map +1 -0
  38. package/dist/auth/oauth-callback.js +94 -0
  39. package/dist/auth/oauth-callback.js.map +1 -0
  40. package/dist/auth/oauth-client.d.ts +43 -0
  41. package/dist/auth/oauth-client.d.ts.map +1 -0
  42. package/dist/auth/oauth-client.js +191 -0
  43. package/dist/auth/oauth-client.js.map +1 -0
  44. package/dist/auth/pkce.d.ts +9 -0
  45. package/dist/auth/pkce.d.ts.map +1 -0
  46. package/dist/auth/pkce.js +12 -0
  47. package/dist/auth/pkce.js.map +1 -0
  48. package/dist/command.d.ts +3 -0
  49. package/dist/command.d.ts.map +1 -0
  50. package/dist/command.js +24 -0
  51. package/dist/command.js.map +1 -0
  52. package/dist/commands/agent-guide.d.ts +5 -0
  53. package/dist/commands/agent-guide.d.ts.map +1 -0
  54. package/dist/commands/agent-guide.js +226 -0
  55. package/dist/commands/agent-guide.js.map +1 -0
  56. package/dist/commands/auth.d.ts +5 -0
  57. package/dist/commands/auth.d.ts.map +1 -0
  58. package/dist/commands/auth.js +68 -0
  59. package/dist/commands/auth.js.map +1 -0
  60. package/dist/commands/balances.d.ts +5 -0
  61. package/dist/commands/balances.d.ts.map +1 -0
  62. package/dist/commands/balances.js +106 -0
  63. package/dist/commands/balances.js.map +1 -0
  64. package/dist/commands/config.d.ts +4 -0
  65. package/dist/commands/config.d.ts.map +1 -0
  66. package/dist/commands/config.js +16 -0
  67. package/dist/commands/config.js.map +1 -0
  68. package/dist/commands/expense-input.d.ts +17 -0
  69. package/dist/commands/expense-input.d.ts.map +1 -0
  70. package/dist/commands/expense-input.js +78 -0
  71. package/dist/commands/expense-input.js.map +1 -0
  72. package/dist/commands/expenses.d.ts +5 -0
  73. package/dist/commands/expenses.d.ts.map +1 -0
  74. package/dist/commands/expenses.js +419 -0
  75. package/dist/commands/expenses.js.map +1 -0
  76. package/dist/commands/groups.d.ts +5 -0
  77. package/dist/commands/groups.d.ts.map +1 -0
  78. package/dist/commands/groups.js +149 -0
  79. package/dist/commands/groups.js.map +1 -0
  80. package/dist/commands/recurring-expenses.d.ts +5 -0
  81. package/dist/commands/recurring-expenses.d.ts.map +1 -0
  82. package/dist/commands/recurring-expenses.js +416 -0
  83. package/dist/commands/recurring-expenses.js.map +1 -0
  84. package/dist/commands/settlements.d.ts +5 -0
  85. package/dist/commands/settlements.d.ts.map +1 -0
  86. package/dist/commands/settlements.js +225 -0
  87. package/dist/commands/settlements.js.map +1 -0
  88. package/dist/commands/shared.d.ts +6 -0
  89. package/dist/commands/shared.d.ts.map +1 -0
  90. package/dist/commands/shared.js +35 -0
  91. package/dist/commands/shared.js.map +1 -0
  92. package/dist/commands/spend-summary.d.ts +5 -0
  93. package/dist/commands/spend-summary.d.ts.map +1 -0
  94. package/dist/commands/spend-summary.js +107 -0
  95. package/dist/commands/spend-summary.js.map +1 -0
  96. package/dist/commands/update.d.ts +9 -0
  97. package/dist/commands/update.d.ts.map +1 -0
  98. package/dist/commands/update.js +78 -0
  99. package/dist/commands/update.js.map +1 -0
  100. package/dist/config.d.ts +14 -0
  101. package/dist/config.d.ts.map +1 -0
  102. package/dist/config.js +52 -0
  103. package/dist/config.js.map +1 -0
  104. package/dist/errors.d.ts +87 -0
  105. package/dist/errors.d.ts.map +1 -0
  106. package/dist/errors.js +183 -0
  107. package/dist/errors.js.map +1 -0
  108. package/dist/index.d.ts +3 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +43 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/interaction.d.ts +16 -0
  113. package/dist/interaction.d.ts.map +1 -0
  114. package/dist/interaction.js +14 -0
  115. package/dist/interaction.js.map +1 -0
  116. package/dist/layers.d.ts +13 -0
  117. package/dist/layers.d.ts.map +1 -0
  118. package/dist/layers.js +17 -0
  119. package/dist/layers.js.map +1 -0
  120. package/dist/output.d.ts +31 -0
  121. package/dist/output.d.ts.map +1 -0
  122. package/dist/output.js +26 -0
  123. package/dist/output.js.map +1 -0
  124. package/dist/update/update-service.d.ts +41 -0
  125. package/dist/update/update-service.d.ts.map +1 -0
  126. package/dist/update/update-service.js +231 -0
  127. package/dist/update/update-service.js.map +1 -0
  128. package/dist/version.d.ts +3 -0
  129. package/dist/version.d.ts.map +1 -0
  130. package/dist/version.js +3 -0
  131. package/dist/version.js.map +1 -0
  132. package/package.json +41 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1
4
+
5
+ First public beta of the AllSquare CLI.
6
+
7
+ - Groups, expenses, recurring expenses, balances, settlements, and spend
8
+ summaries over the AllSquare `/api/v1` API
9
+ - OAuth login with PKCE and OS keychain storage; read and write access by
10
+ default, `--read-only` available
11
+ - Stable JSON output, error codes with a `retryable` flag, and distinct exit
12
+ codes for agents
13
+ - Safe retries with `--idempotency-key`, including replayed results after a
14
+ lost response
15
+ - `allsquare agent-guide` for AI agents; guarded self-update with
16
+ `allsquare update`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Swann&Co. LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # AllSquare CLI
2
+
3
+ Agent-friendly command-line access to AllSquare through the public `/api/v1`
4
+ HTTP API.
5
+
6
+ ## Status
7
+
8
+ **Beta.** Commands, the JSON envelope, error codes, and exit codes are intended
9
+ to stay compatible; new fields and commands will be added. Breaking changes, if
10
+ needed, will come in a minor version with release notes.
11
+
12
+ ## Requirements
13
+
14
+ - Node.js 22 or newer
15
+ - An AllSquare account
16
+
17
+ ## Install
18
+
19
+ ```sh
20
+ npm install --global allsquare
21
+ allsquare auth login
22
+ ```
23
+
24
+ `allsquare update` upgrades a global npm, pnpm, Yarn, or Bun installation.
25
+
26
+ ## Current commands
27
+
28
+ ```sh
29
+ allsquare --help
30
+ allsquare --version
31
+ allsquare agent-guide [--json]
32
+ allsquare update --check [--json]
33
+ allsquare update [--yes] [--json]
34
+ allsquare auth login
35
+ allsquare auth login --read-only
36
+ allsquare auth login --no-browser
37
+ allsquare auth status
38
+ allsquare auth refresh
39
+ allsquare auth logout
40
+ allsquare groups list
41
+ allsquare groups members <group-id>
42
+ allsquare groups create --name TEXT --currency CODE [--member-email EMAIL]
43
+ allsquare groups add-member <group-id> --email EMAIL
44
+ allsquare balances list [--currency USD]
45
+ allsquare balances show <group-id> [--currency USD]
46
+ allsquare expenses list [--group-id ID] [--query TEXT] [--from YYYY-MM-DD] [--to YYYY-MM-DD]
47
+ allsquare expenses list [--paid-by-id MEMBER_ID] [--is-recurring true|false] [--is-settled true|false]
48
+ allsquare expenses list [--limit 20] [--cursor CURSOR]
49
+ allsquare expenses get <expense-id>
50
+ allsquare expenses create <group-id> --description TEXT --amount DECIMAL --currency CODE
51
+ allsquare expenses create <group-id> [--paid-by-id ID] [--split-with ID] [--split-type amount|percentage|exact]
52
+ allsquare expenses create <group-id> [--percentage-split ID=PERCENT] [--exact-split ID=AMOUNT] [--date YYYY-MM-DD]
53
+ allsquare expenses update <expense-id> [--description TEXT] [--amount DECIMAL] [--currency CODE]
54
+ allsquare expenses update <expense-id> [--paid-by-id ID] [--split-with ID] [--split-type amount|percentage|exact]
55
+ allsquare expenses delete <expense-id> [--yes]
56
+ allsquare recurring-expenses list [--group-id ID]
57
+ allsquare recurring-expenses get <recurring-expense-id>
58
+ allsquare recurring-expenses create <group-id> --description TEXT --amount DECIMAL --currency CODE --frequency FREQUENCY
59
+ allsquare recurring-expenses create <group-id> [--start-date YYYY-MM-DD] [--paid-by-id ID] [--split-with ID]
60
+ allsquare recurring-expenses update <recurring-expense-id> [--description TEXT] [--amount DECIMAL] [--frequency FREQUENCY]
61
+ allsquare recurring-expenses update <recurring-expense-id> [--paid-by-id ID] [--split-with ID] [--split-type amount|percentage|exact]
62
+ allsquare recurring-expenses pause <recurring-expense-id>
63
+ allsquare recurring-expenses resume <recurring-expense-id>
64
+ allsquare recurring-expenses delete <recurring-expense-id> [--yes]
65
+ allsquare settlements list [--group-id ID] [--limit 20]
66
+ allsquare settlements preview <group-id> --expense-id ID [--expense-id ID] [--currency USD]
67
+ allsquare settlements settle <group-id> --expense-id ID [--expense-id ID] [--currency USD] [--yes]
68
+ allsquare spend-summary show [--group-id ID] [--from YYYY-MM-DD] [--to YYYY-MM-DD]
69
+ allsquare spend-summary show [--query TEXT] [--currency USD]
70
+ allsquare config show
71
+ allsquare config show --json
72
+ ```
73
+
74
+ Add `--json` to any leaf command for machine-readable output. Login opens the
75
+ system browser by default and completes through an ephemeral loopback callback.
76
+ Use `--no-browser` to print the authorization URL without opening it.
77
+
78
+ Each CLI installation registers its own OAuth public client. Authorization uses
79
+ PKCE, access tokens refresh automatically shortly before expiry, and refreshes
80
+ are serialized across concurrent processes so agents cannot accidentally reuse
81
+ a rotated refresh token. Plain `auth login` requests the CLI's normal read and
82
+ write access in one consent flow. Use `auth login --read-only` to request only
83
+ read access; write commands then fail with `AUTH_REQUIRED` rather than asking
84
+ for more access. If a write command runs with no session at all from an
85
+ interactive terminal, the CLI opens consent, waits for approval, then continues
86
+ that same command. With `--json` or without a terminal it fails immediately with
87
+ `AUTH_REQUIRED` (exit 3) so scripts and agents never wait on a browser. The
88
+ older `auth login --write` flag remains accepted as an alias for the default; it
89
+ is not required. Logging in again, including with `--read-only`, revokes the
90
+ previous session's tokens; if that revocation cannot be confirmed, the login
91
+ still succeeds and reports `replacedSessionRevocation: "unconfirmed"`.
92
+
93
+ Refresh tokens are stored in the operating system credential vault (macOS
94
+ Keychain, Windows Credential Manager, or a Linux Secret Service provider).
95
+ Linux installations therefore need a working Secret Service implementation
96
+ such as GNOME Keyring, KWallet, or KeePassXC. Non-secret client registration
97
+ metadata is stored in the OS-standard application config directory with private
98
+ permissions.
99
+
100
+ The default profile retains its existing keychain account. Setting
101
+ `ALLSQUARE_CONFIG_DIR` creates a separate credential namespace for that folder,
102
+ even when it targets the same API URL. A custom-folder profile does not read or
103
+ move credentials stored under the old unscoped account name; log in separately
104
+ in that profile if it previously relied on those credentials.
105
+ Log out of a custom-folder profile before deleting its folder; deleting the
106
+ folder alone does not remove its operating-system credential entry.
107
+
108
+ The CLI targets `https://allsquare.app/api/v1` by default. Set
109
+ `ALLSQUARE_API_URL` to use another deployment. Plain HTTP is accepted only for
110
+ loopback URLs such as `http://localhost:3000/api/v1`.
111
+
112
+ Successful JSON output uses `{ "ok": true, "data": ... }`. When a write
113
+ reuses an idempotency key the server has already completed, the output adds
114
+ `"replayed": true` (human output prints a note on stderr): nothing new was
115
+ written, and `data` is the original result, which may describe a resource that
116
+ has since changed or been deleted. Expected failures
117
+ use `{ "ok": false, "error": { "code": ..., "message": ..., "retryable": ... } }` on stderr
118
+ and exit nonzero, including argument-parser failures. Every error includes
119
+ `retryable`: true only when repeating the same command can succeed unchanged
120
+ (rate limits, service or network failures, temporary login-service errors, a
121
+ busy session refresh, or `IN_PROGRESS` while the same idempotent write is still
122
+ running); retry writes with the same `--idempotency-key`. Error codes use one style
123
+ (`INVALID_ARGUMENT`, `AUTH_REQUIRED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`,
124
+ `RATE_LIMITED`, `SERVICE_UNAVAILABLE`, `NETWORK_ERROR`, ...); the server's own
125
+ code is kept in `details.apiCode`. A group that does not exist returns
126
+ `FORBIDDEN`, the same as one you cannot access. Exit codes are 0 for success,
127
+ 2 for invalid input or configuration, 3 for authentication or permission, 4 for
128
+ conflicts, 5 for temporary or service failures, and 6 for not found.
129
+ Unexpected failures may exit 1. `allsquare agent-guide --json` lists every
130
+ code.
131
+
132
+ `auth logout` always tries to remove local credentials even if the OAuth
133
+ server cannot be reached. Its JSON result includes `revocation` (`confirmed`,
134
+ `unconfirmed`, or `not_needed`). An unconfirmed result means the token may
135
+ remain valid on the server; revoke the CLI connection in AllSquare settings if
136
+ you need immediate server-side invalidation.
137
+
138
+ ## Agent use
139
+
140
+ Agents should begin with `allsquare agent-guide --json`. The guide is a
141
+ versioned, machine-readable operating contract covering resource discovery,
142
+ pagination, least-privilege authorization, write confirmation, idempotent
143
+ retries, deletion, settlement, Venmo handoff, and the CLI output contract. The
144
+ same content is available for humans with `allsquare agent-guide`.
145
+
146
+ The guide supplements safeguards enforced by the CLI. In particular, `--yes`
147
+ means the calling agent has already obtained the required user confirmation; it
148
+ does not grant permission on its own. Agents must preview settlements, render
149
+ any returned Venmo links, avoid claiming that AllSquare completed or verified
150
+ an external Venmo transaction, and record only the expense IDs the user
151
+ selected.
152
+
153
+ `allsquare update --check` compares the installed version with the npm
154
+ `latest` release without changing the installation. `allsquare update` can
155
+ update recognized npm, pnpm, Yarn, or Bun global installations after an
156
+ interactive confirmation; agents and other non-interactive callers must pass
157
+ `--yes`. Source checkouts, local project dependencies, ephemeral invocations,
158
+ and ambiguous installations are never modified automatically. The command
159
+ returns installation-specific guidance instead. JSON execution captures
160
+ package-manager output so the final success or error envelope remains
161
+ machine-readable.
162
+
163
+ Group creation requires an explicit default currency. Repeat `--member-email`
164
+ to add existing accounts or invite up to 20 initial members. `groups add-member`
165
+ uses the same add-or-invite behavior for one address. These operations can send
166
+ email; JSON output reports whether the membership or invitation was created and
167
+ whether its notification was delivered. Both writes are idempotent, and
168
+ `--idempotency-key` is available when retrying the same logical request.
169
+
170
+ Settlement preview is non-mutating and works with read-only authorization. It
171
+ accepts up to 100 repeated `--expense-id` flags and shows the payments that a
172
+ settlement would create without recording the settlement.
173
+
174
+ Settlement execution requires write authorization and always previews the exact
175
+ expense IDs before recording anything. Interactive confirmation defaults to
176
+ “no”; agents, JSON callers, and other non-interactive sessions must pass
177
+ `--yes`. The confirmed request is idempotent, and `--idempotency-key` can be
178
+ reused when retrying the same logical settlement: if the first attempt
179
+ succeeded but its response was lost, the retry returns the original settlement
180
+ with `"replayed": true` even though the preview now reports the expenses as
181
+ settled. `expenses delete` and `recurring-expenses delete` behave the same way:
182
+ a retry with the original `--idempotency-key` returns the original deletion even
183
+ though the item is already gone. Recording a settlement marks
184
+ the selected expenses settled; it does not transfer money.
185
+
186
+ Expense creation requires an explicit currency: USD, CAD, EUR, or GBP. Write commands need a session
187
+ with write access; see the login notes above for when the CLI can prompt for
188
+ one. `--split-type amount` means split equally between the payer and each
189
+ `--split-with` member; the payer is always included. In a two-person
190
+ group, omitting `--split-with` splits with the other member (as the web app
191
+ does); larger groups must name at least one member besides the payer. Percentage and exact modes
192
+ accept repeated `MEMBER_ID=VALUE` allocation flags, validate their totals
193
+ locally, and include every named member even if it is not repeated in
194
+ `--split-with`. `--date` defaults to today in your local time zone. A retry with
195
+ `--idempotency-key` must be the identical command: the same flags and values,
196
+ including `--date` (it is in the first attempt's result) and `--currency`;
197
+ otherwise the server rejects it as a different request. The order of repeated
198
+ flags such as `--split-with` or `--expense-id` does not matter. The CLI generates an idempotency
199
+ key automatically; pass `--idempotency-key` only when retrying the same logical
200
+ creation across separate invocations.
201
+
202
+ Expense updates are partial, except that changing a split requires
203
+ `--split-type` plus the complete replacement participant list. Interactive
204
+ deletion displays the current expense and defaults to “no.” Agents and JSON
205
+ callers must pass `--yes`; deletion still reads the exact target before sending
206
+ the confirmed, idempotent delete request.
207
+
208
+ Recurring-expense writes use the same participant, split, amount, and
209
+ idempotency safeguards. Frequencies are `weekly`, `biweekly`, `monthly`, or
210
+ `yearly`. Pausing keeps generated history and stops future generation; resuming
211
+ continues from the next scheduled date without backfilling the paused period.
212
+ Recurring deletion is permanent, displays the exact template first for
213
+ interactive users, and requires `--yes` for agents and JSON callers. Deleting a
214
+ template stops future expenses but keeps the ones it already created. A
215
+ template whose start date is today or earlier creates its first expense
216
+ immediately and returns its ID as `immediateExpenseId` (`null` when only
217
+ scheduled). Updates cannot change the next date or the currency; delete and
218
+ recreate the template instead.
219
+
220
+ ## Development
221
+
222
+ The CLI is a standalone package inside the AllSquare repository. It does not
223
+ import application or server modules from the repository root.
224
+
225
+ ```sh
226
+ cd cli
227
+ pnpm install
228
+ pnpm check
229
+ ```
230
+
231
+ Before packaging from this repository, install both the root and `cli/`
232
+ dependencies, then run `pnpm --dir cli run prepack`. That gate runs the CLI
233
+ checks and compares its schemas with the generated v1 API contract. The CI
234
+ workflow runs both test suites on each push and pull request.
@@ -0,0 +1,46 @@
1
+ import { Context, Crypto, Effect, Layer, Schema } from "effect";
2
+ import { HttpClient } from "effect/unstable/http";
3
+ import { AuthService } from "../auth/auth-service.js";
4
+ import type { AuthError } from "../auth/auth-service.js";
5
+ import { API_READ_SCOPE, API_WRITE_SCOPE } from "../auth/model.js";
6
+ import { CliConfig } from "../config.js";
7
+ import { ApiRequestError } from "../errors.js";
8
+ import { Interaction } from "../interaction.js";
9
+ export type ApiClientError = AuthError | ApiRequestError;
10
+ export interface ApiResponse<A> {
11
+ readonly data: A;
12
+ readonly replayed: boolean;
13
+ }
14
+ type ResponseSchema<A> = Schema.ConstraintCodec<A, unknown, never, unknown>;
15
+ type BodySchema<B> = Schema.ConstraintCodec<B, unknown, never, never>;
16
+ type Query = Readonly<Record<string, string | undefined>>;
17
+ interface BaseRequestOptions<A> {
18
+ readonly path: string;
19
+ readonly query?: Query;
20
+ readonly schema: ResponseSchema<A>;
21
+ }
22
+ interface BodyRequestOptions<A, B> extends BaseRequestOptions<A> {
23
+ readonly body: B;
24
+ readonly bodySchema: BodySchema<B>;
25
+ }
26
+ type PostRequestOptions<A, B> = BodyRequestOptions<A, B> & ({
27
+ readonly scope: typeof API_READ_SCOPE;
28
+ readonly idempotencyKey?: never;
29
+ } | {
30
+ readonly scope: typeof API_WRITE_SCOPE;
31
+ readonly idempotencyKey?: string;
32
+ });
33
+ interface WriteRequestOptions<A, B> extends BodyRequestOptions<A, B> {
34
+ readonly idempotencyKey?: string;
35
+ }
36
+ declare const ApiClient_base: Context.ServiceClass<ApiClient, "@allsquare/cli/ApiClient", {
37
+ readonly get: <A>(options: BaseRequestOptions<A>) => Effect.Effect<A, ApiClientError>;
38
+ readonly post: <A, B>(options: PostRequestOptions<A, B>) => Effect.Effect<ApiResponse<A>, ApiClientError>;
39
+ readonly patch: <A, B>(options: WriteRequestOptions<A, B>) => Effect.Effect<ApiResponse<A>, ApiClientError>;
40
+ readonly delete: <A, B>(options: WriteRequestOptions<A, B>) => Effect.Effect<ApiResponse<A>, ApiClientError>;
41
+ }>;
42
+ export declare class ApiClient extends ApiClient_base {
43
+ static readonly layer: Layer.Layer<ApiClient, never, CliConfig | Crypto.Crypto | HttpClient.HttpClient | AuthService | Interaction>;
44
+ }
45
+ export {};
46
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ import { Console, Context, Crypto, Effect, Layer, Option, Schema } from "effect";
2
+ import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
3
+ import { AuthService } from "../auth/auth-service.js";
4
+ import { API_READ_SCOPE, API_WRITE_SCOPE } from "../auth/model.js";
5
+ import { CliConfig } from "../config.js";
6
+ import { ApiRequestError } from "../errors.js";
7
+ import { Interaction } from "../interaction.js";
8
+ import { VERSION } from "../version.js";
9
+ const ApiErrorBody = Schema.Struct({
10
+ error: Schema.String,
11
+ code: Schema.optionalKey(Schema.String),
12
+ fields: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
13
+ route: Schema.optionalKey(Schema.String),
14
+ retryAfterSeconds: Schema.optionalKey(Schema.Number)
15
+ });
16
+ const apiErrorFromResponse = (status, body) => {
17
+ let decoded;
18
+ try {
19
+ const result = Schema.decodeUnknownOption(ApiErrorBody)(JSON.parse(body));
20
+ decoded = Option.getOrUndefined(result);
21
+ }
22
+ catch {
23
+ decoded = undefined;
24
+ }
25
+ return new ApiRequestError({
26
+ message: decoded?.error ?? `API request failed with status ${status}`,
27
+ status,
28
+ ...(decoded?.code === undefined ? {} : { apiCode: decoded.code }),
29
+ ...(decoded?.fields === undefined ? {} : { fields: decoded.fields }),
30
+ ...(decoded?.route === undefined ? {} : { route: decoded.route }),
31
+ ...(decoded?.retryAfterSeconds === undefined
32
+ ? {}
33
+ : { retryAfterSeconds: decoded.retryAfterSeconds })
34
+ });
35
+ };
36
+ const requestUrl = (apiUrl, path, query) => {
37
+ const url = new URL(`${apiUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`);
38
+ for (const [key, value] of Object.entries(query)) {
39
+ if (value !== undefined)
40
+ url.searchParams.set(key, value);
41
+ }
42
+ return url.toString();
43
+ };
44
+ export class ApiClient extends Context.Service()("@allsquare/cli/ApiClient") {
45
+ static layer = Layer.effect(ApiClient, Effect.gen(function* () {
46
+ const config = yield* CliConfig;
47
+ const auth = yield* AuthService;
48
+ const interaction = yield* Interaction;
49
+ const client = yield* HttpClient.HttpClient;
50
+ const crypto = yield* Crypto.Crypto;
51
+ const execute = (request, token, schema) => client.execute(request.pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", `allsquare-cli/${VERSION}`), HttpClientRequest.bearerToken(token))).pipe(Effect.flatMap((response) => {
52
+ if (response.status < 200 || response.status >= 300) {
53
+ return response.text.pipe(Effect.orElseSucceed(() => ""), Effect.flatMap((body) => Effect.fail(apiErrorFromResponse(response.status, body))));
54
+ }
55
+ return HttpClientResponse.schemaBodyJson(schema)(response).pipe(Effect.map((data) => ({
56
+ data,
57
+ replayed: response.headers["idempotent-replayed"] === "true"
58
+ })), Effect.mapError((cause) => new ApiRequestError({
59
+ message: `API returned an invalid response: ${cause.message}`,
60
+ status: response.status
61
+ })));
62
+ }), Effect.mapError((cause) => cause instanceof ApiRequestError
63
+ ? cause
64
+ : new ApiRequestError({
65
+ message: `API request failed: ${cause.message}`
66
+ })));
67
+ const requestWithRefresh = (options) => Effect.gen(function* () {
68
+ const accessToken = (scope) => auth.accessToken(scope).pipe(Effect.catchTag("AuthRequiredError", (error) =>
69
+ // Only a missing session starts consent, and only when someone
70
+ // can answer it. A read-only session was chosen deliberately.
71
+ scope === API_READ_SCOPE ||
72
+ error.reason === "insufficient_scope" ||
73
+ !interaction.canPromptForLogin
74
+ ? Effect.fail(error)
75
+ : auth.login({
76
+ write: true,
77
+ noBrowser: false,
78
+ onAuthorizationUrl: (url) => Console.error(`Authorize AllSquare CLI to make changes:\n${url}`),
79
+ onBrowserOpenError: (cause) => Console.error(`Could not open your browser automatically: ${cause.message}`)
80
+ }).pipe(Effect.andThen(auth.accessToken(scope)))));
81
+ // Authorization happens before the request is sent, so an
82
+ // interactive login keeps the write's original idempotency key.
83
+ const token = yield* accessToken(options.scope);
84
+ const first = yield* Effect.result(execute(options.request, token, options.schema));
85
+ if (first._tag === "Success")
86
+ return first.success;
87
+ if (first.failure.status !== 401)
88
+ return yield* first.failure;
89
+ yield* auth.refresh;
90
+ const refreshedToken = yield* accessToken(options.scope);
91
+ return yield* execute(options.request, refreshedToken, options.schema);
92
+ });
93
+ const idempotencyKey = (supplied) => supplied === undefined
94
+ ? crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new ApiRequestError({
95
+ message: `Could not generate an idempotency key: ${cause.message}`
96
+ })))
97
+ : Effect.succeed(supplied);
98
+ const bodyRequest = (options) => Effect.gen(function* () {
99
+ const apiUrl = yield* config.apiUrl;
100
+ const url = requestUrl(apiUrl, options.path, options.query);
101
+ const request = options.method === "POST"
102
+ ? HttpClientRequest.post(url)
103
+ : options.method === "PATCH"
104
+ ? HttpClientRequest.patch(url)
105
+ : HttpClientRequest.delete(url);
106
+ const encoded = yield* request.pipe(HttpClientRequest.schemaBodyJson(options.bodySchema)(options.body), Effect.mapError((cause) => new ApiRequestError({
107
+ message: `Could not encode API request body: ${cause.message}`
108
+ })));
109
+ const authenticatedRequest = options.scope === API_WRITE_SCOPE
110
+ ? encoded.pipe(HttpClientRequest.setHeader("idempotency-key", yield* idempotencyKey(options.idempotencyKey)))
111
+ : encoded;
112
+ return yield* requestWithRefresh({
113
+ request: authenticatedRequest,
114
+ scope: options.scope,
115
+ schema: options.schema
116
+ });
117
+ });
118
+ return ApiClient.of({
119
+ get: ({ path, query = {}, schema }) => Effect.gen(function* () {
120
+ const apiUrl = yield* config.apiUrl;
121
+ const url = requestUrl(apiUrl, path, query);
122
+ const response = yield* requestWithRefresh({
123
+ request: HttpClientRequest.get(url),
124
+ scope: API_READ_SCOPE,
125
+ schema
126
+ });
127
+ return response.data;
128
+ }),
129
+ post: ({ path, query = {}, body, bodySchema, schema, scope, idempotencyKey }) => bodyRequest({
130
+ method: "POST",
131
+ path,
132
+ query,
133
+ body,
134
+ bodySchema,
135
+ schema,
136
+ scope,
137
+ ...(idempotencyKey === undefined ? {} : { idempotencyKey })
138
+ }),
139
+ patch: ({ path, query = {}, body, bodySchema, schema, idempotencyKey }) => bodyRequest({
140
+ method: "PATCH",
141
+ path,
142
+ query,
143
+ body,
144
+ bodySchema,
145
+ schema,
146
+ scope: API_WRITE_SCOPE,
147
+ ...(idempotencyKey === undefined ? {} : { idempotencyKey })
148
+ }),
149
+ delete: ({ path, query = {}, body, bodySchema, schema, idempotencyKey }) => bodyRequest({
150
+ method: "DELETE",
151
+ path,
152
+ query,
153
+ body,
154
+ bodySchema,
155
+ schema,
156
+ scope: API_WRITE_SCOPE,
157
+ ...(idempotencyKey === undefined ? {} : { idempotencyKey })
158
+ })
159
+ });
160
+ }));
161
+ }
162
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAChF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;IACxC,iBAAiB,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;CACrD,CAAC,CAAA;AAEF,MAAM,oBAAoB,GAAG,CAC3B,MAAc,EACd,IAAY,EACK,EAAE;IACnB,IAAI,OAA6C,CAAA;IACjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;QACzE,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,SAAS,CAAA;IACrB,CAAC;IAED,OAAO,IAAI,eAAe,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,kCAAkC,MAAM,EAAE;QACrE,MAAM;QACN,GAAG,CAAC,OAAO,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACjE,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QACjE,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,SAAS;YAC1C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,EAAE,CAAC;KACtD,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,UAAU,GAAG,CACjB,MAAc,EACd,IAAY,EACZ,KAAmD,EAC3C,EAAE;IACV,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;IAChF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC,CAAA;AAyCD,MAAM,OAAO,SAAU,SAAQ,OAAO,CAAC,OAAO,EAa1C,CAAC,0BAA0B,CAAC;IAC9B,MAAM,CAAU,KAAK,GAAG,KAAK,CAAC,MAAM,CAClC,SAAS,EACT,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,SAAS,CAAA;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,WAAW,CAAA;QAC/B,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,WAAW,CAAA;QACtC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;QAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;QAEnC,MAAM,OAAO,GAAG,CACd,OAA4C,EAC5C,KAAa,EACb,MAAyB,EACuB,EAAE,CAClD,MAAM,CAAC,OAAO,CACZ,OAAO,CAAC,IAAI,CACV,iBAAiB,CAAC,SAAS,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EACzD,iBAAiB,CAAC,SAAS,CAAC,YAAY,EAAE,iBAAiB,OAAO,EAAE,CAAC,EACrE,iBAAiB,CAAC,WAAW,CAAC,KAAK,CAAC,CACrC,CACF,CAAC,IAAI,CACJ,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC1B,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBACpD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CACvB,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAC5D,CAAA;YACH,CAAC;YAED,OAAO,kBAAkB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC7D,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACpB,IAAI;gBACJ,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,MAAM;aAC7D,CAAC,CAAC,EACH,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,IAAI,eAAe,CAAC;gBAClB,OAAO,EAAE,qCAAqC,KAAK,CAAC,OAAO,EAAE;gBAC7D,MAAM,EAAE,QAAQ,CAAC,MAAM;aACxB,CAAC,CAAC,CACN,CAAA;QACH,CAAC,CAAC,EACF,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,KAAK,YAAY,eAAe;YAC9B,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,IAAI,eAAe,CAAC;gBAClB,OAAO,EAAE,uBAAuB,KAAK,CAAC,OAAO,EAAE;aAChD,CAAC,CAAC,CACV,CAAA;QAEH,MAAM,kBAAkB,GAAG,CAAI,OAI9B,EAAiD,EAAE,CAClD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,WAAW,GAAG,CAAC,KAAqD,EAAE,EAAE,CAC5E,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CAC1B,MAAM,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE;YAC7C,+DAA+D;YAC/D,8DAA8D;YAC9D,KAAK,KAAK,cAAc;gBACtB,KAAK,CAAC,MAAM,KAAK,oBAAoB;gBACrC,CAAC,WAAW,CAAC,iBAAiB;gBAC9B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;oBACT,KAAK,EAAE,IAAI;oBACX,SAAS,EAAE,KAAK;oBAChB,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE,CAC1B,OAAO,CAAC,KAAK,CAAC,6CAA6C,GAAG,EAAE,CAAC;oBACnE,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAC5B,OAAO,CAAC,KAAK,CAAC,8CAA8C,KAAK,CAAC,OAAO,EAAE,CAAC;iBAC/E,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CACrD,CACF,CAAA;YAEH,0DAA0D;YAC1D,gEAAgE;YAChE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAChC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAChD,CAAA;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC,OAAO,CAAA;YAClD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAA;YAE7D,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;YACnB,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACxD,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QACxE,CAAC,CAAC,CAAA;QAEJ,MAAM,cAAc,GAAG,CACrB,QAA4B,EACY,EAAE,CAAC,QAAQ,KAAK,SAAS;YACjE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CACtB,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,IAAI,eAAe,CAAC;gBAClB,OAAO,EAAE,0CAA0C,KAAK,CAAC,OAAO,EAAE;aACnE,CAAC,CAAC,CACN;YACH,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAE5B,MAAM,WAAW,GAAG,CAAO,OAS1B,EAAiD,EAAE,CAClD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;YACnC,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;YAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,MAAM;gBACvC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;gBAC7B,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,OAAO;oBAC1B,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC;oBAC9B,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACnC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CACjC,iBAAiB,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAClE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,IAAI,eAAe,CAAC;gBAClB,OAAO,EAAE,sCAAsC,KAAK,CAAC,OAAO,EAAE;aAC/D,CAAC,CAAC,CACN,CAAA;YACD,MAAM,oBAAoB,GAAG,OAAO,CAAC,KAAK,KAAK,eAAe;gBAC5D,CAAC,CAAC,OAAO,CAAC,IAAI,CACV,iBAAiB,CAAC,SAAS,CACzB,iBAAiB,EACjB,KAAK,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAC9C,CACF;gBACH,CAAC,CAAC,OAAO,CAAA;YAEX,OAAO,KAAK,CAAC,CAAC,kBAAkB,CAAC;gBAC/B,OAAO,EAAE,oBAAoB;gBAC7B,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEJ,OAAO,SAAS,CAAC,EAAE,CAAC;YAClB,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CACpC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;gBACnC,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;gBAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC;oBACzC,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC;oBACnC,KAAK,EAAE,cAAc;oBACrB,MAAM;iBACP,CAAC,CAAA;gBACF,OAAO,QAAQ,CAAC,IAAI,CAAA;YACtB,CAAC,CAAC;YACJ,IAAI,EAAE,CAAC,EACL,IAAI,EACJ,KAAK,GAAG,EAAE,EACV,IAAI,EACJ,UAAU,EACV,MAAM,EACN,KAAK,EACL,cAAc,EACf,EAAE,EAAE,CAAC,WAAW,CAAC;gBAChB,MAAM,EAAE,MAAM;gBACd,IAAI;gBACJ,KAAK;gBACL,IAAI;gBACJ,UAAU;gBACV,MAAM;gBACN,KAAK;gBACL,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC;aAC5D,CAAC;YACF,KAAK,EAAE,CAAC,EACN,IAAI,EACJ,KAAK,GAAG,EAAE,EACV,IAAI,EACJ,UAAU,EACV,MAAM,EACN,cAAc,EACf,EAAE,EAAE,CAAC,WAAW,CAAC;gBAChB,MAAM,EAAE,OAAO;gBACf,IAAI;gBACJ,KAAK;gBACL,IAAI;gBACJ,UAAU;gBACV,MAAM;gBACN,KAAK,EAAE,eAAe;gBACtB,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC;aAC5D,CAAC;YACF,MAAM,EAAE,CAAC,EACP,IAAI,EACJ,KAAK,GAAG,EAAE,EACV,IAAI,EACJ,UAAU,EACV,MAAM,EACN,cAAc,EACf,EAAE,EAAE,CAAC,WAAW,CAAC;gBAChB,MAAM,EAAE,QAAQ;gBAChB,IAAI;gBACJ,KAAK;gBACL,IAAI;gBACJ,UAAU;gBACV,MAAM;gBACN,KAAK,EAAE,eAAe;gBACtB,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC;aAC5D,CAAC;SACH,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA"}