@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
@@ -28,7 +28,8 @@ export function andcoPopupPresentationId() {
28
28
  return PRESENTATION_ID_PATTERN.test(presentationId) ? presentationId : null;
29
29
  }
30
30
  /**
31
- * Hands this document's callback to the window that opened it, then closes.
31
+ * Hands this document's callback — an authorization or an Intent outcome — to the window that
32
+ * opened it, then closes.
32
33
  *
33
34
  * The popup cannot complete the exchange itself: the PKCE verifier was generated by the opener and
34
35
  * never left it. So this is a courier, not a consumer — it carries the callback URL back and dies.
@@ -41,7 +42,11 @@ export function relayAndcoPopupCallback(allowedOrigins, options = {}) {
41
42
  if (typeof window === "undefined")
42
43
  return { status: "not_callback" };
43
44
  const url = options.callbackUrl ? new URL(options.callbackUrl, window.location.href) : new URL(window.location.href);
44
- const isCallback = Boolean(options.callbackUrl) || url.searchParams.has("code") || url.searchParams.has("error");
45
+ const isCallback = Boolean(options.callbackUrl) ||
46
+ url.searchParams.has("code") ||
47
+ url.searchParams.has("error") ||
48
+ // An Intent callback carries no authorization code; its outcome is the whole payload.
49
+ url.searchParams.has("andco_intent_result");
45
50
  if (!isCallback)
46
51
  return { status: "not_callback" };
47
52
  const presentationId = andcoPopupPresentationId();
@@ -62,8 +67,9 @@ export function relayAndcoPopupCallback(allowedOrigins, options = {}) {
62
67
  const message = openerOrigin === window.location.origin ? sdkMessage(presentationId, url) : protocolResult(presentationId, url);
63
68
  // Strip before leaving. If closing is refused, the code must not remain visible in the address
64
69
  // bar or in the window name.
65
- for (const parameter of ["code", "state", "error", "error_description"])
70
+ for (const parameter of ["code", "state", "error", "error_description", "andco_intent_result", "intent_id"]) {
66
71
  url.searchParams.delete(parameter);
72
+ }
67
73
  window.history.replaceState(window.history.state, "", url);
68
74
  window.name = "";
69
75
  window.opener?.postMessage(message, openerOrigin);
@@ -75,14 +81,25 @@ function sdkMessage(presentationId, url) {
75
81
  }
76
82
  function protocolResult(presentationId, url) {
77
83
  const envelope = { protocol: ANDCO_SDK_PROTOCOL, version: ANDCO_PROTOCOL_VERSION, presentationId };
84
+ // An Intent reports a lifecycle outcome and nothing else. Whether money moved is read from the
85
+ // Resource Server afterwards, never carried in a window message.
86
+ const intent = url.searchParams.get("andco_intent_result");
87
+ if (intent === "complete") {
88
+ return { ...envelope, type: "intent.complete" };
89
+ }
90
+ else if (intent) {
91
+ return { ...envelope, type: "intent.dismiss" };
92
+ }
78
93
  const code = url.searchParams.get("code");
79
- if (code)
94
+ if (code) {
80
95
  return { ...envelope, type: "oauth.authorization_code", authorizationCode: code };
96
+ }
81
97
  const error = url.searchParams.get("error") ?? "invalid_request";
82
98
  // `access_denied` is the only OAuth error that carries a decision, and it is the one the
83
99
  // Authorization Server returns when the user declines.
84
- if (error === "access_denied")
100
+ if (error === "access_denied") {
85
101
  return { ...envelope, type: "oauth.dismiss" };
102
+ }
86
103
  const description = url.searchParams.get("error_description");
87
104
  return {
88
105
  ...envelope,
@@ -105,12 +122,19 @@ function protocolResult(presentationId, url) {
105
122
  * ```
106
123
  */
