@fourier-labs/harbour-contracts 0.47.0 → 0.51.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 +35 -3
- package/contract-release.json +25 -22
- package/dist/gen-ts/app-integrations-declaration.d.ts +1 -0
- 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 +1 -1
- 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 +46 -4
- package/dist/gen-ts/integration-execute-result.d.ts +1 -1
- package/dist/gen-ts/saas-connection.d.ts +3 -3
- package/dist/gen-ts/transform-report.d.ts +2 -0
- package/dist/gen-ts/well-defined-app-manifest.d.ts +31 -0
- package/gen-ts/app-integrations-declaration.ts +1 -0
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/data-grant-v2.ts +6 -1
- package/gen-ts/index.ts +1 -0
- package/gen-ts/integration-execute-input-rules.ts +119 -0
- package/gen-ts/integration-execute-request.ts +51 -4
- package/gen-ts/integration-execute-result.ts +1 -1
- package/gen-ts/saas-connection.ts +8 -3
- package/gen-ts/transform-report.ts +2 -0
- package/gen-ts/well-defined-app-manifest.ts +31 -0
- package/package.json +1 -1
- package/schema/app-integrations-declaration.schema.json +6 -4
- package/schema/integration-execute-request.schema.json +35 -10
- package/schema/integration-execute-result.schema.json +3 -1
- package/schema/saas-connection.schema.json +3 -3
- package/schema/transform-report.schema.json +2 -1
- package/schema/well-defined-app-manifest.schema.json +61 -0
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ exist here first.
|
|
|
25
25
|
| `error-envelope.schema.json` | The failure envelope emitted once per FAILED transition | §2.1 |
|
|
26
26
|
| `error-category.enum.json` | Closed failure-category enum + per-category `retryable` registry | §2.2 |
|
|
27
27
|
| `artifact-contract.schema.json` | Build output contract (the provider-portability seam), including the digest-pinned well-defined app manifest reference | §3 |
|
|
28
|
-
| `well-defined-app-manifest.schema.json` | Versioned runtime wiring for static, full-stack, and browser-first apps: workloads, schedules, queues,
|
|
28
|
+
| `well-defined-app-manifest.schema.json` | Versioned runtime wiring for static, full-stack, and browser-first apps: workloads, schedules, queues, service bindings, language runtime, browser capabilities, and route authentication | Well-defined apps P0 |
|
|
29
29
|
| `version-state.schema.json` | State machine T1a — states, legal transitions, versionId format | §1 |
|
|
30
30
|
| `env-bindings.schema.json` | `EnvBindings` / `Target` / `Binding` (BYOC-complete) | §4.2.2 |
|
|
31
31
|
| `kvs-routing-entry.schema.json` | CloudFront KVS routing value (`{static, api}`) | §8 |
|
|
@@ -42,6 +42,32 @@ exist here first.
|
|
|
42
42
|
| `integration-execute-request`, `integration-execute-result`, `integration-connect-result`, `integration-error-details` | App SDK integrations wire contract on `/_harbour/integrations/*`: per-operation input bounds, the five result shapes, consent states and the closed `details.code` reason set | Dev kit §4, §8 |
|
|
43
43
|
| `saas-connection.schema.json`, `data-grant-v2.schema.json` | Tenant SaaS connection record (one installation and channel map per tenant and provider) and the additive `harbour.data-grant/2.0` grant record with structured resources and provisioning lease/cleanup (status machine generated; legacy `data-grant` untouched) | Dev kit §5 |
|
|
44
44
|
| `kit-bundle-manifest.schema.json`, `kit-lock.schema.json` | Published kit bundle identity (SDK tarball digest plus optional `sdk.version` and https `sdk.url` download location) and the committed `.harbour/kit.lock.json` the CodeBuild SDK-vendoring gate compares against | Dev kit §3 |
|
|
45
|
+
| `deploy-response.schema.json` | 202 Accepted body for `POST /v1/tenants/{tenant}/apps/{app}/deployments`, returned only after the authoritative QUEUED version exists and its SQS message is accepted | — |
|
|
46
|
+
| `deployment-source.schema.json` | Credential-free GitHub source in the `DeployRequest` body; credentials are resolved internally by MaterializeSource | — |
|
|
47
|
+
| `deployment-environment.schema.json` | Public environment accepted by the preview APIs (`preview` only; production is reachable only through promotion) | — |
|
|
48
|
+
| `api-error-response.schema.json` | Safe JSON error body for Deploy/Status Lambda 4xx/5xx responses; distinct from the internal `ErrorEnvelope` | — |
|
|
49
|
+
| `deployment-status-request.schema.json` | Validated path parameters for `GET /v1/tenants/{tenant}/apps/{app}/deployments/{versionId}` | — |
|
|
50
|
+
| `deployment-status-response.schema.json` | 200 OK body for GET status: that exact immutable version, with the `progress` trail, `deployTarget` and `diagnosticRef` | — |
|
|
51
|
+
| `deployment-status-failure.schema.json` | Frontend-safe failure projection nested in a failed status response; a digest-bound `diagnosticRef` locates the redacted diagnostic | — |
|
|
52
|
+
| `promotion-request.schema.json` | Body for `POST /v1/tenants/{tenant}/apps/{app}/promotions`; repository identity comes from stored records, not the caller | ADR 0003 |
|
|
53
|
+
| `promotion-response.schema.json` | 202 Accepted body returned once the production version and its durable idempotency binding exist | ADR 0003 |
|
|
54
|
+
| `transform-report.schema.json` | Structured output an agent must return from one transformation attempt; handed to the agent as its output schema and re-validated by the control plane | — |
|
|
55
|
+
| `transform-parked-result.schema.json` | Sequential-preview result after a durable checkpoint when builder input is required; not a successful report | — |
|
|
56
|
+
| `builder-input-proposal.schema.json` | Value-free proposal emitted when sequential preview cannot continue without builder-owned input | — |
|
|
57
|
+
| `deployment-continuation.schema.json` | Immutable reference admitting one child deployment from a parked input request; never credential values | — |
|
|
58
|
+
| `security-scan-input.schema.json` | Input to the Security Assurance machine: SOURCE (M1) scans an ingested commit, ARTIFACT (M2) scans built outputs | Security Assurance §2 |
|
|
59
|
+
| `security-verdict.schema.json` | Security Assurance decision; `BLOCKED` surfaces as the non-retryable `gate.security-scan` category | Security Assurance §2 |
|
|
60
|
+
| `data-source.schema.json` | Admin-registered external data source; `credentialRef` is a secretref URI and the credential never crosses this boundary | Data-Access MVP |
|
|
61
|
+
| `data-dependency.schema.json` | One external data dependency of an app, declared or detected, classified against the tenant's data-source inventory | Data-Access MVP |
|
|
62
|
+
| `secret-reference.schema.json` | Named secret slot an app depends on; only the reference and its lifecycle cross, never the value | Secret-Manager MVP |
|
|
63
|
+
| `ai-callsite-inventory.schema.json` | Immutable, sanitized AI call-site discovery for one exact deployment version (detection only) | AI governance |
|
|
64
|
+
| `ai-runtime-contract.schema.json` | Immutable runtime contract derived by an AI Governance PLAN decision; scoped configuration plus a Secrets Manager reference | AI governance |
|
|
65
|
+
| `ai-policy-snapshot.schema.json` | Immutable, content-addressed AI policy snapshot consumed by the AI Gateway | AI governance |
|
|
66
|
+
| `ai-gateway-key-lookup.schema.json` | Value-free Gateway authentication metadata for one version-scoped Harbour key | AI governance |
|
|
67
|
+
| `ai-trace-event.schema.json` | Value-free asynchronous AI Gateway trace metadata (v1); raw prompts and responses never enter the queue | AI governance |
|
|
68
|
+
| `ai-transform-event.schema.json` | Checksum-bound deterministic AI interception task input carrying the ordered PLAN interception plan | AI governance |
|
|
69
|
+
| `ai-transform-result.schema.json` | Bounded deterministic interception result: descriptors and digest evidence only | AI governance |
|
|
70
|
+
| `ai-verification-evidence.schema.json` | Bounded, value-free evidence submitted by deployment verification for one governed version | AI governance |
|
|
45
71
|
|
|
46
72
|
Runtime resource profiles are a closed contract shared by transformation, artifacts, and
|
|
47
73
|
well-defined application manifests. The supported profiles are `standard`, `memory-1g`, and
|
|
@@ -58,6 +84,12 @@ Two schemas carry logic beyond structural types, generated into helpers:
|
|
|
58
84
|
- **`binding-classification-subject.schema.json`** and **`sanitized-binding-evidence.schema.json`**
|
|
59
85
|
→ canonical length-prefixed SHA-256 preimage/digest helpers plus exact-name,
|
|
60
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.
|
|
61
93
|
|
|
62
94
|
## Workflow
|
|
63
95
|
|
|
@@ -111,7 +143,7 @@ consumer cutover evidence.
|
|
|
111
143
|
|
|
112
144
|
## Consuming
|
|
113
145
|
|
|
114
|
-
- **Go:** `go get github.com/Fourier-Labs-AI/harbour-contracts/gen-go@v0.
|
|
146
|
+
- **Go:** `go get github.com/Fourier-Labs-AI/harbour-contracts/gen-go@v0.50.0`
|
|
115
147
|
|
|
116
148
|
Two things make this work, and the second is not the obvious one.
|
|
117
149
|
|
|
@@ -148,7 +180,7 @@ consumer cutover evidence.
|
|
|
148
180
|
Without the above, `go get` fails with `Repository not found` — which reads as a
|
|
149
181
|
missing tag, not as missing access.
|
|
150
182
|
|
|
151
|
-
- **TS:** `npm i @fourier-labs/harbour-contracts` — published from the tag by CI.
|
|
183
|
+
- **TS:** `npm i @fourier-labs/harbour-contracts` — published from the tag by CI. The hand-written AI policy resolver (`resolver/index.ts`) ships in the same package as the `@fourier-labs/harbour-contracts/resolver` subpath export (`package.json` `exports`).
|
|
152
184
|
|
|
153
185
|
## Notes
|
|
154
186
|
|
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.51.0.0",
|
|
4
|
+
"publishVersion": "0.51.0",
|
|
5
|
+
"publishTag": "v0.51.0",
|
|
6
|
+
"schemasDigest": "sha256:018bf3078087a18b07990772c94669f658159bc84b0c8accedc7dd5daa38bee5",
|
|
7
7
|
"aiFixturesDigest": "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6",
|
|
8
|
-
"generatedGoDigest": "sha256:
|
|
9
|
-
"generatedTypeScriptDigest": "sha256:
|
|
8
|
+
"generatedGoDigest": "sha256:98bb6999738be926c36fd8d0735a17defdad590ced57e94bc431bb9bba53c9ad",
|
|
9
|
+
"generatedTypeScriptDigest": "sha256:1d83b04e4899777f2963177e39e7ac99852cae80ffb8c03b6a294f5fb19d3600",
|
|
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,7 +31,7 @@
|
|
|
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:1a6ac2e7d77a4d4dcbce8b937747f125081413f819ffee9e27e8b950a436cb22",
|
|
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",
|
|
@@ -52,22 +52,22 @@
|
|
|
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:
|
|
56
|
-
"schema/integration-execute-result.schema.json": "sha256:
|
|
55
|
+
"schema/integration-execute-request.schema.json": "sha256:18613bab7c233d1e2d52b960f5fe6ae4409171fa03a3aebf415cf86ae66e2866",
|
|
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:fa62fc6e28124120c71ebc36e257263ba8820d48b7d505d54ac53bd812fdd9ee",
|
|
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:858da48fa7fe97d8d06267bb3435d71ba307133b246cb020f5b473bb488c445a",
|
|
69
69
|
"schema/version-state.schema.json": "sha256:3b190007e2f8e3821c41ef11c6f5b226da7d5a18d34d4077f4618004846fd444",
|
|
70
|
-
"schema/well-defined-app-manifest.schema.json": "sha256:
|
|
70
|
+
"schema/well-defined-app-manifest.schema.json": "sha256:40f1bfea912dd85a05a0a3be2f6becadaf8ae3c5de9d5782ff1c8dd1bd83485f"
|
|
71
71
|
},
|
|
72
72
|
"aiFixtures": {
|
|
73
73
|
"fixtures/ai/adversarial-binding-classification-value.json": "sha256:ca009f3f6162a9d93b4ab416d52c1fba12a8a15aca5aed956c5fdcf2171b1e28",
|
|
@@ -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:66055e0d4354f73d922ee17984de8df065db665f93182b87fcd8a6a94ae94300",
|
|
111
|
+
"gen-go/types.go": "sha256:c94b302d560efb7d8a8f6a4387a22a2f0321489e04d9d126d561cd921ff07eec",
|
|
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:aabebdbffc70dd87017f1c72e0b8713f9cef506ba5258279b3a1405185875141",
|
|
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:d55491e8d1a73d8999a43904344760de9c6eb2ce500b1e942d49261a9c67808f",
|
|
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,26 +154,27 @@
|
|
|
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-
|
|
159
|
-
"gen-ts/integration-execute-
|
|
160
|
+
"gen-ts/integration-execute-input-rules.ts": "sha256:a779c872c44bb892dec7b28173d961696d95e5b6e1fa14e2ba67dd78344cdc5b",
|
|
161
|
+
"gen-ts/integration-execute-request.ts": "sha256:06056ac8ca4a649560189ee78b40a9d5ad5ca5089d967e5708cbc082aa21ba95",
|
|
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:62b5c5ffd125099bae46142013461c9ac3f75c2d27ab72cd4842a937f3e2f308",
|
|
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:005a10b34b89f055c3aae4bf54c095bbca7a9e15bd9b406b61a9eaafffc37a61",
|
|
173
176
|
"gen-ts/version-state-machine.ts": "sha256:c67af3e86ac306743345b850dfd3ae5201c19d10c40e2c149cf8c38258320d5f",
|
|
174
177
|
"gen-ts/version-state.ts": "sha256:a835112930299e7b593a1b8810b7414f6c9e809da2092306c743128170a3b702",
|
|
175
|
-
"gen-ts/well-defined-app-manifest.ts": "sha256:
|
|
178
|
+
"gen-ts/well-defined-app-manifest.ts": "sha256:2b72a39c2fa8e76239634d0478dc0da05446e74e944950eacf999b78876455c6"
|
|
176
179
|
}
|
|
177
180
|
}
|
|
@@ -28,6 +28,7 @@ export interface DeclaredConnection {
|
|
|
28
28
|
'slack.message.post'?: AppModeChannelOperation;
|
|
29
29
|
'gmail.thread.list'?: UserModeChannelOperation;
|
|
30
30
|
'gmail.message.read'?: UserModeChannelOperation;
|
|
31
|
+
'gmail.message.send'?: UserModeChannelOperation;
|
|
31
32
|
'warehouse.view.read'?: AppModeViewOperation;
|
|
32
33
|
};
|
|
33
34
|
}
|
|
@@ -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.51.0.0";
|
|
2
|
+
export declare const ContractPublishVersion: "0.51.0";
|
|
3
|
+
export declare const ContractPublishTag: "v0.51.0";
|
|
4
|
+
export declare const ContractSchemasDigest: "sha256:018bf3078087a18b07990772c94669f658159bc84b0c8accedc7dd5daa38bee5";
|
|
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:98bb6999738be926c36fd8d0735a17defdad590ced57e94bc431bb9bba53c9ad";
|
|
7
|
+
export declare const ContractGeneratedTypeScriptDigest: "sha256:1d83b04e4899777f2963177e39e7ac99852cae80ffb8c03b6a294f5fb19d3600";
|
|
@@ -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.51.0.0";
|
|
4
|
+
export const ContractPublishVersion = "0.51.0";
|
|
5
|
+
export const ContractPublishTag = "v0.51.0";
|
|
6
|
+
export const ContractSchemasDigest = "sha256:018bf3078087a18b07990772c94669f658159bc84b0c8accedc7dd5daa38bee5";
|
|
7
7
|
export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
|
|
8
|
-
export const ContractGeneratedGoDigest = "sha256:
|
|
9
|
-
export const ContractGeneratedTypeScriptDigest = "sha256:
|
|
8
|
+
export const ContractGeneratedGoDigest = "sha256:98bb6999738be926c36fd8d0735a17defdad590ced57e94bc431bb9bba53c9ad";
|
|
9
|
+
export const ContractGeneratedTypeScriptDigest = "sha256:1d83b04e4899777f2963177e39e7ac99852cae80ffb8c03b6a294f5fb19d3600";
|
|
@@ -33,7 +33,7 @@ export interface DataGrantV2 {
|
|
|
33
33
|
*
|
|
34
34
|
* @maxItems 5
|
|
35
35
|
*/
|
|
36
|
-
operations: ('slack.channel.history' | 'slack.message.post' | 'gmail.thread.list' | 'gmail.message.read' | 'warehouse.view.read')[];
|
|
36
|
+
operations: ('slack.channel.history' | 'slack.message.post' | 'gmail.thread.list' | 'gmail.message.read' | 'gmail.message.send' | 'warehouse.view.read')[];
|
|
37
37
|
/**
|
|
38
38
|
* Approved logical resources. Never inherited by wildcard. Empty until the first approval.
|
|
39
39
|
*
|
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
|
+
}
|