@fourier-labs/harbour-contracts 0.50.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/contract-release.json +22 -19
- package/dist/gen-ts/app-integrations-declaration.d.ts +25 -4
- package/dist/gen-ts/contract-release.d.ts +6 -6
- package/dist/gen-ts/contract-release.js +6 -6
- package/dist/gen-ts/data-grant-v2.d.ts +18 -3
- package/dist/gen-ts/index.d.ts +1 -0
- package/dist/gen-ts/index.js +1 -0
- package/dist/gen-ts/integration-execute-input-rules.d.ts +187 -0
- package/dist/gen-ts/integration-execute-input-rules.js +114 -0
- package/dist/gen-ts/integration-execute-request.d.ts +8 -2
- package/dist/gen-ts/saas-connection.d.ts +6 -0
- package/dist/gen-ts/transform-report.d.ts +55 -14
- package/gen-ts/app-integrations-declaration.ts +25 -4
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/data-grant-v2.ts +17 -3
- package/gen-ts/index.ts +1 -0
- package/gen-ts/integration-execute-input-rules.ts +119 -0
- package/gen-ts/integration-execute-request.ts +8 -2
- package/gen-ts/saas-connection.ts +6 -0
- package/gen-ts/transform-report.ts +55 -14
- package/package.json +1 -1
- package/schema/app-integrations-declaration.schema.json +37 -6
- package/schema/data-grant-v2.schema.json +2 -1
- package/schema/integration-execute-request.schema.json +9 -9
- package/schema/saas-connection.schema.json +3 -2
- package/schema/transform-report.schema.json +40 -22
package/README.md
CHANGED
|
@@ -84,6 +84,12 @@ Two schemas carry logic beyond structural types, generated into helpers:
|
|
|
84
84
|
- **`binding-classification-subject.schema.json`** and **`sanitized-binding-evidence.schema.json`**
|
|
85
85
|
→ canonical length-prefixed SHA-256 preimage/digest helpers plus exact-name,
|
|
86
86
|
requested-mapping and allocation-receipt validators in Go and TypeScript.
|
|
87
|
+
- **`integration-execute-request.schema.json`** → `IntegrationExecuteInputRules` +
|
|
88
|
+
`validateIntegrationExecuteInput(operation, input)` (TS) / `ValidateIntegrationExecuteInput` (Go),
|
|
89
|
+
derived from each operation's `then`-block (`propertyNames.enum`, `required`, per-field
|
|
90
|
+
`maximum`/`default`). The governance execute route, the app SDK and the kit's local check all
|
|
91
|
+
refuse a request with the same sentence (`limit must be an integer between 1 and 15`); the
|
|
92
|
+
SDK vendors the TypeScript file because it ships with no dependencies.
|
|
87
93
|
|
|
88
94
|
## Workflow
|
|
89
95
|
|
package/contract-release.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "harbour.contract-release/1.0",
|
|
3
|
-
"repositoryVersion": "0.
|
|
4
|
-
"publishVersion": "0.
|
|
5
|
-
"publishTag": "v0.
|
|
6
|
-
"schemasDigest": "sha256:
|
|
3
|
+
"repositoryVersion": "0.53.0.0",
|
|
4
|
+
"publishVersion": "0.53.0",
|
|
5
|
+
"publishTag": "v0.53.0",
|
|
6
|
+
"schemasDigest": "sha256:c4d7c9642842bf3709003f447fb22429ce3573acabaa2da8279e0fc399e8d237",
|
|
7
7
|
"aiFixturesDigest": "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6",
|
|
8
|
-
"generatedGoDigest": "sha256:
|
|
9
|
-
"generatedTypeScriptDigest": "sha256:
|
|
8
|
+
"generatedGoDigest": "sha256:8f3c3281a76792a9480ca1f7bcbad820db1908efffb29072fcdb0a8f99dbd208",
|
|
9
|
+
"generatedTypeScriptDigest": "sha256:82a083862e500a06df3550e694221cff9dcb98461d5174889c374713f961040c",
|
|
10
10
|
"generatedExclusions": {
|
|
11
11
|
"gen-go/contract_release.go": "contains the generated aggregate digests (self-reference)",
|
|
12
12
|
"gen-ts/contract-release.ts": "contains the generated aggregate digests (self-reference)"
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"schema/ai-transform-result.schema.json": "sha256:244e380a36957ab2dcb526ea22aaecb56b53664ce1fda6934d0021c084c99133",
|
|
32
32
|
"schema/ai-verification-evidence.schema.json": "sha256:bdbc762df256d2a8dd0bdda60d4d13915ce40034cd756af9515871d145f10ad9",
|
|
33
33
|
"schema/api-error-response.schema.json": "sha256:3d43d5bdf0b59c944e20509d832e6776c3ecdba4d6c245a9118bbe52062a8304",
|
|
34
|
-
"schema/app-integrations-declaration.schema.json": "sha256:
|
|
34
|
+
"schema/app-integrations-declaration.schema.json": "sha256:5d43c31719c93c9c8235b6c03dbe55a42ebdb2efb78225ad14222bd1086dcf6a",
|
|
35
35
|
"schema/artifact-contract.schema.json": "sha256:8047b461a1c6d07bbe867a259c57bdf5cc0fdf282cf842ec7b683a9e93713b10",
|
|
36
36
|
"schema/binding-classification-subject.schema.json": "sha256:a8825ce652e7e00a18759c4249bd5bcd0cae552dd58440513f867e87f685133c",
|
|
37
37
|
"schema/builder-input-proposal.schema.json": "sha256:fe85cffa6d76b1e1effe1f5d75525f8fe0e1eaa3be4a304e491aa23ad72c0e72",
|
|
38
38
|
"schema/data-dependency.schema.json": "sha256:89ba8481ccabc935e7dab5d13871c985839a751ad1586710d8382a15e1f3340c",
|
|
39
|
-
"schema/data-grant-v2.schema.json": "sha256:
|
|
39
|
+
"schema/data-grant-v2.schema.json": "sha256:7fa6022aa2fcfd3c94bb969315155ea24e5a1aa25a25cb718df4465397076e07",
|
|
40
40
|
"schema/data-grant.schema.json": "sha256:e640553831ecc1c01cc9a37fe388b84d0ffeaa0d0050e63712b48f65fa437c8a",
|
|
41
41
|
"schema/data-source.schema.json": "sha256:aec8d2497dae9a91886a3df9f6fbbdb3d3fcab44b14fbcacc3f5e99b827f1bba",
|
|
42
42
|
"schema/deploy-request.schema.json": "sha256:32531e21983cdfeec511f8b4cddd4dad6530cd90e4e9fe147225d78d138b6198",
|
|
@@ -52,20 +52,20 @@
|
|
|
52
52
|
"schema/error-envelope.schema.json": "sha256:75dccda7dd16fdf8435deda53fc7dbcdc109b65114617a17f11763010c656f90",
|
|
53
53
|
"schema/integration-connect-result.schema.json": "sha256:3f254b67ef739dc1fa395bdc8c4dc93359b16d481dba2a2d777aa5b6f34b7af8",
|
|
54
54
|
"schema/integration-error-details.schema.json": "sha256:09cebaddb7f88e63cbebaa1cada5f91057ae4dc4370e973ef38f7f11c6323284",
|
|
55
|
-
"schema/integration-execute-request.schema.json": "sha256:
|
|
55
|
+
"schema/integration-execute-request.schema.json": "sha256:efbf25e2c01ccafec6e5c8023d891daa51e8b8c29a1735afe0f0eb33c4615834",
|
|
56
56
|
"schema/integration-execute-result.schema.json": "sha256:766146779c01ee1dbdd2161c5dc594f43ccaf322762352bb0dc5b80bcb3275b5",
|
|
57
57
|
"schema/kit-bundle-manifest.schema.json": "sha256:c15e28ae028dbd1c262427b9c797142e19b1f38fa36b4ecfe187e32c20a174ec",
|
|
58
58
|
"schema/kit-lock.schema.json": "sha256:5e24b76660f83990af7a6c037af350c3850ee439ef956f65a0360facbdcdd997",
|
|
59
59
|
"schema/kvs-routing-entry.schema.json": "sha256:b75f3a5dfa93a428b2764e03222b66b6ef2bed980f8bac8ffac54afdf3526bc5",
|
|
60
60
|
"schema/promotion-request.schema.json": "sha256:00e96dfd8ddbdba88ea222941d43f3609ff6c815a79e34f6705cab458101c969",
|
|
61
61
|
"schema/promotion-response.schema.json": "sha256:4129712d2e29d6ef21d93d4bd0bacfd60ada3f2ee0ade852824e64346b817634",
|
|
62
|
-
"schema/saas-connection.schema.json": "sha256:
|
|
62
|
+
"schema/saas-connection.schema.json": "sha256:1fcb1a3ee922e49d8f846db6bd6284f6de483b26cb1ae03da0381b396bb8132d",
|
|
63
63
|
"schema/sanitized-binding-evidence.schema.json": "sha256:1191ab88e2764df927e5f17837674cebebe068d480da2b2fdb390e589f96b51f",
|
|
64
64
|
"schema/secret-reference.schema.json": "sha256:db4e3c2c6a2dbd72d7ea0f9c91cb3b2343a7e67b601996c6818b2ef08e440895",
|
|
65
65
|
"schema/security-scan-input.schema.json": "sha256:83502dffb3c07ac2e0698928e6dc123f454fc667d4aacd336bcad6944ef2ac3d",
|
|
66
66
|
"schema/security-verdict.schema.json": "sha256:ec3c2d5c9df3601aeed0909b36c7f427469c7f0eeb3295c5809b96d09bf3fc26",
|
|
67
67
|
"schema/transform-parked-result.schema.json": "sha256:11729980d4c89d4a9e18f5f213232cd3359739c85626b36c20c2a37ba794c9af",
|
|
68
|
-
"schema/transform-report.schema.json": "sha256:
|
|
68
|
+
"schema/transform-report.schema.json": "sha256:62c8f3aa3ac748cb54d78cb729bdfbaaccf1c03574ea92841eb23b2557eff3cf",
|
|
69
69
|
"schema/version-state.schema.json": "sha256:3b190007e2f8e3821c41ef11c6f5b226da7d5a18d34d4077f4618004846fd444",
|
|
70
70
|
"schema/well-defined-app-manifest.schema.json": "sha256:40f1bfea912dd85a05a0a3be2f6becadaf8ae3c5de9d5782ff1c8dd1bd83485f"
|
|
71
71
|
},
|
|
@@ -105,8 +105,10 @@
|
|
|
105
105
|
"gen-go/contracts_test.go": "sha256:56376287cb1548be1b35ab4c65d6f0b558c5d34885f7a47cb3d9e8d786607bf5",
|
|
106
106
|
"gen-go/doc.go": "sha256:c17bc2d39ed175ce7105c68305f8ae7152f659eb7cd187ecceee7698eb3a65dd",
|
|
107
107
|
"gen-go/error_category.go": "sha256:b2ddea22dcfa030f1a45e721e635485b0bd816cf1f38e7654628a653f5ed4583",
|
|
108
|
-
"gen-go/
|
|
109
|
-
"gen-go/
|
|
108
|
+
"gen-go/integration_execute_input_rules.go": "sha256:224fb490a5f936b30927e6637e698093e1e42a695cbc1bcf9a39bc70db9f2172",
|
|
109
|
+
"gen-go/integration_execute_input_rules_test.go": "sha256:c437a8406a2f062a0173b9419e4cb176fc6eb54e569506225d5162323f55b40b",
|
|
110
|
+
"gen-go/schema_documents.go": "sha256:a01b157658d23e2585e64ff101ed720942856626676b50c83efbd8b044a050d6",
|
|
111
|
+
"gen-go/types.go": "sha256:819a27018a274c83233993a0c54359219ebbd0b11523f68c3c34ba0223d9e5ba",
|
|
110
112
|
"gen-go/version_state.go": "sha256:4ef8abb126414c0025cf2c59bc9c79a74ef4a2ff89e8f316cbf05acc872371c1"
|
|
111
113
|
},
|
|
112
114
|
"generatedTypeScript": {
|
|
@@ -131,13 +133,13 @@
|
|
|
131
133
|
"gen-ts/ai-transform-result.ts": "sha256:208c66b2aeb425c0cbcc18b7191f6e8989443f2b2c1690648e0e06cc1448ce25",
|
|
132
134
|
"gen-ts/ai-verification-evidence.ts": "sha256:979439d77f8b47631d1eb3b6c3c54ea6fec41089d2a51dc815fd370b1c4917fc",
|
|
133
135
|
"gen-ts/api-error-response.ts": "sha256:0fd73be282e7a5d72d6c343a4e64d09c232556b16161167f0cffd6278ed7e3d3",
|
|
134
|
-
"gen-ts/app-integrations-declaration.ts": "sha256:
|
|
136
|
+
"gen-ts/app-integrations-declaration.ts": "sha256:c2edea82c2c80d15dc233499ef289ea310e59b61b1e4428ea9e4fed85168d2a4",
|
|
135
137
|
"gen-ts/artifact-contract.ts": "sha256:4a6dc6d2caaeed4bb2066cc8a333e6e09e5a0d4745e0b1038293d54ab9403aeb",
|
|
136
138
|
"gen-ts/binding-classification-digest-helper.ts": "sha256:40fc7d85b4c3f3ffc5caf7d7950c62d013c83ee42058c7453b2100d95817f167",
|
|
137
139
|
"gen-ts/binding-classification-subject.ts": "sha256:c409a3fd7b942b50419632c51d9a79474fb30d6374bdc79c0e25c73618e800c3",
|
|
138
140
|
"gen-ts/builder-input-proposal.ts": "sha256:b0108c3787873d3cf1a39f00967379418a6766275d3474cfa55f5a9d93ead174",
|
|
139
141
|
"gen-ts/data-dependency.ts": "sha256:0572c31d7ae9088eccc104eee1a205bf176d1d6dacfaf0f290d666024e284966",
|
|
140
|
-
"gen-ts/data-grant-v2.ts": "sha256:
|
|
142
|
+
"gen-ts/data-grant-v2.ts": "sha256:d7c78866dfbad6c852bdbf470057a7b926edcadf5563e6ea4da3da9ae96cf699",
|
|
141
143
|
"gen-ts/data-grant.ts": "sha256:2358579d8aa1e57b01a6f2b0eacce5531e7f21ef226404c9809925365e0d6155",
|
|
142
144
|
"gen-ts/data-source.ts": "sha256:77d6bb3b0eaa355789c5d97dd1f548734add6950329dc69549350b8d17c8d582",
|
|
143
145
|
"gen-ts/deploy-request.ts": "sha256:0214faa8be6680f0bfc117d5ce67c1a59fcc18bd0ea4bc840e03e7d051fa9ab6",
|
|
@@ -152,24 +154,25 @@
|
|
|
152
154
|
"gen-ts/error-category-registry.ts": "sha256:7466a502c0723a77db825cdeaa9556a244201b55d9b7acbf2c098e9e84351df1",
|
|
153
155
|
"gen-ts/error-category.ts": "sha256:9bda89180c3b740c9aa8be466216592e8ca9d0d368c274333edf6ea5c45afd8e",
|
|
154
156
|
"gen-ts/error-envelope.ts": "sha256:4457309be794f9d4fc0cf62530cc2d442c007bac9ae84aff66df099cc9e40310",
|
|
155
|
-
"gen-ts/index.ts": "sha256:
|
|
157
|
+
"gen-ts/index.ts": "sha256:1fa78bbfd85f29f777f9018d110b70388af35ab2e428c07f75f6701ff3eaba22",
|
|
156
158
|
"gen-ts/integration-connect-result.ts": "sha256:4b10f169b6e1af364e9b418de21caa9fadbfb77b43cbff77d741cfae0b52180c",
|
|
157
159
|
"gen-ts/integration-error-details.ts": "sha256:8243f5871f376d1cab4d90e07f3151d148fb03c704168dbccdfe6c9fe76bce83",
|
|
158
|
-
"gen-ts/integration-execute-
|
|
160
|
+
"gen-ts/integration-execute-input-rules.ts": "sha256:a779c872c44bb892dec7b28173d961696d95e5b6e1fa14e2ba67dd78344cdc5b",
|
|
161
|
+
"gen-ts/integration-execute-request.ts": "sha256:b80260f8cca8da13692a6e0fa5de62b82c6eb48b0f3f3664f44f086441a519db",
|
|
159
162
|
"gen-ts/integration-execute-result.ts": "sha256:4aaf6a9688f9dfdbc87a408556b62f822518acf5b76548076adb55393c0863b4",
|
|
160
163
|
"gen-ts/kit-bundle-manifest.ts": "sha256:9041b32af3df781ca6800bc14c3719e5e54c81dff370633454ac3ad27432eb58",
|
|
161
164
|
"gen-ts/kit-lock.ts": "sha256:de4ae82bac7bd234a3230ae671962e80eed6542d695a032a442673659dc6be5e",
|
|
162
165
|
"gen-ts/kvs-routing-entry.ts": "sha256:d92e85ee9f257d77850d1d3aac9438e2055f38084ff84a48fcedae239d47d4ca",
|
|
163
166
|
"gen-ts/promotion-request.ts": "sha256:9db88cef67f777a16bac628039ec3f582acb9e8cfff6fb592b30d0a0e55abc68",
|
|
164
167
|
"gen-ts/promotion-response.ts": "sha256:f1c9cafac96faeb02a443a31786f67714e818f38d90108e7af41958396524d6f",
|
|
165
|
-
"gen-ts/saas-connection.ts": "sha256:
|
|
168
|
+
"gen-ts/saas-connection.ts": "sha256:b60ab802c73edfe805d59d55a70ecd54fe569646d238f5ff213d26ab34b717f1",
|
|
166
169
|
"gen-ts/sanitized-binding-digest-helper.ts": "sha256:c47d7fdc20500e2e72c23fe39ecdd610fc397c47fcba51335ebec3c380fe6872",
|
|
167
170
|
"gen-ts/sanitized-binding-evidence.ts": "sha256:f3fb7b1d7d2e0929eb0ea981eaf32db3104010647185f6ae59f67167d42538a9",
|
|
168
171
|
"gen-ts/secret-reference.ts": "sha256:731fbcde7315cb4128d5b01bdbe6e6f248f13c7ad8dea04a3a4e927ac4890198",
|
|
169
172
|
"gen-ts/security-scan-input.ts": "sha256:b31f048be04c34b40f49d3b3ba53277e473cd435ae9216ba3a3b4dac69fb5685",
|
|
170
173
|
"gen-ts/security-verdict.ts": "sha256:fea950cddbd12fe0218e2573c9a29c6748d56612a464559f1e6d688fa97f68c7",
|
|
171
174
|
"gen-ts/transform-parked-result.ts": "sha256:6279e27cee8d75c29f3ede45580fef1daa0ca300867a78e43ead8a7689ec613f",
|
|
172
|
-
"gen-ts/transform-report.ts": "sha256:
|
|
175
|
+
"gen-ts/transform-report.ts": "sha256:2f42c94644b0f4e2d03e339d0b9d333254555746f74d362d820522156e964ef9",
|
|
173
176
|
"gen-ts/version-state-machine.ts": "sha256:c67af3e86ac306743345b850dfd3ae5201c19d10c40e2c149cf8c38258320d5f",
|
|
174
177
|
"gen-ts/version-state.ts": "sha256:a835112930299e7b593a1b8810b7414f6c9e809da2092306c743128170a3b702",
|
|
175
178
|
"gen-ts/well-defined-app-manifest.ts": "sha256:2b72a39c2fa8e76239634d0478dc0da05446e74e944950eacf999b78876455c6"
|
|
@@ -8,12 +8,16 @@ export type IntegrationAlias = string;
|
|
|
8
8
|
* @maxItems 64
|
|
9
9
|
*/
|
|
10
10
|
export type ResourceNames = IntegrationAlias[];
|
|
11
|
+
/**
|
|
12
|
+
* Whose authority an operation runs under. Fixed per operation (slack.message.post: declared per app); never chosen at call time.
|
|
13
|
+
*/
|
|
14
|
+
export type IntegrationIdentityMode = 'app' | 'user';
|
|
11
15
|
/**
|
|
12
16
|
* A plain column identifier of a warehouse view.
|
|
13
17
|
*/
|
|
14
18
|
export type ColumnName = string;
|
|
15
19
|
/**
|
|
16
|
-
* Committed `.harbour/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (
|
|
20
|
+
* Committed `.harbour/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (six operations). Each operation carries a fixed identity mode — slack.channel.history=user, gmail.thread.list=user, gmail.message.read=user, gmail.message.send=user, warehouse.view.read=app — except slack.message.post, whose mode the app DECLARES: `app` posts as the company's Slack app (the one audited bot identity, under the IT-approved per-app display name when the connection declares `presentation`), `user` posts as the signed-in person after their own consent. The mode is never chosen at call time: governance runs the call under the approved grant of the declared mode. A `saas` connection may declare only the Slack and Gmail operations and a `database` connection only warehouse.view.read; `presentation` is a `saas` connection's optional per-app display name and icon for app-mode Slack posts, shown to IT at approval time and applied only from the approved grant. Gmail is user-mode only (reads plus one send); its single resource is the fixed `inbox` alias. This file also owns the shared IntegrationOperation / IntegrationIdentityMode / IntegrationEnvironment / IntegrationConnectionKind / IntegrationPresentation definitions referenced by the other integration schemas.
|
|
17
21
|
*/
|
|
18
22
|
export interface AppIntegrationsDeclaration {
|
|
19
23
|
schema: 'harbour.app-integrations/2.0';
|
|
@@ -23,21 +27,38 @@ export interface AppIntegrationsDeclaration {
|
|
|
23
27
|
}
|
|
24
28
|
export interface DeclaredConnection {
|
|
25
29
|
kind: IntegrationConnectionKind;
|
|
30
|
+
presentation?: IntegrationPresentation;
|
|
26
31
|
operations: {
|
|
27
32
|
'slack.channel.history'?: UserModeChannelOperation;
|
|
28
|
-
'slack.message.post'?:
|
|
33
|
+
'slack.message.post'?: DeclaredModeChannelOperation;
|
|
29
34
|
'gmail.thread.list'?: UserModeChannelOperation;
|
|
30
35
|
'gmail.message.read'?: UserModeChannelOperation;
|
|
31
36
|
'gmail.message.send'?: UserModeChannelOperation;
|
|
32
37
|
'warehouse.view.read'?: AppModeViewOperation;
|
|
33
38
|
};
|
|
34
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* saas connections only (Slack): how this app's app-mode posts are presented; meaningless for every other operation.
|
|
42
|
+
*/
|
|
43
|
+
export interface IntegrationPresentation {
|
|
44
|
+
/**
|
|
45
|
+
* 1..80 characters of plain text: not blank, no control characters, no @-mentions (`@name`, `<@id>`). Must not impersonate a person: IT sees it before approving.
|
|
46
|
+
*/
|
|
47
|
+
displayName: string;
|
|
48
|
+
/**
|
|
49
|
+
* A Slack emoji shortcode such as `:sandwich:`; absent keeps the bot's own icon.
|
|
50
|
+
*/
|
|
51
|
+
iconEmoji?: string;
|
|
52
|
+
}
|
|
35
53
|
export interface UserModeChannelOperation {
|
|
36
54
|
identity: 'user';
|
|
37
55
|
resources: ResourceNames;
|
|
38
56
|
}
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
/**
|
|
58
|
+
* slack.message.post: the app declares the mode — app (posts as the company's Slack app) or user (posts as the signed-in person).
|
|
59
|
+
*/
|
|
60
|
+
export interface DeclaredModeChannelOperation {
|
|
61
|
+
identity: IntegrationIdentityMode;
|
|
41
62
|
resources: ResourceNames;
|
|
42
63
|
}
|
|
43
64
|
export interface AppModeViewOperation {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare const ContractRepositoryVersion: "0.
|
|
2
|
-
export declare const ContractPublishVersion: "0.
|
|
3
|
-
export declare const ContractPublishTag: "v0.
|
|
4
|
-
export declare const ContractSchemasDigest: "sha256:
|
|
1
|
+
export declare const ContractRepositoryVersion: "0.53.0.0";
|
|
2
|
+
export declare const ContractPublishVersion: "0.53.0";
|
|
3
|
+
export declare const ContractPublishTag: "v0.53.0";
|
|
4
|
+
export declare const ContractSchemasDigest: "sha256:c4d7c9642842bf3709003f447fb22429ce3573acabaa2da8279e0fc399e8d237";
|
|
5
5
|
export declare const ContractAiFixturesDigest: "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
|
|
6
|
-
export declare const ContractGeneratedGoDigest: "sha256:
|
|
7
|
-
export declare const ContractGeneratedTypeScriptDigest: "sha256:
|
|
6
|
+
export declare const ContractGeneratedGoDigest: "sha256:8f3c3281a76792a9480ca1f7bcbad820db1908efffb29072fcdb0a8f99dbd208";
|
|
7
|
+
export declare const ContractGeneratedTypeScriptDigest: "sha256:82a083862e500a06df3550e694221cff9dcb98461d5174889c374713f961040c";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
-
export const ContractRepositoryVersion = "0.
|
|
4
|
-
export const ContractPublishVersion = "0.
|
|
5
|
-
export const ContractPublishTag = "v0.
|
|
6
|
-
export const ContractSchemasDigest = "sha256:
|
|
3
|
+
export const ContractRepositoryVersion = "0.53.0.0";
|
|
4
|
+
export const ContractPublishVersion = "0.53.0";
|
|
5
|
+
export const ContractPublishTag = "v0.53.0";
|
|
6
|
+
export const ContractSchemasDigest = "sha256:c4d7c9642842bf3709003f447fb22429ce3573acabaa2da8279e0fc399e8d237";
|
|
7
7
|
export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
|
|
8
|
-
export const ContractGeneratedGoDigest = "sha256:
|
|
9
|
-
export const ContractGeneratedTypeScriptDigest = "sha256:
|
|
8
|
+
export const ContractGeneratedGoDigest = "sha256:8f3c3281a76792a9480ca1f7bcbad820db1908efffb29072fcdb0a8f99dbd208";
|
|
9
|
+
export const ContractGeneratedTypeScriptDigest = "sha256:82a083862e500a06df3550e694221cff9dcb98461d5174889c374713f961040c";
|
|
@@ -8,7 +8,7 @@ export type DataGrantV2Status = 'REQUESTED' | 'GRANTED' | 'DENIED' | 'REVOKED';
|
|
|
8
8
|
export type DataGrantV2ProvisioningStatus = 'NONE' | 'PENDING' | 'FAILED' | 'DONE';
|
|
9
9
|
export type DataGrantV2CleanupStatus = 'PENDING' | 'FAILED' | 'DONE';
|
|
10
10
|
/**
|
|
11
|
-
* An app's integration grant for one connection, environment and identity mode (control table sk APP#<appId>#IGRANT#<environment>#<sourceId>#<mode>; grantId = base64url(JSON ["v2",appId,environment,sourceId,mode])). Additive to the legacy DataGrant (data-grant.schema.json), which is never consulted by v2 paths. A fresh REQUESTED record carries empty `operations`/`resources` until the attached request is approved; approved resources are structured `{name, columns?}` entries (a Slack channel alias, the fixed Gmail `inbox` alias, or a warehouse view alias with its approved columns). Expiry is enforced from UTC time on every call and SDK calls never extend it; `expiredAt` is stamped by the recovery sweep afterwards. Status edges live in x-transitions; DENIED and REVOKED are terminal (a regrant after revocation is a new REQUESTED record once cleanup is DONE). Database grants add credentialRef/mintedPrincipal; both hold references only. `identityMode` and `installation` are the deprecated 0.45.0 spellings: the record carries `mode`, and a SaaS grant runs on the connection's single installation.
|
|
11
|
+
* An app's integration grant for one connection, environment and identity mode (control table sk APP#<appId>#IGRANT#<environment>#<sourceId>#<mode>; grantId = base64url(JSON ["v2",appId,environment,sourceId,mode])). Additive to the legacy DataGrant (data-grant.schema.json), which is never consulted by v2 paths. A fresh REQUESTED record carries empty `operations`/`resources` until the attached request is approved; approved resources are structured `{name, columns?}` entries (a Slack channel alias, the fixed Gmail `inbox` alias, or a warehouse view alias with its approved columns). Expiry is enforced from UTC time on every call and SDK calls never extend it; `expiredAt` is stamped by the recovery sweep afterwards. Status edges live in x-transitions; DENIED and REVOKED are terminal (a regrant after revocation is a new REQUESTED record once cleanup is DONE). Database grants add credentialRef/mintedPrincipal; both hold references only. An app-mode Slack grant may carry `presentation` (0.53.0): the IT-approved display name and icon the app's posts go out under, copied from the approved request and read by every post — never from the call. `identityMode` and `installation` are the deprecated 0.45.0 spellings: the record carries `mode`, and a SaaS grant runs on the connection's single installation.
|
|
12
12
|
*/
|
|
13
13
|
export interface DataGrantV2 {
|
|
14
14
|
schema: 'harbour.data-grant/2.0';
|
|
@@ -23,7 +23,7 @@ export interface DataGrantV2 {
|
|
|
23
23
|
*/
|
|
24
24
|
sourceId: string;
|
|
25
25
|
/**
|
|
26
|
-
* Whose authority an operation runs under. Fixed per operation;
|
|
26
|
+
* Whose authority an operation runs under. Fixed per operation (slack.message.post: declared per app); never chosen at call time.
|
|
27
27
|
*/
|
|
28
28
|
mode: 'app' | 'user';
|
|
29
29
|
kind: 'saas' | 'database';
|
|
@@ -58,6 +58,7 @@ export interface DataGrantV2 {
|
|
|
58
58
|
* The one open access request attached to this grant, if any.
|
|
59
59
|
*/
|
|
60
60
|
pendingRequestId?: string;
|
|
61
|
+
presentation?: IntegrationPresentation;
|
|
61
62
|
provisioning: DataGrantV2Provisioning;
|
|
62
63
|
/**
|
|
63
64
|
* Database grants: secretref://tenant/{t}/app/{a}/env/{env}/source/{s}. Set only by a successful publish and removed by cleanup.
|
|
@@ -71,7 +72,7 @@ export interface DataGrantV2 {
|
|
|
71
72
|
updatedAt: string;
|
|
72
73
|
/**
|
|
73
74
|
* @deprecated
|
|
74
|
-
* Whose authority an operation runs under. Fixed per operation;
|
|
75
|
+
* Whose authority an operation runs under. Fixed per operation (slack.message.post: declared per app); never chosen at call time.
|
|
75
76
|
*/
|
|
76
77
|
identityMode?: 'app' | 'user';
|
|
77
78
|
/**
|
|
@@ -96,6 +97,19 @@ export interface IntegrationResource {
|
|
|
96
97
|
*/
|
|
97
98
|
columns?: string[];
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* App-mode Slack grants only: how the app's posts are presented, as IT approved it. Absent means the posts carry the bot's own name.
|
|
102
|
+
*/
|
|
103
|
+
interface IntegrationPresentation {
|
|
104
|
+
/**
|
|
105
|
+
* 1..80 characters of plain text: not blank, no control characters, no @-mentions (`@name`, `<@id>`). Must not impersonate a person: IT sees it before approving.
|
|
106
|
+
*/
|
|
107
|
+
displayName: string;
|
|
108
|
+
/**
|
|
109
|
+
* A Slack emoji shortcode such as `:sandwich:`; absent keeps the bot's own icon.
|
|
110
|
+
*/
|
|
111
|
+
iconEmoji?: string;
|
|
112
|
+
}
|
|
99
113
|
export interface DataGrantV2Provisioning {
|
|
100
114
|
status: DataGrantV2ProvisioningStatus;
|
|
101
115
|
/**
|
|
@@ -126,3 +140,4 @@ export interface DataGrantV2Provisioning {
|
|
|
126
140
|
export declare function canTransitionDataGrantV2Status(from: string, to: string): boolean;
|
|
127
141
|
/** True iff the DataGrantV2Status is terminal (DENIED, REVOKED). */
|
|
128
142
|
export declare function isTerminalDataGrantV2Status(state: string): boolean;
|
|
143
|
+
export {};
|
package/dist/gen-ts/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export * from './error-category.js';
|
|
|
43
43
|
export * from './error-envelope.js';
|
|
44
44
|
export * from './integration-connect-result.js';
|
|
45
45
|
export * from './integration-error-details.js';
|
|
46
|
+
export * from './integration-execute-input-rules.js';
|
|
46
47
|
export * from './integration-execute-request.js';
|
|
47
48
|
export * from './integration-execute-result.js';
|
|
48
49
|
export * from './kit-bundle-manifest.js';
|
package/dist/gen-ts/index.js
CHANGED
|
@@ -45,6 +45,7 @@ export * from './error-category.js';
|
|
|
45
45
|
export * from './error-envelope.js';
|
|
46
46
|
export * from './integration-connect-result.js';
|
|
47
47
|
export * from './integration-error-details.js';
|
|
48
|
+
export * from './integration-execute-input-rules.js';
|
|
48
49
|
export * from './integration-execute-request.js';
|
|
49
50
|
export * from './integration-execute-result.js';
|
|
50
51
|
export * from './kit-bundle-manifest.js';
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-operation input rules of integration-execute-request: derived from each
|
|
3
|
+
* operation's then-block (propertyNames.enum, required, per-field overrides).
|
|
4
|
+
* The governance execute route, the app SDK and the kit's local check validate
|
|
5
|
+
* with validateIntegrationExecuteInput below, so a request is refused with the
|
|
6
|
+
* same message wherever it is made.
|
|
7
|
+
*/
|
|
8
|
+
export type IntegrationExecuteInputFieldRule = {
|
|
9
|
+
kind: 'integer';
|
|
10
|
+
required: boolean;
|
|
11
|
+
minimum: number;
|
|
12
|
+
maximum: number;
|
|
13
|
+
default?: number;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'string';
|
|
16
|
+
required: boolean;
|
|
17
|
+
minLength: number;
|
|
18
|
+
maxLength?: number;
|
|
19
|
+
pattern?: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'string[]';
|
|
22
|
+
required: boolean;
|
|
23
|
+
minItems: number;
|
|
24
|
+
maxItems: number;
|
|
25
|
+
uniqueItems: boolean;
|
|
26
|
+
item: {
|
|
27
|
+
minLength: number;
|
|
28
|
+
maxLength?: number;
|
|
29
|
+
pattern?: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export type IntegrationExecuteOperationRule = {
|
|
33
|
+
idempotencyKey: boolean;
|
|
34
|
+
fields: Record<string, IntegrationExecuteInputFieldRule>;
|
|
35
|
+
};
|
|
36
|
+
export declare const IntegrationExecuteInputRules: {
|
|
37
|
+
readonly "slack.channel.history": {
|
|
38
|
+
readonly idempotencyKey: false;
|
|
39
|
+
readonly fields: {
|
|
40
|
+
readonly limit: {
|
|
41
|
+
readonly kind: "integer";
|
|
42
|
+
readonly required: false;
|
|
43
|
+
readonly minimum: 1;
|
|
44
|
+
readonly maximum: 15;
|
|
45
|
+
readonly default: 15;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
readonly "slack.message.post": {
|
|
50
|
+
readonly idempotencyKey: true;
|
|
51
|
+
readonly fields: {
|
|
52
|
+
readonly text: {
|
|
53
|
+
readonly kind: "string";
|
|
54
|
+
readonly required: true;
|
|
55
|
+
readonly minLength: 1;
|
|
56
|
+
readonly maxLength: 4000;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
readonly "gmail.thread.list": {
|
|
61
|
+
readonly idempotencyKey: false;
|
|
62
|
+
readonly fields: {
|
|
63
|
+
readonly limit: {
|
|
64
|
+
readonly kind: "integer";
|
|
65
|
+
readonly required: false;
|
|
66
|
+
readonly minimum: 1;
|
|
67
|
+
readonly maximum: 15;
|
|
68
|
+
readonly default: 15;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
readonly "gmail.message.read": {
|
|
73
|
+
readonly idempotencyKey: false;
|
|
74
|
+
readonly fields: {
|
|
75
|
+
readonly messageId: {
|
|
76
|
+
readonly kind: "string";
|
|
77
|
+
readonly required: true;
|
|
78
|
+
readonly minLength: 0;
|
|
79
|
+
readonly pattern: "^[A-Za-z0-9_-]{1,64}$";
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
readonly "gmail.message.send": {
|
|
84
|
+
readonly idempotencyKey: true;
|
|
85
|
+
readonly fields: {
|
|
86
|
+
readonly to: {
|
|
87
|
+
readonly kind: "string[]";
|
|
88
|
+
readonly required: true;
|
|
89
|
+
readonly minItems: 1;
|
|
90
|
+
readonly maxItems: 50;
|
|
91
|
+
readonly uniqueItems: true;
|
|
92
|
+
readonly item: {
|
|
93
|
+
readonly minLength: 3;
|
|
94
|
+
readonly maxLength: 254;
|
|
95
|
+
readonly pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$";
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
readonly cc: {
|
|
99
|
+
readonly kind: "string[]";
|
|
100
|
+
readonly required: false;
|
|
101
|
+
readonly minItems: 1;
|
|
102
|
+
readonly maxItems: 50;
|
|
103
|
+
readonly uniqueItems: true;
|
|
104
|
+
readonly item: {
|
|
105
|
+
readonly minLength: 3;
|
|
106
|
+
readonly maxLength: 254;
|
|
107
|
+
readonly pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$";
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
readonly bcc: {
|
|
111
|
+
readonly kind: "string[]";
|
|
112
|
+
readonly required: false;
|
|
113
|
+
readonly minItems: 1;
|
|
114
|
+
readonly maxItems: 50;
|
|
115
|
+
readonly uniqueItems: true;
|
|
116
|
+
readonly item: {
|
|
117
|
+
readonly minLength: 3;
|
|
118
|
+
readonly maxLength: 254;
|
|
119
|
+
readonly pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$";
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
readonly subject: {
|
|
123
|
+
readonly kind: "string";
|
|
124
|
+
readonly required: true;
|
|
125
|
+
readonly minLength: 1;
|
|
126
|
+
readonly maxLength: 256;
|
|
127
|
+
};
|
|
128
|
+
readonly text: {
|
|
129
|
+
readonly kind: "string";
|
|
130
|
+
readonly required: true;
|
|
131
|
+
readonly minLength: 1;
|
|
132
|
+
readonly maxLength: 4000;
|
|
133
|
+
};
|
|
134
|
+
readonly replyToMessageId: {
|
|
135
|
+
readonly kind: "string";
|
|
136
|
+
readonly required: false;
|
|
137
|
+
readonly minLength: 0;
|
|
138
|
+
readonly pattern: "^[A-Za-z0-9_-]{1,64}$";
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
readonly "warehouse.view.read": {
|
|
143
|
+
readonly idempotencyKey: false;
|
|
144
|
+
readonly fields: {
|
|
145
|
+
readonly columns: {
|
|
146
|
+
readonly kind: "string[]";
|
|
147
|
+
readonly required: true;
|
|
148
|
+
readonly minItems: 1;
|
|
149
|
+
readonly maxItems: 32;
|
|
150
|
+
readonly uniqueItems: false;
|
|
151
|
+
readonly item: {
|
|
152
|
+
readonly minLength: 0;
|
|
153
|
+
readonly pattern: "^[A-Za-z_][A-Za-z0-9_]{0,62}$";
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
readonly limit: {
|
|
157
|
+
readonly kind: "integer";
|
|
158
|
+
readonly required: false;
|
|
159
|
+
readonly minimum: 1;
|
|
160
|
+
readonly maximum: 1000;
|
|
161
|
+
readonly default: 100;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
export type IntegrationExecuteOperation = keyof typeof IntegrationExecuteInputRules;
|
|
167
|
+
/** The upper bound of one integer input as a literal type: integrationExecuteInputMaximum('slack.channel.history', 'limit') is 15. */
|
|
168
|
+
export declare function integrationExecuteInputMaximum<O extends IntegrationExecuteOperation, F extends keyof (typeof IntegrationExecuteInputRules)[O]['fields']>(operation: O, field: F): Extract<(typeof IntegrationExecuteInputRules)[O]['fields'][F], {
|
|
169
|
+
maximum: number;
|
|
170
|
+
}>['maximum'];
|
|
171
|
+
export type IntegrationExecuteInputProblem = {
|
|
172
|
+
field: string;
|
|
173
|
+
message: string;
|
|
174
|
+
};
|
|
175
|
+
export type IntegrationExecuteInputValidation = {
|
|
176
|
+
ok: true;
|
|
177
|
+
input: Record<string, unknown>;
|
|
178
|
+
} | {
|
|
179
|
+
ok: false;
|
|
180
|
+
problem: IntegrationExecuteInputProblem;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Validates one operation's input against the contract and returns it with the
|
|
184
|
+
* contract's defaults applied. The message names the field and its allowed
|
|
185
|
+
* range; it is the sentence the platform answers with, so a caller may show it.
|
|
186
|
+
*/
|
|
187
|
+
export declare function validateIntegrationExecuteInput(operation: string, input: unknown): IntegrationExecuteInputValidation;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
export const IntegrationExecuteInputRules = {
|
|
4
|
+
"slack.channel.history": {
|
|
5
|
+
idempotencyKey: false,
|
|
6
|
+
fields: {
|
|
7
|
+
limit: { kind: "integer", required: false, minimum: 1, maximum: 15, default: 15 },
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
"slack.message.post": {
|
|
11
|
+
idempotencyKey: true,
|
|
12
|
+
fields: {
|
|
13
|
+
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"gmail.thread.list": {
|
|
17
|
+
idempotencyKey: false,
|
|
18
|
+
fields: {
|
|
19
|
+
limit: { kind: "integer", required: false, minimum: 1, maximum: 15, default: 15 },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
"gmail.message.read": {
|
|
23
|
+
idempotencyKey: false,
|
|
24
|
+
fields: {
|
|
25
|
+
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9_-]{1,64}$" },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
"gmail.message.send": {
|
|
29
|
+
idempotencyKey: true,
|
|
30
|
+
fields: {
|
|
31
|
+
to: { kind: "string[]", required: true, minItems: 1, maxItems: 50, uniqueItems: true, item: { minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" } },
|
|
32
|
+
cc: { kind: "string[]", required: false, minItems: 1, maxItems: 50, uniqueItems: true, item: { minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" } },
|
|
33
|
+
bcc: { kind: "string[]", required: false, minItems: 1, maxItems: 50, uniqueItems: true, item: { minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" } },
|
|
34
|
+
subject: { kind: "string", required: true, minLength: 1, maxLength: 256 },
|
|
35
|
+
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
36
|
+
replyToMessageId: { kind: "string", required: false, minLength: 0, pattern: "^[A-Za-z0-9_-]{1,64}$" },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
"warehouse.view.read": {
|
|
40
|
+
idempotencyKey: false,
|
|
41
|
+
fields: {
|
|
42
|
+
columns: { kind: "string[]", required: true, minItems: 1, maxItems: 32, uniqueItems: false, item: { minLength: 0, pattern: "^[A-Za-z_][A-Za-z0-9_]{0,62}$" } },
|
|
43
|
+
limit: { kind: "integer", required: false, minimum: 1, maximum: 1000, default: 100 },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/** The upper bound of one integer input as a literal type: integrationExecuteInputMaximum('slack.channel.history', 'limit') is 15. */
|
|
48
|
+
export function integrationExecuteInputMaximum(operation, field) {
|
|
49
|
+
const rule = IntegrationExecuteInputRules[operation].fields[field];
|
|
50
|
+
if (!rule || rule.kind !== 'integer')
|
|
51
|
+
throw new Error(`${String(operation)}.input.${String(field)} has no integer bound`);
|
|
52
|
+
return rule.maximum;
|
|
53
|
+
}
|
|
54
|
+
const patterns = new Map();
|
|
55
|
+
function matches(pattern, value) {
|
|
56
|
+
let compiled = patterns.get(pattern);
|
|
57
|
+
if (!compiled) {
|
|
58
|
+
compiled = new RegExp(pattern, 'u');
|
|
59
|
+
patterns.set(pattern, compiled);
|
|
60
|
+
}
|
|
61
|
+
return compiled.test(value);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validates one operation's input against the contract and returns it with the
|
|
65
|
+
* contract's defaults applied. The message names the field and its allowed
|
|
66
|
+
* range; it is the sentence the platform answers with, so a caller may show it.
|
|
67
|
+
*/
|
|
68
|
+
export function validateIntegrationExecuteInput(operation, input) {
|
|
69
|
+
const rule = IntegrationExecuteInputRules[operation];
|
|
70
|
+
if (!rule)
|
|
71
|
+
return { ok: false, problem: { field: 'operation', message: `${operation} is not a declared integration operation` } };
|
|
72
|
+
const raw = input === undefined ? {} : input;
|
|
73
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
|
|
74
|
+
return { ok: false, problem: { field: 'input', message: 'input must be an object' } };
|
|
75
|
+
const given = raw;
|
|
76
|
+
for (const name of Object.keys(given))
|
|
77
|
+
if (!Object.prototype.hasOwnProperty.call(rule.fields, name))
|
|
78
|
+
return { ok: false, problem: { field: name, message: `${name} is not an input of ${operation}` } };
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const [name, field] of Object.entries(rule.fields)) {
|
|
81
|
+
const value = given[name];
|
|
82
|
+
if (value === undefined || value === null) {
|
|
83
|
+
if (field.required)
|
|
84
|
+
return { ok: false, problem: { field: name, message: `${name} is required for ${operation}` } };
|
|
85
|
+
if (field.kind === 'integer' && field.default !== undefined)
|
|
86
|
+
out[name] = field.default;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (field.kind === 'integer') {
|
|
90
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < field.minimum || value > field.maximum)
|
|
91
|
+
return { ok: false, problem: { field: name, message: `${name} must be an integer between ${field.minimum} and ${field.maximum}` } };
|
|
92
|
+
}
|
|
93
|
+
else if (field.kind === 'string') {
|
|
94
|
+
if (typeof value !== 'string' || value.length < field.minLength || (field.maxLength !== undefined && value.length > field.maxLength))
|
|
95
|
+
return { ok: false, problem: { field: name, message: field.maxLength !== undefined ? `${name} must be ${field.minLength} to ${field.maxLength} characters` : `${name} must be a string of at least ${field.minLength} characters` } };
|
|
96
|
+
if (field.pattern && !matches(field.pattern, value))
|
|
97
|
+
return { ok: false, problem: { field: name, message: `${name} must match ${field.pattern}` } };
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
if (!Array.isArray(value) || value.length < field.minItems || value.length > field.maxItems)
|
|
101
|
+
return { ok: false, problem: { field: name, message: `${name} must list ${field.minItems} to ${field.maxItems} items` } };
|
|
102
|
+
const seen = new Set();
|
|
103
|
+
for (const [index, item] of value.entries()) {
|
|
104
|
+
if (typeof item !== 'string' || item.length < field.item.minLength || (field.item.maxLength !== undefined && item.length > field.item.maxLength) || (field.item.pattern !== undefined && !matches(field.item.pattern, item)))
|
|
105
|
+
return { ok: false, problem: { field: name, message: `${name}[${index}] is not a valid ${name} entry` } };
|
|
106
|
+
if (field.uniqueItems && seen.has(item))
|
|
107
|
+
return { ok: false, problem: { field: name, message: `${name} must not repeat an entry` } };
|
|
108
|
+
seen.add(item);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
out[name] = value;
|
|
112
|
+
}
|
|
113
|
+
return { ok: true, input: out };
|
|
114
|
+
}
|