@canopy-io/node 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Canopy Identity Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # @canopy-io/node
2
+
3
+ Official TypeScript SDK for [Canopy](https://canopy-io.com) — hierarchical identity and access management for B2B SaaS.
4
+
5
+ > **Status: 0.1.0.** The client, pagination and the four main resources are built and tested. 27 of 81 operations have a typed wrapper; the rest are reachable through `canopy.client.request` with the same envelope handling, error typing and retry policy.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @canopy-io/node
11
+ ```
12
+
13
+ Requires Node 18 or later. Ships ESM and CommonJS, and has **zero runtime dependencies**.
14
+
15
+ Despite the name, it is not Node-only: the client is `fetch` and nothing else, so the same build runs in browsers, on Cloudflare Workers and on Deno.
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import { Canopy, isCanopyError } from "@canopy-io/node";
21
+
22
+ const canopy = new Canopy({ apiKey: process.env.CANOPY_API_KEY });
23
+
24
+ // The call every integrator makes on every request.
25
+ const { allowed } = await canopy.permissions.evaluate({
26
+ identity_id: identityId,
27
+ permission: "documents.read",
28
+ scope: "node",
29
+ node_id: nodeId,
30
+ });
31
+
32
+ // Pagination is handled for you, whichever style the endpoint uses.
33
+ for await (const identity of canopy.identities.list({ take: 50 })) {
34
+ console.log(identity.email);
35
+ }
36
+
37
+ // Errors carry a stable code, not just a message.
38
+ try {
39
+ await canopy.assignments.create({
40
+ identity_id: identityId,
41
+ node_id: nodeId,
42
+ role_id: roleId,
43
+ });
44
+ } catch (error) {
45
+ if (isCanopyError(error) && error.code === "rbac.assignment_conflict") {
46
+ // Already assigned — not a failure worth surfacing.
47
+ } else {
48
+ throw error;
49
+ }
50
+ }
51
+ ```
52
+
53
+ Anything without a typed wrapper is still reachable, with the same envelope
54
+ handling and retry policy:
55
+
56
+ ```ts
57
+ const page = await canopy.client.request("GET", "/api/v1/audit-events", {
58
+ query: { limit: 50 },
59
+ });
60
+ ```
61
+
62
+ ## Design
63
+
64
+ Three decisions shape this package.
65
+
66
+ **Types are generated from the published spec, never hand-written.**
67
+ `src/generated/types.ts` comes from <https://canopy-io.com/openapi/api.json>, the same document that renders Canopy's API reference. `npm run generate:check` fails if the committed types no longer match the live spec, so the SDK cannot silently describe an API that has moved on.
68
+
69
+ **Zero runtime dependencies.**
70
+ The client is `fetch` and nothing else, so it runs unchanged on Node, in browsers, on Cloudflare Workers, and on Deno — and it adds no supply-chain surface to anything that installs it. CI fails if a runtime dependency appears.
71
+
72
+ **The hand-written layer exists for correctness, not convenience.**
73
+ Writing a `fetch` call against a documented REST API is easy, and an LLM will do it for you. What neither gets reliably right is the part this package owns:
74
+
75
+ - **Which operations are safe to retry.** GET, HEAD, PUT and DELETE are idempotent by HTTP definition and are retried on a 5xx; POST is not, and a blind retry there can create a second role assignment. A 429 is retried regardless, because the request was refused before anything happened.
76
+ - **Two pagination styles behind one shape.** The audit log is cursor-paginated; everything else is offset. The top-level response is identical either way, so a hand-rolled loop silently reads only the first page of one of them — or never terminates.
77
+ - **The five-shape response envelope.** `{ data }`, `{ items }`, `{ items, pagination }`, `{ summary, results }` for partial success, `{ error }`, and bare 204.
78
+ - **Typed error codes.** `catch (e) { if (e.code === "rbac.assignment_conflict") }` branches on a contract rather than on a message that may be reworded.
79
+ - **Webhook signature verification.** Security-critical, and easy to get subtly wrong — timing-unsafe comparison, missing timestamp check. _(Not built yet — see the roadmap.)_
80
+
81
+ ## Roadmap
82
+
83
+ - [x] Package scaffold, dual ESM/CJS build, generated types
84
+ - [x] Client core — auth, envelope unwrapping, typed errors, retry
85
+ - [x] Pagination — one async iterator covering offset and cursor
86
+ - [x] Resource wrappers — `permissions`, `identities`, `roles`, `assignments`
87
+ - [x] Spec-drift guard — fails when the API's surface changes
88
+ - [ ] Webhook signature verification
89
+
90
+ Other languages are deliberately **not** planned here. Point your own generator at the published spec — that serves Python, Go and the rest better than a partly-maintained SDK would.
91
+
92
+ ## Contributing
93
+
94
+ ```bash
95
+ npm ci
96
+ npm run verify # lint, typecheck, spec drift, test, build
97
+ ```
98
+
99
+ To work against an unreleased API, point the generator somewhere else:
100
+
101
+ ```bash
102
+ CANOPY_SPEC_URL=../canopy/apps/canopy-api/spec/openapi.api.json npm run generate
103
+ ```
104
+
105
+ Releases use [changesets](https://github.com/changesets/changesets). Include one in your PR:
106
+
107
+ ```bash
108
+ npm run changeset
109
+ ```
110
+
111
+ ## License
112
+
113
+ MIT © Canopy Identity Inc.