@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.
- package/LICENSE +661 -0
- package/dist/api.d.ts +284 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +462 -0
- package/dist/api.js.map +1 -0
- package/dist/index.d.ts +429 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +646 -0
- package/dist/index.js.map +1 -0
- package/dist/mock.d.ts +122 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +200 -0
- package/dist/mock.js.map +1 -0
- package/dist/plan.d.ts +141 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +226 -0
- package/dist/plan.js.map +1 -0
- package/package.json +13 -13
package/dist/index.d.ts
ADDED
|
@@ -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":"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"}
|