107
124
  export function openAndcoPopupWindow(options) {
108
- const popup = window.open(options.url?.href ?? "about:blank", `${ANDCO_POPUP_NAME_PREFIX}${options.presentationId}`, windowFeatures({ width: 620, height: 720 }));
125
+ const name = `${ANDCO_POPUP_NAME_PREFIX}${options.presentationId}`;
126
+ const href = options.url?.href ?? "about:blank";
127
+ console.debug("[openAndcoPopupWindow] opening %s %s", options.target ?? "popup", name);
128
+ // Without window features a browser opens a tab; with them, a popup window.
129
+ const popup = options.target === "newtab"
130
+ ? window.open(href, name)
131
+ : window.open(href, name, windowFeatures({ width: 620, height: 720 }));
109
132
  if (!popup) {
110
133
  return {
111
134
  blocked: true,
112
135
  navigate: () => { },
113
136
  result: Promise.resolve(Result.fail(ANDCO_ERROR_CODES.POPUP_BLOCKED)),
137
+ focus: () => { },
114
138
  close: () => { },
115
139
  };
116
140
  }
@@ -154,15 +178,20 @@ export function openAndcoPopupWindow(options) {
154
178
  popup.close();
155
179
  settle(Result.fail(ANDCO_ERROR_CODES.POPUP_TIMEOUT));
156
180
  }, POPUP_TIMEOUT_MS);
157
- options.signal?.addEventListener("abort", () => {
181
+ const abort = () => {
158
182
  popup.close();
159
183
  settle(Result.ok(null));
160
- });
184
+ };
185
+ if (options.signal?.aborted)
186
+ abort();
187
+ else
188
+ options.signal?.addEventListener("abort", abort, { once: true });
161
189
  });
162
190
  return {
163
191
  blocked: false,
164
192
  navigate: (url) => popup.location.assign(url.href),
165
193
  result,
194
+ focus: () => popup.focus(),
166
195
  close: () => popup.close(),
167
196
  };
168
197
  }
@@ -1,6 +1,7 @@
1
1
  import { AndcoClient, type AndcoClientOptions } from "../client.js";
2
2
  import { Result } from "../errors.js";
