@substrat-run/connector-planima 0.2.0 → 0.2.2

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.
@@ -0,0 +1,429 @@
1
+ import { z } from 'zod';
2
+ import { type ConnectionActivity, type ConnectionCredential, type ConnectionProbe, type ConnectionId } from '@substrat-run/contracts';
3
+ import type { FetchLike, ScopeHost } from '@substrat-run/kernel';
4
+ export { PlanimaApi, PlanimaApiError, PLANIMA_API_BASE, PLANIMA_ACCEPT, PLANIMA_MAX_PAGE, PLANIMA_ACTION_STATUSES, planimaSecret, type PlanimaAction, type PlanimaApiOptions, type PlanimaBuilding, type PlanimaComponent, type PlanimaFacility, type PlanimaOrganization, type PlanimaSecret, } from './api.js';
5
+ export { decimalOf, planimaActionFact, planimaBuildingFact, planimaComponentFact, planimaDecimal, planimaFacilityFact, type PlanimaActionFact, type PlanimaBuildingFact, type PlanimaComponentFact, type PlanimaFacilityFact, } from './plan.js';
6
+ export { PlanimaMock, type PlanimaMockOptions } from './mock.js';
7
+ /**
8
+ * The Planima connector — the INBOUND half of maintenance-plan integration.
9
+ *
10
+ * [Planima](https://planima.se/) is a Swedish web application for planned facility
11
+ * maintenance (sv. *underhållsplan*). A plan is a tree — organization → facility →
12
+ * building → component — with **actions** hanging off it: dated, priced, categorized
13
+ * work a property owner intends to do in a given year.
14
+ *
15
+ * ## Poll-only, and that is a design fact rather than an omission
16
+ *
17
+ * Like `connector-fortnox` and unlike `connector-scrive`, this connector registers no
18
+ * event handler at all, because nothing inside a scope initiates the work. A vertical
19
+ * does not *ask* for next year's maintenance plan the way it asks for a signature; the
20
+ * plan changes in Planima — a surveyor walks a roof and moves an action from 2031 to
21
+ * 2027 — and the platform finds out by looking.
22
+ *
23
+ * So there is no `registerPlanimaConnector`. {@link sweepPlanimaPlan} is the whole
24
+ * trigger surface, and a deployment binds it into the platform sweeper exactly as it
25
+ * binds Fortnox's.
26
+ *
27
+ * ## Read-only, and the credential is why
28
+ *
29
+ * Planima's API can create and update organizations, facilities, buildings and
30
+ * components. This connector uses none of it. A Planima token carries the full access
31
+ * of the user who minted it — there are no scopes to narrow — so the narrowing that
32
+ * matters is operational: hand this connector a **read-only user's token** and the
33
+ * blast radius of a leaked credential is a maintenance plan someone could already see.
34
+ * A write path would forfeit that, and no vertical has asked for one; when one does,
35
+ * it belongs behind an event and a dispatch, in the shape `connector-scrive` already
36
+ * has.
37
+ *
38
+ * ## Where the data lands, and why the connector does not decide
39
+ *
40
+ * A sweep has no delivered event, so it has neither a scope to write to nor authority
41
+ * to write with. That is declared **once**, explicitly, by {@link bindPlanimaScope}:
42
+ * which scope, which operation to land the plan through, and which permission that
43
+ * operation checks.
44
+ *
45
+ * The operation is the *consumer's*, and that is deliberate. What comes out of Planima
46
+ * is neutral fact — a component, a year, a price, a status string. What a business
47
+ * *means* by them (which status counts as committed spend, which category rolls into
48
+ * which budget line, whether a deferred action still books) is vocabulary, and
49
+ * vocabulary is the vertical's layer.
50
+ */
51
+ /**
52
+ * The standing grants this connector requires — deliberately EMPTY, with a mechanism
53
+ * in place of a declaration.
54
+ *
55
+ * The reasoning is `FORTNOX_CONNECTION_GRANTS`'s, and it applies here for the same
56
+ * reason: the permission this connector needs is whatever the *consumer's* landing
57
+ * operation checks, which differs per vertical and is unknown at this package's build
58
+ * time. So the check moves from build time to bind time — {@link bindPlanimaScope}
59
+ * verifies the connection actually holds the named permission in the named scope and
60
+ * **refuses the binding otherwise**, naming what is missing. A sweep can therefore
61
+ * never be configured into a state where it fetches a whole maintenance plan and
62
+ * cannot write it down.
63
+ */
64
+ export declare const PLANIMA_CONNECTION_GRANTS: readonly [];
65
+ /**
66
+ * The currency a Planima plan's prices are in — a DECLARED fact, because the API does
67
+ * not carry one.
68
+ *
69
+ * Planima sends `unit_price: 1200` and nothing else: no currency field on the action,
70
+ * the facility, the organization or the account. The product is Swedish and its prices
71
+ * are kronor, so `SEK` is the right default — but it is a default this connector chose,
72
+ * not a value it read, and Substrat money is a `{ amount, currency }` pair that cannot
73
+ * be built without one. Naming it on the binding is what keeps that choice visible and
74
+ * overridable instead of hard-coded three files down.
75
+ */
76
+ export declare const PLANIMA_DEFAULT_CURRENCY = "SEK";
77
+ /**
78
+ * How many years past the current one a sweep reads when a binding names no window.
79
+ *
80
+ * A Swedish maintenance plan is conventionally drawn 30 years out, and almost nothing
81
+ * consumes all of it: the far years are placeholders that move every time a surveyor
82
+ * revisits. Ten years is the horizon a budget actually uses, and — because the window
83
+ * is part of the sync's identity — a shorter one also means the far-future churn does
84
+ * not make every sweep look like a change.
85
+ */
86
+ export declare const PLANIMA_DEFAULT_HORIZON_YEARS = 10;
87
+ /**
88
+ * What the connector remembers about one scope it syncs into.
89
+ *
90
+ * Directory-side (`putConnectorState`) for the same reason Fortnox's binding is: this
91
+ * is a connector's own bookkeeping, it must survive across sweeps, and it must be
92
+ * readable without entering a scope.
93
+ */
94
+ export interface PlanimaBinding {
95
+ scopeId: string;
96
+ tenantId: string;
97
+ /** The scope's vertical — half the key that reopens the connection to poll. */
98
+ vertical: string;
99
+ /**
100
+ * The operation the plan is landed through, e.g. `'maintenance/record-plan'`.
101
+ *
102
+ * Named by whoever binds, never defaulted. A default here would be a name this
103
+ * package invented for an operation it does not implement — so the first deployment
104
+ * to get it wrong would find out at sweep time, from an `unknown operation` error
105
+ * three layers down, rather than at bind time from this function.
106
+ */
107
+ operation: string;
108
+ /**
109
+ * The permission `operation` checks, which the connection must hold on `scopeId`.
110
+ *
111
+ * Recorded so the sweep can re-verify cheaply and so an operator can read the whole
112
+ * authority of this binding without opening the vertical's source.
113
+ */
114
+ permission: string;
115
+ /**
116
+ * WHICH Planima organization this scope syncs, or `null` for every one the token can
117
+ * see.
118
+ *
119
+ * A token belongs to a Planima customer account, and an account may hold several
120
+ * organizations — a municipality with one per administration, say. `null` is the
121
+ * right default for the common case of one organization per account, and naming an id
122
+ * is what keeps two scopes on one token from each landing the other's buildings.
123
+ */
124
+ organizationId: number | null;
125
+ /** The currency the plan's prices are read as — see {@link PLANIMA_DEFAULT_CURRENCY}. */
126
+ currency: string;
127
+ /**
128
+ * A FIXED year window, or `null` to read a rolling one from `horizonYears`.
129
+ *
130
+ * Fixed is for a back-fill or a frozen budget year; rolling is what a standing sync
131
+ * wants, because "the next ten years" should still mean that in January.
132
+ */
133
+ window: {
134
+ fromYear: number;
135
+ toYear: number;
136
+ } | null;
137
+ /** Years past the current one, when `window` is `null`. */
138
+ horizonYears: number;
139
+ boundAt: string;
140
+ /** Set after the first successful sync — the cursor that makes a re-sync cheap. */
141
+ lastSync?: {
142
+ syncedAt: string;
143
+ /**
144
+ * SHA-256 of the assembled plan AND the window it was read through. Unchanged ⇒
145
+ * the sync is skipped without landing.
146
+ *
147
+ * The window is in here for the reason Fortnox's is: what lands is the plan
148
+ * *filtered to* the window, so the same rows read through a different window are a
149
+ * different result, and hashing the rows alone would make an explicit back-fill
150
+ * over an already-synced organization a silent no-op. It matters more here than
151
+ * there, because this window MOVES on its own — a rolling horizon crosses a new
152
+ * year every January, and without the window in the hash that year's actions would
153
+ * not land until something else about the plan happened to change.
154
+ */
155
+ contentHash: string;
156
+ facilities: number;
157
+ actions: number;
158
+ };
159
+ }
160
+ /**
161
+ * One page of a maintenance plan, as it crosses into a scope.
162
+ *
163
+ * Parsed with this schema on the way OUT, before every `invoke`. The engine-seam rule
164
+ * (`returns()`) exists because a value crossing a version boundary must be pinned to a
165
+ * published shape rather than to whatever the code currently produces, and a connector
166
+ * seam is the same boundary with a network in the middle: a vertical compiled against
167
+ * one version of this package and running against another must get a throw, never a
168
+ * silently-reshaped plan on a screen.
169
+ *
170
+ * ## The paging shape, and how a consumer reads it
171
+ *
172
+ * Pages are global across one sync and each page names exactly one facility. A
173
+ * facility's buildings and components ride its **first** page only (`facilityHead`) —
174
+ * they are the same on every page of that facility, and repeating a component list
175
+ * across ten pages of actions is bytes through a clone pipe for nothing. So a consumer
176
+ * upserts on `facilityHead`, appends actions on every page, and commits or swaps when
177
+ * `final` arrives.
178
+ */
179
+ export declare const planimaPlanPage: z.ZodObject<{
180
+ syncId: z.ZodString;
181
+ connectionId: z.ZodString;
182
+ organization: z.ZodNullable<z.ZodObject<{
183
+ id: z.ZodNumber;
184
+ name: z.ZodString;
185
+ }, z.core.$strip>>;
186
+ facility: z.ZodNullable<z.ZodObject<{
187
+ id: z.ZodNumber;
188
+ name: z.ZodString;
189
+ address: z.ZodNullable<z.ZodString>;
190
+ zipCode: z.ZodNullable<z.ZodString>;
191
+ region: z.ZodNullable<z.ZodString>;
192
+ tags: z.ZodArray<z.ZodString>;
193
+ residentialArea: z.ZodNullable<z.ZodString>;
194
+ nonResidentialArea: z.ZodNullable<z.ZodString>;
195
+ yearOfConstruction: z.ZodNullable<z.ZodNumber>;
196
+ description: z.ZodNullable<z.ZodString>;
197
+ }, z.core.$strip>>;
198
+ window: z.ZodObject<{
199
+ fromYear: z.ZodNumber;
200
+ toYear: z.ZodNumber;
201
+ }, z.core.$strip>;
202
+ currency: z.ZodString;
203
+ page: z.ZodNumber;
204
+ pageCount: z.ZodNumber;
205
+ final: z.ZodBoolean;
206
+ facilityHead: z.ZodBoolean;
207
+ buildings: z.ZodDefault<z.ZodArray<z.ZodObject<{
208
+ id: z.ZodNumber;
209
+ facilityId: z.ZodNumber;
210
+ name: z.ZodString;
211
+ address: z.ZodNullable<z.ZodString>;
212
+ zipCode: z.ZodNullable<z.ZodString>;
213
+ region: z.ZodNullable<z.ZodString>;
214
+ yearOfConstruction: z.ZodNullable<z.ZodNumber>;
215
+ }, z.core.$strip>>>;
216
+ components: z.ZodDefault<z.ZodArray<z.ZodObject<{
217
+ id: z.ZodNumber;
218
+ facilityId: z.ZodNumber;
219
+ buildingId: z.ZodNullable<z.ZodNumber>;
220
+ name: z.ZodString;
221
+ component: z.ZodNullable<z.ZodString>;
222
+ category: z.ZodNullable<z.ZodString>;
223
+ type: z.ZodNullable<z.ZodString>;
224
+ amount: z.ZodNullable<z.ZodString>;
225
+ unit: z.ZodNullable<z.ZodString>;
226
+ }, z.core.$strip>>>;
227
+ actions: z.ZodArray<z.ZodObject<{
228
+ id: z.ZodNumber;
229
+ facilityId: z.ZodNumber;
230
+ buildingId: z.ZodNullable<z.ZodNumber>;
231
+ componentId: z.ZodNullable<z.ZodNumber>;
232
+ projectId: z.ZodNullable<z.ZodNumber>;
233
+ name: z.ZodString;
234
+ year: z.ZodNumber;
235
+ status: z.ZodString;
236
+ description: z.ZodNullable<z.ZodString>;
237
+ amount: z.ZodNullable<z.ZodString>;
238
+ unit: z.ZodNullable<z.ZodString>;
239
+ unitPrice: z.ZodNullable<z.ZodObject<{
240
+ amount: z.core.$ZodBranded<z.ZodString, "MoneyAmount", "out">;
241
+ currency: z.core.$ZodBranded<z.ZodString, "CurrencyCode", "out">;
242
+ }, z.core.$strip>>;
243
+ totalPrice: z.ZodNullable<z.ZodObject<{
244
+ amount: z.core.$ZodBranded<z.ZodString, "MoneyAmount", "out">;
245
+ currency: z.core.$ZodBranded<z.ZodString, "CurrencyCode", "out">;
246
+ }, z.core.$strip>>;
247
+ totalPriceInclVat: z.ZodNullable<z.ZodObject<{
248
+ amount: z.core.$ZodBranded<z.ZodString, "MoneyAmount", "out">;
249
+ currency: z.core.$ZodBranded<z.ZodString, "CurrencyCode", "out">;
250
+ }, z.core.$strip>>;
251
+ finalCost: z.ZodNullable<z.ZodObject<{
252
+ amount: z.core.$ZodBranded<z.ZodString, "MoneyAmount", "out">;
253
+ currency: z.core.$ZodBranded<z.ZodString, "CurrencyCode", "out">;
254
+ }, z.core.$strip>>;
255
+ vatRate: z.ZodNullable<z.ZodString>;
256
+ investmentRate: z.ZodNullable<z.ZodString>;
257
+ isEnergySaving: z.ZodBoolean;
258
+ co2EquivalentKg: z.ZodNullable<z.ZodString>;
259
+ category: z.ZodNullable<z.ZodString>;
260
+ location: z.ZodNullable<z.ZodString>;
261
+ building: z.ZodNullable<z.ZodString>;
262
+ tags: z.ZodArray<z.ZodString>;
263
+ updatedAt: z.ZodNullable<z.ZodString>;
264
+ }, z.core.$strip>>;
265
+ }, z.core.$strip>;
266
+ export type PlanimaPlanPage = z.infer<typeof planimaPlanPage>;
267
+ export interface PlanimaConnectorOptions {
268
+ fetch: FetchLike;
269
+ apiBase?: string;
270
+ timeoutMs?: number;
271
+ /** Injected so a test can assert elapsed time without sleeping. */
272
+ now?: () => number;
273
+ /** Injected for the same reason — the rate-limit throttle waits through this. */
274
+ sleep?: (ms: number) => Promise<void>;
275
+ /**
276
+ * A sliding rate-limit window shared by every client built during one sweep.
277
+ *
278
+ * `sweepPlanimaPlan` creates one and threads it through, because Planima meters per
279
+ * TOKEN: without it, each bound scope's client starts with an empty window and the
280
+ * second scope's requests pile on top of the first's.
281
+ */
282
+ rateWindow?: number[];
283
+ }
284
+ /**
285
+ * Declare that a connection should sync one scope — the one-time setup a poll-only
286
+ * connector needs in place of a dispatch.
287
+ *
288
+ * **Refuses a binding whose grant is missing**, which is the whole reason this is a
289
+ * function rather than a config object. The alternative — write the binding, discover
290
+ * at sweep time that the connection cannot invoke the operation — fails in the worst
291
+ * possible place: after a whole maintenance plan has been fetched against a
292
+ * 10-request-per-10-seconds budget, in a background timer nobody is watching. Here it
293
+ * fails in the operator's hands, naming the permission to grant.
294
+ */
295
+ export declare function bindPlanimaScope(host: ScopeHost, input: {
296
+ connectionId: ConnectionId;
297
+ tenantId: string;
298
+ scopeId: string;
299
+ vertical: string;
300
+ operation: string;
301
+ permission: string;
302
+ organizationId?: number | null;
303
+ currency?: string;
304
+ window?: {
305
+ fromYear: number;
306
+ toYear: number;
307
+ } | null;
308
+ horizonYears?: number;
309
+ now?: () => number;
310
+ }): Promise<PlanimaBinding>;
311
+ /** Every scope this connection syncs into. */
312
+ export declare function listPlanimaBindings(host: ScopeHost, connectionId: ConnectionId): Promise<PlanimaBinding[]>;
313
+ /**
314
+ * Stop syncing one scope. The binding row is replaced with a tombstone rather than
315
+ * removed, because `putConnectorState` is the only verb this surface has — and an
316
+ * unbound scope that a later sweep silently re-adopts would be worse than a visible
317
+ * dead row.
318
+ */
319
+ export declare function unbindPlanimaScope(host: ScopeHost, connectionId: ConnectionId, scopeId: string): Promise<void>;
320
+ /** What one scope's sync did. */
321
+ export interface PlanimaSyncResult {
322
+ scopeId: string;
323
+ /** False when the plan was identical to the last sync — nothing was landed. */
324
+ changed: boolean;
325
+ syncId: string;
326
+ window: {
327
+ fromYear: number;
328
+ toYear: number;
329
+ };
330
+ facilities: number;
331
+ actions: number;
332
+ pages: number;
333
+ }
334
+ /** The window a binding reads through, resolved against the clock for a rolling one. */
335
+ export declare function windowFor(binding: PlanimaBinding, nowMs: number): {
336
+ fromYear: number;
337
+ toYear: number;
338
+ };
339
+ /**
340
+ * Sync ONE bound scope: read the plan, hash it, and land it through the binding's
341
+ * operation as the connection itself (#97).
342
+ *
343
+ * Idempotent and cheap to re-land on a no-op. The assembled plan is hashed before
344
+ * anything is landed, and an unchanged hash returns `changed: false` without a single
345
+ * `invoke` — which matters because a sweep runs on a timer and most passes find a plan
346
+ * nobody has touched.
347
+ *
348
+ * Note what that does NOT save: the provider reads still happen, because Planima offers
349
+ * no collection-level `updated_at` or ETag to ask "has anything changed" cheaply. The
350
+ * skip saves the writes and the events, not the round trips. Being straight about that
351
+ * is what makes the sweep interval a real decision — every pass costs
352
+ * `1 + 3 × facilities` requests against a 10-per-10-second budget.
353
+ */
354
+ export declare function syncPlanimaScope(host: ScopeHost, connectionId: ConnectionId, binding: PlanimaBinding, options: PlanimaConnectorOptions & {
355
+ window?: {
356
+ fromYear: number;
357
+ toYear: number;
358
+ };
359
+ }): Promise<PlanimaSyncResult>;
360
+ /** What one sweep pass over a connection did. */
361
+ export interface PlanimaSweepResult {
362
+ found: number;
363
+ synced: PlanimaSyncResult[];
364
+ unchanged: number;
365
+ failed: {
366
+ scopeId: string;
367
+ error: string;
368
+ }[];
369
+ }
370
+ /**
371
+ * Poll Planima for every scope this connection is bound to — the sweeper a deployment
372
+ * schedules.
373
+ *
374
+ * A timer calls this; it holds no timer itself. That keeps the trigger a deployment
375
+ * concern (`startPlatformSweeper` on node, `definePlatformSweeperDO`'s alarm on
376
+ * Cloudflare) and this a plain, testable function.
377
+ *
378
+ * Robust the way a poller must be: an unchanged plan is skipped without landing
379
+ * anything, and a failure on one scope is recorded and stepped over rather than sinking
380
+ * the pass — one vertical's missing grant must not stop another tenant's plan syncing.
381
+ */
382
+ export declare function sweepPlanimaPlan(host: ScopeHost, connectionId: ConnectionId, options: PlanimaConnectorOptions & {
383
+ window?: {
384
+ fromYear: number;
385
+ toYear: number;
386
+ };
387
+ }): Promise<PlanimaSweepResult>;
388
+ /**
389
+ * Probe a credential that is not stored yet — the connect-time check (#605).
390
+ *
391
+ * Takes the candidate secret directly, touches no connection and no store, and records
392
+ * no health: there may be no connection to record against, and a candidate's failure is
393
+ * not a fact about a live one.
394
+ *
395
+ * The probe reads `/organizations`, which is both the cheapest authenticated read
396
+ * Planima offers and the one that answers the question an operator actually has: not
397
+ * "is this token valid" but "does this token see the customer account I meant". A token
398
+ * from the wrong Planima login is perfectly valid and syncs somebody else's buildings.
399
+ */
400
+ export declare function probePlanimaSecret(secret: Record<string, string>, options: PlanimaConnectorOptions): Promise<ConnectionProbe>;
401
+ /** Probe the credential a live connection already holds. Verifying is itself a use. */
402
+ export declare function probePlanimaConnection(host: ScopeHost, connection: {
403
+ tenantId: string;
404
+ vertical: string;
405
+ }, options: PlanimaConnectorOptions): Promise<ConnectionProbe>;
406
+ /**
407
+ * What this connection has been doing, for a console — one entry per bound scope.
408
+ *
409
+ * Reads the binding ledger rather than the provider: this answers "what has the platform
410
+ * synced", which is the question an operator asks when a plan looks stale, and it
411
+ * answers it without spending a provider round trip against a 10-per-10-second budget.
412
+ */
413
+ export declare function planimaConnectionActivity(host: ScopeHost, connectionId: ConnectionId): Promise<ConnectionActivity>;
414
+ /**
415
+ * The stored credential, REDUCED (#605) — and for this provider that is one masked
416
+ * field and nothing else.
417
+ *
418
+ * There is no identifier half to show. Fortnox can display its client id and
419
+ * DatabaseNumber unmasked because they name the integration and the company; a Planima
420
+ * credential is a single opaque token and every character of it is secret. So this
421
+ * surface is honest rather than useful, and the useful answer — *which* Planima account
422
+ * this is — comes from {@link probePlanimaConnection}, which reads the organization
423
+ * names back from the provider.
424
+ */
425
+ export declare function planimaCredentialSummary(host: ScopeHost, connection: {
426
+ tenantId: string;
427
+ vertical: string;
428
+ }): Promise<ConnectionCredential>;
429
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAuB,SAAS,EAAa,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAmBjG,OAAO,EACL,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,aAAc,CAAC;AAMrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAE9C;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAKhD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB;;;;;;;;;;;WAWG;QACH,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmC1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,KAAK,EAAE;IACL,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,cAAc,CAAC,CA6CzB;AAED,8CAA8C;AAC9C,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,SAAS,EACf,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC,CAS3B;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,SAAS,EACf,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,iCAAiC;AACjC,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAItG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,uBAAuB,GAAG;IAAE,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACnF,OAAO,CAAC,iBAAiB,CAAC,CAoK5B;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,uBAAuB,GAAG;IAAE,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACnF,OAAO,CAAC,kBAAkB,CAAC,CA2B7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED,uFAAuF;AACvF,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,SAAS,EACf,UAAU,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAClD,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,eAAe,CAAC,CAS1B;AA8DD;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,SAAS,EACf,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,kBAAkB,CAAC,CA6C7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,SAAS,EACf,UAAU,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,oBAAoB,CAAC,CAe/B"}