@andco/sdk 0.0.2 → 0.0.4

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 (83) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +96 -54
  3. package/dist/auth.d.ts +8 -3
  4. package/dist/auth.d.ts.map +1 -1
  5. package/dist/auth.js +32 -23
  6. package/dist/browser/controller.d.ts +55 -1
  7. package/dist/browser/controller.d.ts.map +1 -1
  8. package/dist/browser/controller.js +166 -9
  9. package/dist/browser/frame.d.ts +4 -3
  10. package/dist/browser/frame.d.ts.map +1 -1
  11. package/dist/browser/frame.js +12 -20
  12. package/dist/browser/index.d.ts +18 -6
  13. package/dist/browser/index.d.ts.map +1 -1
  14. package/dist/browser/index.js +64 -29
  15. package/dist/browser/popup.d.ts +6 -1
  16. package/dist/browser/popup.d.ts.map +1 -1
  17. package/dist/browser/popup.js +37 -8
  18. package/dist/cli/index.d.ts +4 -2
  19. package/dist/cli/index.d.ts.map +1 -1
  20. package/dist/cli/index.js +10 -3
  21. package/dist/cli/server.d.ts +5 -0
  22. package/dist/cli/server.d.ts.map +1 -1
  23. package/dist/cli/server.js +5 -0
  24. package/dist/client.d.ts +66 -19
  25. package/dist/client.d.ts.map +1 -1
  26. package/dist/client.js +125 -74
  27. package/dist/config.d.ts +2 -1
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +1 -0
  30. package/dist/credentials.d.ts +58 -4
  31. package/dist/credentials.d.ts.map +1 -1
  32. package/dist/credentials.js +0 -0
  33. package/dist/errors.d.ts +23 -21
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/errors.js +18 -20
  36. package/dist/globals.d.ts +25 -0
  37. package/dist/globals.d.ts.map +1 -0
  38. package/dist/globals.js +15 -0
  39. package/dist/grants-api.d.ts +34 -0
  40. package/dist/grants-api.d.ts.map +1 -0
  41. package/dist/grants-api.js +48 -0
  42. package/dist/grants.d.ts +16 -0
  43. package/dist/grants.d.ts.map +1 -0
  44. package/dist/grants.js +13 -0
  45. package/dist/index.d.ts +12 -6
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +8 -2
  48. package/dist/inflight.d.ts +31 -0
  49. package/dist/inflight.d.ts.map +1 -0
  50. package/dist/inflight.js +26 -0
  51. package/dist/intents.d.ts +386 -40
  52. package/dist/intents.d.ts.map +1 -1
  53. package/dist/intents.js +712 -53
  54. package/dist/oauth.d.ts +55 -6
  55. package/dist/oauth.d.ts.map +1 -1
  56. package/dist/oauth.js +86 -57
  57. package/dist/presenter.d.ts +59 -11
  58. package/dist/presenter.d.ts.map +1 -1
  59. package/dist/presenter.js +40 -1
  60. package/dist/resource.d.ts +109 -0
  61. package/dist/resource.d.ts.map +1 -0
  62. package/dist/resource.js +151 -0
  63. package/dist/rest.d.ts +23 -4
  64. package/dist/rest.d.ts.map +1 -1
  65. package/dist/rest.js +46 -5
  66. package/dist/server/index.d.ts +3 -0
  67. package/dist/server/index.d.ts.map +1 -1
  68. package/dist/server/index.js +1 -0
  69. package/dist/server-metadata.generated.d.ts.map +1 -1
  70. package/dist/server-metadata.generated.js +8 -4
  71. package/dist/service.d.ts +109 -0
  72. package/dist/service.d.ts.map +1 -0
  73. package/dist/service.js +241 -0
  74. package/dist/session-store.d.ts +12 -4
  75. package/dist/session-store.d.ts.map +1 -1
  76. package/dist/session-store.js +64 -14
  77. package/dist/storage.d.ts +16 -23
  78. package/dist/storage.d.ts.map +1 -1
  79. package/dist/storage.js +27 -25
  80. package/dist/tokens.d.ts +46 -0
  81. package/dist/tokens.d.ts.map +1 -0
  82. package/dist/tokens.js +148 -0
  83. package/package.json +13 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,2FAA2F;AAC3F,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,CAAC;AAEF,gGAAgG;AAChG,MAAM,MAAM,mBAAmB,GAAG;IAChC,gDAAgD;IAChD,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,gGAAgG;IAChG,oBAAoB,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzD;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,8FAA8F;AAC9F,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,uBAAuB,CAAC,GAAG,QAAQ,CAAC;AAE7E,wDAAwD;AACxD,KAAK,eAAe,GAAG;IAAE,GAAG,EAAE;QAAE,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC;IAAC,GAAG,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAAA;CAAE,CAAC;AAC/G,yDAAyD;AACzD,KAAK,kBAAkB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;CAAE,CAAC;AACrF,0DAA0D;AAC1D,KAAK,mBAAmB,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjD,wEAAwE;IACxE,IAAI,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC/F,6EAA6E;IAC7E,OAAO,IAAI,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5G,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,aAAa;;IACxB,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,uFAAuF;IACvF,SAAgB,gBAAgB,EAAE,MAAM,CAAC;gBAItB,OAAO,EAAE;QAC1B,QAAQ,EAAE,MAAM,CAAC;QACjB,qEAAqE;QACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,MAAM,EAAE,WAAW,CAAC;QACpB,MAAM,EAAE,WAAW,CAAC;KACrB;IAOD;;;;;;;;;OASG;IACI,QAAQ,CAAC,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAO;;;;;;IASrE,wFAAwF;IACjF,OAAO,CAAC,OAAO,GAAE,mBAAwB,GAAG,eAAe;CAqFnE"}