3
- import type { AndcoPresenter, AndcoPresentOptions } from "../presenter.js";
3
+ import { AndcoPresenter, type AndcoPresentOptions } from "../presenter.js";
4
+ /** Browser launch, terminal output, and callback timeout overrides for CLI authorization. */
4
5
  export type AndcoCliPresenterOptions = {
5
6
  /** Opens the authorization URL. Defaults to printing it, so nothing is assumed about the host. */
6
7
  open?: (url: URL) => void | Promise<void>;
@@ -28,11 +29,12 @@ export type AndcoCliPresenterOptions = {
28
29
  * const { data: session } = await andco.auth.signIn();
29
30
  * ```
30
31
  */
31
- export declare class AndcoCliPresenter implements AndcoPresenter {
32
+ export declare class AndcoPresenterCli extends AndcoPresenter {
32
33
  private readonly options;
33
34
  constructor(options?: AndcoCliPresenterOptions);
34
35
  present(presentation: AndcoPresentOptions): Promise<Result<URL | null>>;
35
36
  }
37
+ /** Instance configuration with an optional CLI authorization presenter. */
36
38
  export type AndcoCliOptions = Omit<AndcoClientOptions, "presenter"> & {
37
39
  presenter?: AndcoPresenter;
38
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAqB,MAAM,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3E,MAAM,MAAM,wBAAwB,GAAG;IACrC,kGAAkG;IAClG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,wBAA6B;IAE7D,OAAO,CAAC,YAAY,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;CAiC9E;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG;IACpE,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAE/E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAqB,MAAM,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3E,6FAA6F;AAC7F,MAAM,MAAM,wBAAwB,GAAG;IACrC,kGAAkG;IAClG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,wBAA6B;IAI7D,OAAO,CAAC,YAAY,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;CAqC9E;AAED,2EAA2E;AAC3E,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG;IACpE,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAE/E"}
package/dist/cli/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { AndcoClient } from "../client.js";
2
2
  import { isLoopback } from "../config.js";
3
3
  import { ANDCO_ERROR_CODES, Result } from "../errors.js";
4
+ import { AndcoPresenter } from "../presenter.js";
4
5
  import { LoopbackServer } from "./server.js";
5
6
  /**
6
7
  * Presents an authorization from a terminal, using a one-shot loopback receiver.
@@ -21,9 +22,10 @@ import { LoopbackServer } from "./server.js";
21
22
  * const { data: session } = await andco.auth.signIn();
22
23
  * ```
23
24
  */
24
- export class AndcoCliPresenter {
25
+ export class AndcoPresenterCli extends AndcoPresenter {
25
26
  options;
26
27
  constructor(options = {}) {
28
+ super();
27
29
  this.options = options;
28
30
  }
29
31
  async present(presentation) {
@@ -44,9 +46,14 @@ export class AndcoCliPresenter {
44
46
  });
45
47
  }
46
48
  try {
49
+ // Resolved after the receiver is bound: a pushed request is created on demand, and there is
50
+ // nothing to push before the callback it must name exists.
51
+ const resolved = await AndcoPresenter.present(presentation.url);
52
+ if (resolved.error)
53
+ return Result.fail(resolved.error);
47
54
  // The bound port may differ from the configured one when it asked for any free port, and the
48
55
  // authorization request must carry the exact URI the server will redirect to.
49
- const url = new URL(presentation.url.href);
56
+ const url = new URL(resolved.data.href);
50
57
  url.searchParams.set("redirect_uri", receiver.redirectUri.href);
51
58
  if (this.options.open)
52
59
  await this.options.open(url);
@@ -68,5 +75,5 @@ export class AndcoCliPresenter {
68
75
  * credential is `andco.session.set(...)`, not a write into a private storage key.
69
76
  */
70
77
  export function createAndcoInstanceForCLI(options) {
71
- return new AndcoClient({ ...options, presenter: options.presenter ?? new AndcoCliPresenter() });
78
+ return new AndcoClient({ ...options, presenter: options.presenter ?? new AndcoPresenterCli() });
72
79
  }
@@ -1,9 +1,14 @@
1
+ /** Local OAuth callback receiver. The caller owns state/PKCE validation and server cleanup. */
1
2
  export declare class LoopbackServer {
2
3
  #private;
3
4
  private constructor();
5
+ /** Starts listening; a missing port selects an available port reflected in `redirectUri`. */
4
6
  static start(configured: URL): Promise<LoopbackServer>;
7
+ /** Exact callback URL including the port allocated by the operating system. */
5
8
  get redirectUri(): URL;
9
+ /** Waits for a callback or returns `null` on timeout. Call `close()` after either outcome. */
6
10
  wait(timeoutMs: number): Promise<URL | null>;
11
+ /** Closes the listener and any remaining connections. */
7
12
  close(): Promise<void>;
8
13
  private listen;
9
14
  private static hostFor;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/cli/server.ts"],"names":[],"mappings":"AAEA,qBAAa,cAAc;;IAKzB,OAAO;WAKM,KAAK,CAAC,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC;IAc5D,IAAI,WAAW,IAAI,GAAG,CAErB;IAED,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAS5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDtB,OAAO,CAAC,MAAM;IAcd,OAAO,CAAC,MAAM,CAAC,OAAO;CAGvB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/cli/server.ts"],"names":[],"mappings":"AAEA,+FAA+F;AAC/F,qBAAa,cAAc;;IAKzB,OAAO;IAKP,6FAA6F;WAChF,KAAK,CAAC,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC;IAc5D,+EAA+E;IAC/E,IAAI,WAAW,IAAI,GAAG,CAErB;IAED,8FAA8F;IAC9F,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAS5C,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDtB,OAAO,CAAC,MAAM;IAcd,OAAO,CAAC,MAAM,CAAC,OAAO;CAGvB"}
@@ -1,4 +1,5 @@
1
1
  import { createServer } from "node:http";
2
+ /** Local OAuth callback receiver. The caller owns state/PKCE validation and server cleanup. */
2
3
  export class LoopbackServer {
3
4
  #server;
4
5
  #resolveCallback;
@@ -7,6 +8,7 @@ export class LoopbackServer {
7
8
  this.#redirectUri = configured;
8
9
  this.#server = this.#buildServer();
9
10
  }
11
+ /** Starts listening; a missing port selects an available port reflected in `redirectUri`. */
10
12
  static async start(configured) {
11
13
  const receiver = new LoopbackServer(configured);
12
14
  await receiver.listen();
@@ -19,9 +21,11 @@ export class LoopbackServer {
19
21
  receiver.#redirectUri.port = String(address.port);
20
22
  return receiver;
21
23
  }
24
+ /** Exact callback URL including the port allocated by the operating system. */
22
25
  get redirectUri() {
23
26
  return this.#redirectUri;
24
27
  }
28
+ /** Waits for a callback or returns `null` on timeout. Call `close()` after either outcome. */
25
29
  wait(timeoutMs) {
26
30
  return new Promise((resolve) => {
27
31
  this.#resolveCallback = resolve;
@@ -30,6 +34,7 @@ export class LoopbackServer {
30
34
  timer.unref?.();
31
35
  });
32
36
  }
37
+ /** Closes the listener and any remaining connections. */
33
38
  close() {
34
39
  return new Promise((resolve) => {
35
40
  this.#server.closeAllConnections?.();
package/dist/client.d.ts CHANGED
@@ -1,16 +1,22 @@
1
- import type { AndCoRandomSource } from "@andco/protocol";
2
1
  import { AndcoAuth } from "./auth.js";
3
2
  import { type AndcoConfig, type AndcoConfigInput } from "./config.js";
4
3
  import { type AndcoCredentials, type AndcoSession } from "./credentials.js";
4
+ import { type AndcoGlobals } from "./globals.js";
5
+ import { AndcoGrants } from "./grants-api.js";
6
+ import type { AndcoInflight } from "./inflight.js";
5
7
  import { AndcoIntents } from "./intents.js";
6
8
  import { type AndcoAuthorizationRequest, AndcoOAuth, type AndcoTransportMode } from "./oauth.js";
7
- import type { AndcoPresenter } from "./presenter.js";
9
+ import { AndcoPresenter } from "./presenter.js";
10
+ import { AndcoResource } from "./resource.js";
8
11
  import { AndcoRest } from "./rest.js";
9
12
  import { type AndcoSessionSource, AndcoSessionStore } from "./session-store.js";
10
- import type { AndcoLock, AndcoStorage } from "./storage.js";
13
+ import type { AndcoStorage } from "./storage.js";
14
+ import { AndcoTokens } from "./tokens.js";
11
15
  /** A Resource Server Definition: immutable, and able to bind itself to a client. */
12
16
  export interface AndcoResourceServer<Client> {
17
+ /** Registered Resource Server ID used in authorization contributions. */
13
18
  readonly id: string;
19
+ /** OAuth Resource Indicator used to select the matching bearer credential. */
14
20
  readonly resource: string;
15
21
  /**
16
22
  * Produces an operational client for this Resource Server from resolved credentials.
@@ -21,15 +27,11 @@ export interface AndcoResourceServer<Client> {
21
27
  */
22
28
  use(credentials: AndcoCredentials): Client;
23
29
  }
24
- /** Platform capabilities the SDK reads off the runtime, grouped so a future addition is not a breaking change. */
25
- export type AndcoGlobals = {
26
- fetch: typeof globalThis.fetch;
27
- /** Threaded into the Protocol Contract's `RANDOM_UUID`, so a runtime without Web Crypto can supply its own. */
28
- crypto: AndCoRandomSource;
29
- };
30
+ /** Configuration and runtime capabilities shared by browser, server, and CLI instances. */
30
31
  export type AndcoClientOptions = AndcoConfigInput & {
31
32
  /** Confidential credential. Its presence is what makes this a confidential OAuth client. */
32
33
  clientSecret?: string | null;
34
+ /** Overrides runtime fetch and randomness; omitted capabilities use platform defaults. */
33
35
  globals?: Partial<AndcoGlobals>;
34
36
  /** Authorization Transport Policy. Defaults to `auto`. */
35
37
  transport?: AndcoTransportMode;
@@ -37,14 +39,66 @@ export type AndcoClientOptions = AndcoConfigInput & {
37
39
  presenter?: AndcoPresenter;
38
40
  /** Persists a transaction so it survives a full-page redirect. */
39
41
  persistTransaction?: (request: AndcoAuthorizationRequest) => void | Promise<void>;
42
+ /** Session persistence adapter. Defaults to memory unless a runtime factory supplies one. */
40
43
  storage?: AndcoStorage;
41
44
  /** A session the caller already resolved, so the first snapshot is real rather than unresolved. */
42
45
  initialSession?: AndcoSession | null;
43
- lock?: AndcoLock;
46
+ /** Shares one refresh among concurrent callers. Defaults to sharing within this runtime. */
47
+ inflight?: AndcoInflight;
44
48
  /** Consulted once during the load, before storage. This is where a URL callback enters. */
45
49
  source?: AndcoSessionSource;
50
+ /** Writes session changes to storage unless `false`; defaults to `true`. */
46
51
  persist?: boolean;
47
52
  };
53
+ /**
54
+ * The eight collaborator fields shared, verbatim, by {@link AndcoClient} and both of the authorized
55
+ * views behind `with(...)` and `authorized()`. Declaring them once here is what keeps adding a field
56
+ * to the instance from requiring three synchronized edits.
57
+ *
58
+ * This is a construction seam, not a claim that the three are interchangeable: each subclass still
59
+ * decides for itself — per the ADR-0023 invariants documented at `AndcoAuthedClientImpl` and
60
+ * `AndcoAuthorizedViewImpl` — whether a given field's value is freshly built or borrowed by reference
61
+ * from an owner. The base constructor only stores whatever it is handed.
62
+ */
63
+ declare class AndcoClientFields {
64
+ readonly config: AndcoConfig;
65
+ readonly clientSecret: string | null;
66
+ readonly globals: AndcoGlobals;
67
+ readonly oauth: AndcoOAuth;
68
+ readonly rest: AndcoRest;
69
+ readonly intents: AndcoIntents;
70
+ readonly auth: AndcoAuth;
71
+ readonly session: AndcoSessionStore;
72
+ /** Offline verification of access tokens with the Authorization Server JWKS. */
73
+ readonly tokens: AndcoTokens;
74
+ /** Live Grant lookup for a token. */
75
+ readonly grants: AndcoGrants;
76
+ protected constructor(fields: {
77
+ config: AndcoConfig;
78
+ clientSecret: string | null;
79
+ globals: AndcoGlobals;
80
+ oauth: AndcoOAuth;
81
+ rest: AndcoRest;
82
+ intents: AndcoIntents;
83
+ auth: AndcoAuth;
84
+ session: AndcoSessionStore;
85
+ tokens: AndcoTokens;
86
+ grants: AndcoGrants;
87
+ });
88
+ /**
89
+ * Acts as the Resource Server identified by `resource`: verifies tokens and protects routes.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * const orders = andco.resource({ resource: "https://casa-norte.example/api" });
94
+ * app.use("/api/orders/*", orders.protect({ scopes: ["orders:read"] }).hono());
95
+ * ```
96
+ */
97
+ resource(options: {
98
+ resource: string;
99
+ resourceMetadata?: string;
100
+ }): AndcoResource;
101
+ }
48
102
  /**
49
103
  * An Andco Instance without resolved credentials.
50
104
  *
@@ -65,15 +119,7 @@ export type AndcoClientOptions = AndcoConfigInput & {
65
119
  * const bound = andco.with(await sessionFromCookie());
66
120
  * ```
67
121
  */
68
- export declare class AndcoClient {
69
- readonly config: AndcoConfig;
70
- readonly clientSecret: string | null;
71
- readonly globals: AndcoGlobals;
72
- readonly oauth: AndcoOAuth;
73
- readonly rest: AndcoRest;
74
- readonly intents: AndcoIntents;
75
- readonly auth: AndcoAuth;
76
- readonly session: AndcoSessionStore;
122
+ export declare class AndcoClient extends AndcoClientFields {
77
123
  /** Runtime discriminant: `false` here, `true` on the value `with(...)` returns. */
78
124
  readonly isAuthed: false;
79
125
  constructor(options: AndcoClientOptions);
@@ -132,4 +178,5 @@ export declare class AndcoClient {
132
178
  export type AndcoClientAuthed = Omit<AndcoClient, "isAuthed" | "with" | "authorized"> & AndcoCredentials & {
133
179
  readonly isAuthed: true;
134
180
  };
181
+ export {};
135
182
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -2,10 +2,63 @@ import { AndcoAuth } from "./auth.js";
2
2
  import { AndcoConfig as Config } from "./config.js";
3
3
  import { credentialsFromSession, isCredentials } from "./credentials.js";
4
4
  import { Result } from "./errors.js";
5
+ import { resolveGlobals } from "./globals.js";
6
+ import { AndcoGrants } from "./grants-api.js";
5
7
  import { AndcoIntents } from "./intents.js";
6
8
  import { AndcoOAuth } from "./oauth.js";
9
+ import { AndcoPresenter } from "./presenter.js";
10
+ import { AndcoResource } from "./resource.js";
7
11
  import { AndcoRest } from "./rest.js";
8
12
  import { AndcoSessionStore, browserRequired } from "./session-store.js";
13
+ import { AndcoTokens } from "./tokens.js";
14
+ /**
15
+ * The eight collaborator fields shared, verbatim, by {@link AndcoClient} and both of the authorized
16
+ * views behind `with(...)` and `authorized()`. Declaring them once here is what keeps adding a field
17
+ * to the instance from requiring three synchronized edits.
18
+ *
19
+ * This is a construction seam, not a claim that the three are interchangeable: each subclass still
20
+ * decides for itself — per the ADR-0023 invariants documented at `AndcoAuthedClientImpl` and
21
+ * `AndcoAuthorizedViewImpl` — whether a given field's value is freshly built or borrowed by reference
22
+ * from an owner. The base constructor only stores whatever it is handed.
23
+ */
24
+ class AndcoClientFields {
25
+ config;
26
+ clientSecret;
27
+ globals;
28
+ oauth;
29
+ rest;
30
+ intents;
31
+ auth;
32
+ session;
33
+ /** Offline verification of access tokens with the Authorization Server JWKS. */
34
+ tokens;
35
+ /** Live Grant lookup for a token. */
36
+ grants;
37
+ constructor(fields) {
38
+ this.config = fields.config;
39
+ this.clientSecret = fields.clientSecret;
40
+ this.globals = fields.globals;
41
+ this.oauth = fields.oauth;
42
+ this.rest = fields.rest;
43
+ this.intents = fields.intents;
44
+ this.auth = fields.auth;
45
+ this.session = fields.session;
46
+ this.tokens = fields.tokens;
47
+ this.grants = fields.grants;
48
+ }
49
+ /**
50
+ * Acts as the Resource Server identified by `resource`: verifies tokens and protects routes.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const orders = andco.resource({ resource: "https://casa-norte.example/api" });
55
+ * app.use("/api/orders/*", orders.protect({ scopes: ["orders:read"] }).hono());
56
+ * ```
57
+ */
58
+ resource(options) {
59
+ return new AndcoResource({ ...options, tokens: this.tokens, grants: this.grants });
60
+ }
61
+ }
9
62
  /**
10
63
  * An Andco Instance without resolved credentials.
11
64
  *
@@ -26,50 +79,52 @@ import { AndcoSessionStore, browserRequired } from "./session-store.js";
26
79
  * const bound = andco.with(await sessionFromCookie());
27
80
  * ```
28
81
  */
29
- export class AndcoClient {
30
- config;
31
- clientSecret;
32
- globals;
33
- oauth;
34
- rest;
35
- intents;
36
- auth;
37
- session;
82
+ export class AndcoClient extends AndcoClientFields {
38
83
  /** Runtime discriminant: `false` here, `true` on the value `with(...)` returns. */
39
84
  isAuthed = false;
40
85
  constructor(options) {
41
- this.config = Config.from(options);
42
- this.clientSecret = options.clientSecret ?? null;
43
- this.globals = {
44
- fetch: options.globals?.fetch ?? globalThis.fetch.bind(globalThis),
45
- crypto: options.globals?.crypto ?? globalThis.crypto,
46
- };
47
- this.oauth = new AndcoOAuth({
48
- config: this.config,
49
- fetch: this.globals.fetch,
50
- clientSecret: this.clientSecret,
86
+ const config = Config.from(options);
87
+ const clientSecret = options.clientSecret ?? null;
88
+ const globals = resolveGlobals(options.globals);
89
+ const oauth = new AndcoOAuth({
90
+ config,
91
+ globals,
92
+ clientSecret,
51
93
  transport: options.transport,
52
94
  });
53
95
  // The store is real even without a presenter: it is what lets `rest` stay one object across a
54
96
  // sign-in, and what gives `auth` something to operate on regardless of runtime.
55
- this.session = new AndcoSessionStore({
56
- config: this.config,
57
- oauth: this.oauth,
97
+ const session = new AndcoSessionStore({
98
+ config,
99
+ oauth,
58
100
  storage: options.storage,
59
101
  initialSession: options.initialSession,
60
- lock: options.lock,
102
+ inflight: options.inflight,
61
103
  source: options.source,
62
104
  persist: options.persist,
63
105
  });
64
- this.auth = new AndcoAuth({
65
- oauth: this.oauth,
66
- store: this.session,
67
- presenter: options.presenter ?? statelessPresenter(),
106
+ // One presenter for the whole instance: authorization and Intent are the same seam, so a
107
+ // runtime that can present one can present the other. Not public — nothing outside the
108
+ // instance chooses how it presents, and every mirror of this type would have to carry it.
109
+ const presenter = options.presenter ?? new AndcoPresenterStateless();
110
+ const auth = new AndcoAuth({
111
+ config,
112
+ oauth,
113
+ store: session,
114
+ presenter,
68
115
  persistTransaction: options.persistTransaction,
69
- crypto: this.globals.crypto,
116
+ globals,
70
117
  });
71
- this.rest = new AndcoRest({ config: this.config, fetch: this.globals.fetch, credentials: this.session });
72
- this.intents = new AndcoIntents(this.rest, this.globals.crypto);
118
+ const rest = new AndcoRest({ config, globals, credentials: session });
119
+ const intents = new AndcoIntents({
120
+ rest,
121
+ config,
122
+ presenter,
123
+ globals,
124
+ });
125
+ const tokens = new AndcoTokens({ config, globals });
126
+ const grants = new AndcoGrants({ config, globals });
127
+ super({ config, clientSecret, globals, oauth, rest, intents, auth, session, tokens, grants });
73
128
  }
74
129
  /**
75
130
  * Binds *explicit* credentials for one unit of work, owning its own REST surface.
@@ -131,45 +186,45 @@ export class AndcoClient {
131
186
  }
132
187
  }
133
188
  /** Presents nothing; every authorization attempt fails clearly instead of doing nothing silently. */
134
- function statelessPresenter() {
135
- return {
136
- async present() {
137
- return Result.fail(browserRequired());
138
- },
139
- };
189
+ class AndcoPresenterStateless extends AndcoPresenter {
190
+ async present() {
191
+ return Result.fail(browserRequired());
192
+ }
140
193
  }
141
194
  /**
142
195
  * The implementation behind `with(...)`. Not exported: `AndcoClientAuthed` is the entire public
143
196
  * contract, so a caller never has a reason to name this type.
144
197
  */
145
- class AndcoAuthedClientImpl {
198
+ class AndcoAuthedClientImpl extends AndcoClientFields {
146
199
  credentials;
147
200
  isAuthed = true;
148
- config;
149
- clientSecret;
150
- globals;
151
- oauth;
152
- rest;
153
- intents;
154
- auth;
155
- session;
156
201
  constructor(owner, credentials) {
157
- this.credentials = credentials;
158
- this.config = owner.config;
159
- this.clientSecret = owner.clientSecret;
160
- this.globals = owner.globals;
161
- this.oauth = owner.oauth;
162
- this.rest = new AndcoRest({ config: owner.config, fetch: owner.globals.fetch, credentials });
163
- this.intents = new AndcoIntents(this.rest, owner.globals.crypto);
202
+ const rest = new AndcoRest({ config: owner.config, globals: owner.globals, credentials });
203
+ // No presenter: `with(...)` is the per-request server shape, and a request presents nothing.
204
+ const intents = new AndcoIntents({ rest, config: owner.config, globals: owner.globals });
164
205
  // In-memory and unlinked from `credentials`: it exists only so `auth` has something to operate
165
206
  // on, since a bare token or a caller-supplied resolver carries no persistable session shape.
166
- this.session = new AndcoSessionStore({ config: owner.config, oauth: owner.oauth, persist: false });
167
- this.auth = new AndcoAuth({
207
+ const session = new AndcoSessionStore({ config: owner.config, oauth: owner.oauth, persist: false });
208
+ const auth = new AndcoAuth({
209
+ config: owner.config,
168
210
  oauth: owner.oauth,
169
- store: this.session,
170
- presenter: statelessPresenter(),
171
- crypto: owner.globals.crypto,
211
+ store: session,
212
+ presenter: new AndcoPresenterStateless(),
213
+ globals: owner.globals,
172
214
  });
215
+ super({
216
+ config: owner.config,
217
+ clientSecret: owner.clientSecret,
218
+ globals: owner.globals,
219
+ oauth: owner.oauth,
220
+ rest,
221
+ intents,
222
+ auth,
223
+ session,
224
+ tokens: owner.tokens,
225
+ grants: owner.grants,
226
+ });
227
+ this.credentials = credentials;
173
228
  }
174
229
  accessTokenFor(resource) {
175
230
  return this.credentials.accessTokenFor(resource);
@@ -181,27 +236,23 @@ class AndcoAuthedClientImpl {
181
236
  * unchanged across a sign-in, satisfying "one REST surface per instance" even where a framework
182
237
  * adapter hands out a fresh `AndcoClientAuthed` reference on every Authorized Identity change.
183
238
  */
184
- class AndcoAuthorizedViewImpl {
239
+ class AndcoAuthorizedViewImpl extends AndcoClientFields {
185
240
  owner;
186
241
  isAuthed = true;
187
- config;
188
- clientSecret;
189
- globals;
190
- oauth;
191
- rest;
192
- intents;
193
- auth;
194
- session;
195
242
  constructor(owner) {
243
+ super({
244
+ config: owner.config,
245
+ clientSecret: owner.clientSecret,
246
+ globals: owner.globals,
247
+ oauth: owner.oauth,
248
+ rest: owner.rest,
249
+ intents: owner.intents,
250
+ auth: owner.auth,
251
+ session: owner.session,
252
+ tokens: owner.tokens,
253
+ grants: owner.grants,
254
+ });
196
255
  this.owner = owner;
197
- this.config = owner.config;
198
- this.clientSecret = owner.clientSecret;
199
- this.globals = owner.globals;
200
- this.oauth = owner.oauth;
201
- this.rest = owner.rest;
202
- this.intents = owner.intents;
203
- this.auth = owner.auth;
204
- this.session = owner.session;
205
256
  }
206
257
  /** Delegates to the instance's own session store on every call, so a rotated token is still picked up. */
207
258
  accessTokenFor(resource) {
package/dist/config.d.ts CHANGED
@@ -5,7 +5,7 @@ export type AndcoTheme = "base";
5
5
  * An OAuth scope. Known Andco scopes are suggested; a Resource Server may define its own.
6
6
  *
7
7
  * The Protocol Contract already knows the scope vocabulary, so this is that type rather than a bare
8
- * `string`: an editor completes `bank_accounts:read` without closing the door on a scope Andco has
8
+ * `string`: an editor completes `offline_access` without closing the door on a scope Andco has
9
9
  * not shipped yet.
10
10
  */
11
11
  export type AndcoScope = AndCoScope;
@@ -75,6 +75,7 @@ export declare class AndcoConfig {
75
75
  widgetURL(release: number, surface?: "button"): URL;
76
76
  /** Whether an origin may participate in popup messaging for this instance. */
77
77
  allowsPopupOrigin(origin: string): boolean;
78
+ /** Serializes configuration to constructor input without credentials or runtime collaborators. */
78
79
  toJSON(): AndcoConfigInput;
79
80
  }
80
81
  /** Loopback over plain HTTP is accepted so local development and CLI callbacks work unmodified. */