@substrat-run/connector-planima 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +75 -0
  2. package/package.json +45 -0
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @substrat-run/connector-planima
2
+
3
+ The Substrat connector for [Planima](https://planima.se/) — Swedish planned facility
4
+ maintenance (*underhållsplan*). Reads a maintenance plan on a poll and lands it into a
5
+ scope through the consuming vertical's own operation.
6
+
7
+ Full documentation: **[substrat.net/connectors/planima](https://substrat.net/connectors/planima)**.
8
+
9
+ > **Not yet verified against a live account.** Every claim here comes from Planima's
10
+ > published [OpenAPI document](https://developer.planima.se/) and is held up by a mock
11
+ > that encodes the same reading — so mock and client can agree with each other while both
12
+ > disagree with Planima. `test/live.test.ts` is written and skips until `PLANIMA_TOKEN` is
13
+ > present.
14
+
15
+ ## What it is
16
+
17
+ - **Poll-only.** No event handler, no dispatch. Nothing inside a scope initiates this —
18
+ the plan changes in Planima and the platform finds out by looking.
19
+ - **Read-only.** Every call is a `GET`. Planima's write endpoints exist and are not used.
20
+ - **Host code**, never module code: it is swept on a `ScopeHost`, and module code cannot
21
+ reach `fetch` at all.
22
+
23
+ ## Using it
24
+
25
+ ```ts
26
+ import { bindPlanimaScope, sweepPlanimaPlan } from '@substrat-run/connector-planima';
27
+ import { globalFetch } from '@substrat-run/kernel';
28
+
29
+ // Once, per scope. Refuses if the connection does not hold the permission.
30
+ await bindPlanimaScope(host, {
31
+ connectionId,
32
+ tenantId,
33
+ scopeId,
34
+ vertical: 'maintenance',
35
+ operation: 'maintenance/record-plan', // YOUR operation
36
+ permission: 'plan:record', // which it checks
37
+ organizationId: null, // or one id
38
+ currency: 'SEK',
39
+ horizonYears: 10,
40
+ });
41
+
42
+ // On a timer, from the platform sweeper.
43
+ await sweepPlanimaPlan(host, connectionId, { fetch: globalFetch });
44
+ ```
45
+
46
+ Your landing operation receives a `PlanimaPlanPage`. Pages are global across one sync and
47
+ each names one facility: buildings and components ride the facility's first page
48
+ (`facilityHead`), actions ride every page 500 at a time, and `final` marks the last page of
49
+ the sync. Every page of one sync carries the same `syncId`, so an upsert keyed on it is
50
+ idempotent.
51
+
52
+ ## The credential
53
+
54
+ One static API token, created in Planima under *account settings → API*.
55
+
56
+ **Mint it as a read-only user.** A Planima token carries the full access of whoever created
57
+ it and there are no scopes to narrow it — so choosing the user is the only control there
58
+ is, and this connector never writes.
59
+
60
+ ## Development
61
+
62
+ ```sh
63
+ pnpm --filter @substrat-run/connector-planima test # against the in-memory mock
64
+ ```
65
+
66
+ The live suite runs when a token is present, in `secrets/connectors.env` at the repo root
67
+ (canonical, shared by every connector), this package's `.dev.vars`, or `PLANIMA_TOKEN` in
68
+ the environment. See [`.dev.vars.example`](./.dev.vars.example).
69
+
70
+ ```sh
71
+ PLANIMA_TOKEN=… pnpm --filter @substrat-run/connector-planima test
72
+ ```
73
+
74
+ It is read-only and creates nothing, so it is safe against a real account — but it does
75
+ spend requests against a 10-per-10-second budget.
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@substrat-run/connector-planima",
3
+ "version": "0.2.0",
4
+ "description": "Substrat connector: Planima planned facility maintenance (Swedish). Reads a maintenance plan — facilities, buildings, components and actions — on a poll and lands it into a scope through the vertical's own operation. Auth is a static API token, so there is no token to refresh. Host code — swept on a ScopeHost, never module code.",
5
+ "license": "AGPL-3.0-only",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/substrat-run/substrat.git",
9
+ "directory": "connectors/planima"
10
+ },
11
+ "homepage": "https://substrat.net/connectors/planima",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "type": "module",
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ }
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json",
29
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
30
+ "test": "vitest run"
31
+ },
32
+ "dependencies": {
33
+ "@substrat-run/contracts": "workspace:^",
34
+ "@substrat-run/kernel": "workspace:^"
35
+ },
36
+ "devDependencies": {
37
+ "@substrat-run/adapter-sqlite": "workspace:^",
38
+ "typescript": "catalog:",
39
+ "vitest": "catalog:",
40
+ "zod": "catalog:"
41
+ },
42
+ "peerDependencies": {
43
+ "zod": "^4.4.0"
44
+ }
45
+ }