@@ -0,0 +1,151 @@
1
+ import { Result } from "./errors.js";
2
+ /**
3
+ * One Resource Server's view of Andco: verify the bearer token, fetch the live Grant, and answer
4
+ * 401 or 403 the way RFC 6750 and RFC 9728 describe.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const orders = andco.resource({ resource: "https://casa-norte.example/api" });
9
+ *
10
+ * // Fetch-style handler (Next route handlers, Workers, Bun, Deno)
11
+ * export async function GET(request: Request) {
12
+ * const outcome = await orders.protect({ scopes: ["orders:read"] })(request);
13
+ * if (outcome instanceof Response) return outcome; // 401 or 403, already built
14
+ * return Response.json(await listOrders(outcome.data.subject));
15
+ * }
16
+ *
17
+ * // Hono
18
+ * app.use("/api/orders/*", orders.protect({ authorizationDetails: [{ type: "orders", actions: ["read"] }] }).hono());
19
+ *
20
+ * // Express
21
+ * router.get("/api/orders", orders.protect({ scopes: ["orders:read"] }).express(), handler);
22
+ * ```
23
+ */
24
+ export class AndcoResource {
25
+ resource;
26
+ /** Where `WWW-Authenticate` points clients for this resource's metadata (RFC 9728). */
27
+ resourceMetadata;
28
+ #tokens;
29
+ #grants;
30
+ constructor(options) {
31
+ this.resource = options.resource;
32
+ this.resourceMetadata = options.resourceMetadata ?? wellKnownUrl(options.resource);
33
+ this.#tokens = options.tokens;
34
+ this.#grants = options.grants;
35
+ }
36
+ /**
37
+ * The document to publish at {@link resourceMetadata}, so clients can discover the Authorization Server.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * app.get("/.well-known/oauth-protected-resource/api", (_req, res) =>
42
+ * res.json(orders.metadata({ scopesSupported: ["orders:read"] })),
43
+ * );
44
+ * ```
45
+ */
46
+ metadata(options = {}) {
47
+ return {
48
+ resource: this.resource,
49
+ authorization_servers: [this.#tokens.issuer],
50
+ bearer_methods_supported: ["header"],
51
+ scopes_supported: options.scopesSupported,
52
+ };
53
+ }
54
+ /** Builds a guard requiring a live Grant for this resource that satisfies `options`. */
55
+ protect(options = {}) {
56
+ const guard = (request) => this.#check(request, options);
57
+ return Object.assign(guard, {
58
+ hono: () => async (context, next) => {
59
+ const outcome = await guard(context.req.raw);
60
+ if (outcome instanceof Response)
61
+ return outcome;
62
+ context.set("andcoGrant", outcome.data);
63
+ await next();
64
+ return undefined;
65
+ },
66
+ express: () => async (request, response, next) => {
67
+ const authorization = request.headers["authorization"];
68
+ const headers = new Headers();
69
+ if (typeof authorization === "string")
70
+ headers.set("Authorization", authorization);
71
+ const outcome = await guard(new Request(this.resource, { headers }));
72
+ if (outcome instanceof Response) {
73
+ response.status(outcome.status);
74
+ outcome.headers.forEach((value, name) => response.setHeader(name, value));
75
+ response.send(await outcome.text());
76
+ return;
77
+ }
78
+ response.locals["andcoGrant"] = outcome.data;
79
+ next();
80
+ },
81
+ });
82
+ }
83
+ async #check(request, options) {
84
+ const token = /^Bearer ([^\s]+)$/i.exec(request.headers.get("Authorization") ?? "")?.[1];
85
+ if (!token) {
86
+ console.debug("[resource.protect] no bearer token for %s", this.resource);
87
+ return this.#reject(401, "missing_token");
88
+ }
89
+ if (options.verify) {
90
+ const verified = await this.#tokens.verify(token, { audience: this.resource });
91
+ if (verified.error)
92
+ return this.#reject(401, "invalid_token", { description: verified.error.message });
93
+ }
94
+ const { data: grant, error } = await this.#grants.get(token);
95
+ if (error) {
96
+ console.debug("[resource.protect] grant lookup failed status=%s code=%s", error.status, error.code);
97
+ if (error.status === 401 || error.status === 404)
98
+ return this.#reject(401, "invalid_token");
99
+ return this.#reject(503, "temporarily_unavailable");
100
+ }
101
+ if (!grant.active)
102
+ return this.#reject(401, "invalid_token");
103
+ if (grant.resource !== this.resource) {
104
+ console.debug("[resource.protect] grant resource %s is not %s", grant.resource, this.resource);
105
+ return this.#reject(403, "insufficient_authorization");
106
+ }
107
+ const missingScopes = (options.scopes ?? []).filter((scope) => !grant.scopes.includes(scope));
108
+ if (missingScopes.length) {
109
+ console.debug("[resource.protect] grant lacks scopes %o", missingScopes);
110
+ return this.#reject(403, "insufficient_scope", { scope: options.scopes.join(" ") });
111
+ }
112
+ const missingDetail = (options.authorizationDetails ?? []).find((need) => !satisfies(grant, need));
113
+ if (missingDetail) {
114
+ console.debug("[resource.protect] grant lacks detail %o", missingDetail);
115
+ return this.#reject(403, "insufficient_authorization", { detail: missingDetail });
116
+ }
117
+ return Result.ok(grant);
118
+ }
119
+ #reject(status, error, extra = {}) {
120
+ const headers = new Headers({ "Content-Type": "application/json", "Cache-Control": "no-store" });
121
+ if (status === 401) {
122
+ // RFC 6750: a request with no credentials carries no error code.
123
+ const attributes = [
124
+ error === "missing_token" ? null : `error="${error}"`,
125
+ `resource_metadata="${this.resourceMetadata}"`,
126
+ ];
127
+ headers.set("WWW-Authenticate", `Bearer ${attributes.filter(Boolean).join(", ")}`);
128
+ }
129
+ else if (status === 403 && error === "insufficient_scope") {
130
+ headers.set("WWW-Authenticate", `Bearer error="insufficient_scope", scope="${extra.scope}", resource_metadata="${this.resourceMetadata}"`);
131
+ }
132
+ return new Response(JSON.stringify({ error, error_description: extra.description, required: extra.detail }), {
133
+ status,
134
+ headers,
135
+ });
136
+ }
137
+ }
138
+ function satisfies(grant, need) {
139
+ return grant.authorizationDetails.some((detail) => {
140
+ if (detail["type"] !== need.type)
141
+ return false;
142
+ const actions = Array.isArray(detail["actions"]) ? detail["actions"] : [];
143
+ return (need.actions ?? []).every((action) => actions.includes(action));
144
+ });
145
+ }
146
+ /** RFC 9728: the well-known segment goes between the origin and the resource's own path. */
147
+ function wellKnownUrl(resource) {
148
+ const url = new URL(resource);
149
+ const path = url.pathname.replace(/\/+$/, "");
150
+ return new URL(`/.well-known/oauth-protected-resource${path}`, url.origin).href;
151
+ }
package/dist/rest.d.ts CHANGED
@@ -1,11 +1,15 @@
1
- import { type ResultClientFor } from "@andco/openapi-fetch";
2
- import { type AndCoRestClient } from "@andco/protocol/transport";
1
+ import { type ResultClient } from "@andco/openapi-fetch";
2
+ import { type AndCoRestClient, type AndCoRestPaths } from "@andco/protocol/transport";
3
3
  import type { AndcoConfig } from "./config.js";
4
4
  import type { AndcoCredentials } from "./credentials.js";
5
5
  import { AndcoAPIError } from "./errors.js";
6
+ import type { AndcoGlobals } from "./globals.js";
7
+ import type { GrantReference } from "./grants.js";
8
+ /** API configuration and a credential resolver evaluated for each request. */
6
9
  export type AndcoRestOptions = {
7
10
  config: AndcoConfig;
8
- fetch: typeof globalThis.fetch;
11
+ /** Platform capabilities; only `fetch` is read today. `resolveGlobals()` builds one. */
12
+ globals: AndcoGlobals;
9
13
  credentials: AndcoCredentials;
10
14
  /** Resource Indicator whose token authorizes these requests. Defaults to the configured API. */
11
15
  resource?: string;
@@ -15,6 +19,19 @@ export type AndcoPageOptions = {
15
19
  after?: string;
16
20
  limit?: number;
17
21
  };
22
+ /**
23
+ * Andco authorization controls for one call, passed after the openapi-fetch options and never sent
24
+ * as query data. Without `grant` the Authorization Server selects the Grant.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * await service.rest.http.GET("/accounts", undefined, { grant: grantId });
29
+ * ```
30
+ */
31
+ export type AndcoGrantOptions = {
32
+ grant?: GrantReference;
33
+ };
34
+ /** Per-request query, headers, cancellation, and write idempotency options. */
18
35
  export type AndcoRequestOptions = {
19
36
  /** Query parameters. Array values repeat the key, which is what the Andco API expects. */
20
37
  query?: Record<string, string | number | boolean | readonly string[] | undefined>;
@@ -24,7 +41,7 @@ export type AndcoRequestOptions = {
24
41
  idempotencyKey?: string;
25
42
  };
26
43
  /** `AndcoRest.http`'s type: the generated OAuth resource client, with `AndcoAPIError` as its error. */
27
- export type AndcoHttpClient = ResultClientFor<AndCoRestClient, AndcoAPIError>;
44
+ export type AndcoHttpClient = ResultClient<AndCoRestPaths, "application/json", AndcoAPIError, AndcoGrantOptions>;
28
45
  /**
29
46
  * Typed access to the Andco resource API.
30
47
  *
@@ -53,6 +70,8 @@ export declare class AndcoRest {
53
70
  * The raw OAuth resource client generated from the API's OpenAPI contract. Use this for low-level access to the API endpoints.
54
71
  */
55
72
  readonly raw: AndCoRestClient;
73
+ /** Creates request-local authority for one iterator, subscription or explicitly grouped retry. */
74
+ forOperation(): AndcoRest;
56
75
  constructor(options: AndcoRestOptions);
57
76
  }
58
77
  //# sourceMappingURL=rest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAc,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,KAAK,eAAe,EAAyB,MAAM,2BAA2B,CAAC;AACxF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,iGAAiG;AACjG,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,MAAM,mBAAmB,GAAG;IAChC,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,uGAAuG;AACvG,MAAM,MAAM,eAAe,GAAG,eAAe,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAE9E;;;;;;;;;;;;GAYG;AACH,qBAAa,SAAS;;IAMpB;;;;;;;OAOG;IACH,SAAgB,IAAI,EAAE,eAAe,CAAC;IAEtC;;OAEG;IACH,SAAgB,GAAG,EAAE,eAAe,CAAC;gBAElB,OAAO,EAAE,gBAAgB;CAc7C"}
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAyB,MAAM,2BAA2B,CAAC;AAC7G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,wFAAwF;IACxF,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,iGAAiG;AACjG,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC;AAE3D,+EAA+E;AAC/E,MAAM,MAAM,mBAAmB,GAAG;IAChC,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,uGAAuG;AACvG,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAC;AAEjH;;;;;;;;;;;;GAYG;AACH,qBAAa,SAAS;;IAOpB;;;;;;;OAOG;IACH,SAAgB,IAAI,EAAE,eAAe,CAAC;IAEtC;;OAEG;IACH,SAAgB,GAAG,EAAE,eAAe,CAAC;IAErC,kGAAkG;IAC3F,YAAY,IAAI,SAAS;gBAWb,OAAO,EAAE,gBAAgB;CA0C7C"}
package/dist/rest.js CHANGED
@@ -16,9 +16,10 @@ import { AndcoAPIError } from "./errors.js";
16
16
  */
17
17
  export class AndcoRest {
18
18
  #config;
19
- #fetch;
19
+ #globals;
20
20
  #credentials;
21
21
  #resource;
22
+ #middleware = [];
22
23
  /**
23
24
  * Typed OAuth resource client generated from the API's OpenAPI contract.
24
25
  * @example
@@ -32,16 +33,56 @@ export class AndcoRest {
32
33
  * The raw OAuth resource client generated from the API's OpenAPI contract. Use this for low-level access to the API endpoints.
33
34
  */
34
35
  raw;
36
+ /** Creates request-local authority for one iterator, subscription or explicitly grouped retry. */
37
+ forOperation() {
38
+ const rest = new AndcoRest({
39
+ config: this.#config,
40
+ globals: this.#globals,
41
+ credentials: this.#credentials.forOperation?.() ?? this.#credentials,
42
+ resource: this.#resource,
43
+ });
44
+ rest.raw.use(...this.#middleware);
45
+ return rest;
46
+ }
35
47
  constructor(options) {
36
48
  this.#config = options.config;
37
- this.#fetch = options.fetch;
49
+ this.#globals = options.globals;
38
50
  this.#credentials = options.credentials;
39
51
  this.#resource = options.resource ?? options.config.endpoints.api.origin;
40
- this.raw = createAndCoRestClient({
52
+ const create = (controls = {}) => createAndCoRestClient({
41
53
  endpoint: this.#config.endpoints.api,
42
54
  apiVersion: this.#config.apiVersion,
43
- accessToken: () => this.#credentials.accessTokenFor(this.#resource),
44
- fetch: this.#fetch,
55
+ accessToken: () => this.#credentials.accessTokenFor(this.#resource, { grant: controls.grant }),
56
+ fetch: this.#globals.fetch,
57
+ });
58
+ const base = create();
59
+ const middleware = this.#middleware;
60
+ const methods = new Set(["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE", "request"]);
61
+ this.raw = new Proxy(base, {
62
+ get(target, property, receiver) {
63
+ if (property === "use")
64
+ return (...items) => {
65
+ middleware.push(...items);
66
+ };
67
+ if (property === "eject")
68
+ return (...items) => {
69
+ for (const item of items) {
70
+ const i = middleware.indexOf(item);
71
+ if (i >= 0)
72
+ middleware.splice(i, 1);
73
+ }
74
+ };
75
+ if (typeof property !== "string" || !methods.has(property))
76
+ return Reflect.get(target, property, receiver);
77
+ return (...args) => {
78
+ const index = property === "request" ? 3 : 2;
79
+ const controls = (args[index] ?? {});
80
+ const client = create(controls);
81
+ client.use(...middleware);
82
+ const call = Reflect.get(client, property);
83
+ return call.apply(client, args.slice(0, index));
84
+ };
85
+ },
45
86
  });
46
87
  this.http = withResult(this.raw, AndcoAPIError.fromOpenAPIFetch);
47
88
  }
@@ -1,4 +1,5 @@
1
1
  import { AndcoClient, type AndcoClientOptions } from "../client.js";
2
+ /** Server instance configuration; bind caller credentials separately with `with(...)`. */
2
3
  export type AndcoServerOptions = AndcoClientOptions;
3
4
  /**
4
5
  * Creates an Andco Instance for a server.
@@ -18,4 +19,6 @@ export type AndcoServerOptions = AndcoClientOptions;
18
19
  * ```
19
20
  */
20
21
  export declare function createAndcoInstanceForServer(options: AndcoServerOptions): AndcoClient;
22
+ export type { Grant, GrantReference } from "../grants.js";
23
+ export { AndcoAuthorizationRequiredError, AndcoService, type AndcoServiceCredentialsOptions, type AndcoServiceOptions, type AndcoServiceSubject, createAndcoService, } from "../service.js";
21
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAOrF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGpE,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAOrF;AACD,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EACL,+BAA+B,EAC/B,YAAY,EACZ,KAAK,8BAA8B,EACnC,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,kBAAkB,GACnB,MAAM,eAAe,CAAC"}
@@ -25,3 +25,4 @@ export function createAndcoInstanceForServer(options) {
25
25
  }
26
26
  return new AndcoClient(options);
27
27
  }
28
+ export { AndcoAuthorizationRequiredError, AndcoService, createAndcoService, } from "../service.js";
@@ -1 +1 @@
1
- {"version":3,"file":"server-metadata.generated.d.ts","sourceRoot":"","sources":["../src/server-metadata.generated.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,KAAK,IAAI,MAAM,eAAe,CAAC;AAsC3C,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,cAAc,CAEvE"}
1
+ {"version":3,"file":"server-metadata.generated.d.ts","sourceRoot":"","sources":["../src/server-metadata.generated.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,KAAK,IAAI,MAAM,eAAe,CAAC;AA0C3C,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,cAAc,CAEvE"}
@@ -6,8 +6,8 @@
6
6
  // sign-in. `{issuer}` is substituted with the configured `endpointAuth`.
7
7
  //
8
8
  // Regenerate: pnpm --filter @andco/sdk run refresh:server-metadata
9
- // Source: https://auth.localhost/auth/v1/.well-known/openid-configuration
10
- // Generated: 2026-09-19T01:19:04.372Z
9
+ // Source: https://oauth.localhost/auth/v1/.well-known/openid-configuration
10
+ // Generated: 2026-09-30T18:33:31.034Z
11
11
  const TEMPLATE = {
12
12
  issuer: "{issuer}",
13
13
  authorization_endpoint: "{issuer}/oauth/authorize",
@@ -15,11 +15,15 @@ const TEMPLATE = {
15
15
  device_authorization_endpoint: "{issuer}/oauth/device/authorize",
16
16
  jwks_uri: "{issuer}/.well-known/jwks.json",
17
17
  userinfo_endpoint: "{issuer}/oauth/userinfo",
18
- registration_endpoint: "{issuer}/oauth/clients/register",
19
18
  scopes_supported: ["openid", "profile", "email", "phone", "offline_access"],
20
19
  response_types_supported: ["code"],
21
20
  response_modes_supported: ["query"],
22
- grant_types_supported: ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"],
21
+ grant_types_supported: [
22
+ "authorization_code",
23
+ "refresh_token",
24
+ "urn:ietf:params:oauth:grant-type:device_code",
25
+ "client_credentials",
26
+ ],
23
27
  subject_types_supported: ["public"],
24
28
  id_token_signing_alg_values_supported: ["RS256", "HS256", "ES256"],
25
29
  token_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post", "none"],
@@ -0,0 +1,109 @@
1
+ import { type GrantedOrganizationSubject } from "@andco/protocol";
2
+ import { AndcoConfig, type AndcoConfigInput } from "./config.js";
3
+ import type { AndcoCredentials } from "./credentials.js";
4
+ import { AndcoError } from "./errors.js";
5
+ import { type AndcoGlobals } from "./globals.js";
6
+ import { type Grant, type GrantReference } from "./grants.js";
7
+ import { AndcoIntents } from "./intents.js";
8
+ import { AndcoRest } from "./rest.js";
9
+ /**
10
+ * Whose resources a Service acts on: always an Organization, named by its identifier.
11
+ *
12
+ * @example
13
+ * const subject: AndcoServiceSubject = { type: "organization", identifier: "123" };
14
+ */
15
+ export type AndcoServiceSubject = GrantedOrganizationSubject;
16
+ export interface AndcoServiceOptions extends AndcoConfigInput {
17
+ clientSecret: string;
18
+ /**
19
+ * The Organization this Service acts for when a call does not name one: `service.rest` and
20
+ * `service.credentials()` use it. Omit it when every call passes its own `subject`.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * createAndcoService({ clientId, clientSecret, subject: { type: "organization", identifier: "123" } });
25
+ * ```
26
+ */
27
+ subject?: AndcoServiceSubject;
28
+ globals?: Partial<AndcoGlobals>;
29
+ /** Registered Resource Indicator, independent of the HTTP endpoint origin. */
30
+ resource?: string;
31
+ }
32
+ export interface AndcoServiceCredentialsOptions {
33
+ /** The Organization to act for; defaults to the Service's `subject`. */
34
+ subject?: AndcoServiceSubject;
35
+ /** Exact Grant to use instead of the server's selection, e.g. when several qualify. */
36
+ grant?: GrantReference;
37
+ }
38
+ export interface AndcoServiceListGrantsOptions {
39
+ /** The Organization whose Grants to list; defaults to the Service's `subject`. */
40
+ subject?: AndcoServiceSubject;
41
+ /** Resource Indicator; defaults to the Service's resource. */
42
+ resource?: string;
43
+ }
44
+ /** No approved Grant covers this subject and resource. Version one never opens a login flow. */
45
+ export declare class AndcoAuthorizationRequiredError extends AndcoError {
46
+ readonly subject: AndcoServiceSubject;
47
+ readonly resource: string;
48
+ constructor(subject: AndcoServiceSubject, resource: string);
49
+ }
50
+ /**
51
+ * Confidential client acting for Organizations through Grants approved in Platform.
52
+ *
53
+ * Each call sends the subject and the resource, and the Authorization Server selects the Grant.
54
+ * When more than one Grant qualifies the exchange fails with `ambiguous_grant`, whose `details`
55
+ * name the candidate ids; pass one as `grant` to pin it. Access tokens live in memory only.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const service = createAndcoService({ clientId, clientSecret });
60
+ * const bank = new Bank(service.credentials({ subject: { type: "organization", identifier: "123" } }));
61
+ * const { data } = await bank.client.http.GET("/accounts").throwOnError();
62
+ * await service.intents({ subject: { type: "organization", identifier: "123" } }).approve(transferId);
63
+ * ```
64
+ */
65
+ export declare class AndcoService {
66
+ #private;
67
+ readonly config: AndcoConfig;
68
+ readonly rest: AndcoRest;
69
+ constructor(options: AndcoServiceOptions);
70
+ /**
71
+ * Credentials for Bank or another Resource Server definition, acting for one Organization.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const bank = new Bank(service.credentials({ subject: { type: "organization", identifier: "123" } }));
76
+ * ```
77
+ */
78
+ credentials(options?: AndcoServiceCredentialsOptions): AndcoCredentials;
79
+ /**
80
+ * The Intent lifecycle (`approve`, `reject`, `cancel`, `get`, `events`, `subscribe`) acting for
81
+ * one Organization: the same {@link AndcoIntents} `andco.intents` exposes, authorized with
82
+ * `credentials(options)`. A method rather than a property, like `credentials()`, because the
83
+ * subject and the Grant are chosen per call; without options it acts for the Service's `subject`.
84
+ * Presentation needs a person and a browser, so `present()` and `presentationURL()` are not for a
85
+ * Service.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const intents = service.intents({ subject: { type: "organization", identifier: "123" } });
90
+ * await intents.approve(transferId); // the server selects the Grant holding bank_transfer approve
91
+ * await service.intents({ grant: approverGrantId }).reject(transferId, { reason: "over_budget" });
92
+ * const { data: transfer } = await service.intents().get(transferId);
93
+ * ```
94
+ */
95
+ intents(options?: AndcoServiceCredentialsOptions): AndcoIntents;
96
+ /**
97
+ * Lists the active Grants the server would select from for one subject and resource.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const grants = await service.listGrants({ subject: { type: "organization", identifier: "123" } });
102
+ * const bank = new Bank(service.credentials({ subject, grant: grants[0] }));
103
+ * ```
104
+ */
105
+ listGrants(options?: AndcoServiceListGrantsOptions): Promise<readonly Grant[]>;
106
+ }
107
+ /** Constructs a service lazily; does not perform network I/O or approve access. */
108
+ export declare function createAndcoService(options: AndcoServiceOptions): AndcoService;
109
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,0BAA0B,EAIhC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAW,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,0BAA0B,CAAC;AAE7D,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,8BAA8B;IAC7C,wEAAwE;IACxE,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,uFAAuF;IACvF,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AACD,MAAM,WAAW,6BAA6B;IAC5C,kFAAkF;IAClF,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,gGAAgG;AAChG,qBAAa,+BAAgC,SAAQ,UAAU;aAE3C,OAAO,EAAE,mBAAmB;aAC5B,QAAQ,EAAE,MAAM;gBADhB,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM;CAOnC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;gBAOb,OAAO,EAAE,mBAAmB;IAmBxC;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,GAAE,8BAAmC,GAAG,gBAAgB;IAM3E;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,OAAO,GAAE,8BAAmC,GAAG,YAAY;IAenE;;;;;;;;OAQG;IACG,UAAU,CAAC,OAAO,GAAE,6BAAkC,GAAG,OAAO,CAAC,SAAS,KAAK,EAAE,CAAC;CAiHzF;AAiBD,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAE7E"}