@aooth/auth 0.1.18 → 0.1.19

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.
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_clock = require("./clock-Bl-H3eqE.cjs");
3
3
  const require_payload = require("./payload-BJjvj8AH.cjs");
4
- const require_auth_code_store = require("./auth-code-store-B_m51OSB.cjs");
4
+ const require_dynamic_client_store = require("./dynamic-client-store-DAStgv2j.cjs");
5
5
  let node_crypto = require("node:crypto");
6
6
  //#region src/atscript-db/authz-stores.ts
7
7
  /**
@@ -9,7 +9,7 @@ let node_crypto = require("node:crypto");
9
9
  * fresh opaque `handle`; `get` rejects (and lazily evicts) a past-expiry row;
10
10
  * `delete` drops it once consumed. PK is `handle`, so reads/deletes are O(1).
11
11
  */
12
- var PendingAuthorizationStoreAtscriptDb = class extends require_auth_code_store.PendingAuthorizationStore {
12
+ var PendingAuthorizationStoreAtscriptDb = class extends require_dynamic_client_store.PendingAuthorizationStore {
13
13
  table;
14
14
  clock;
15
15
  ttlMs;
@@ -32,7 +32,9 @@ var PendingAuthorizationStoreAtscriptDb = class extends require_auth_code_store.
32
32
  createdAt: now,
33
33
  expiresAt,
34
34
  ...rec.clientId !== void 0 && { clientId: rec.clientId },
35
+ ...rec.clientName !== void 0 && { clientName: rec.clientName },
35
36
  ...rec.clientState !== void 0 && { clientState: rec.clientState },
37
+ ...rec.resource !== void 0 && { resource: rec.resource },
36
38
  ...rec.scope !== void 0 && { scope: rec.scope },
37
39
  ...rec.nonce !== void 0 && { nonce: rec.nonce },
38
40
  ...rec.idToken !== void 0 && { idToken: rec.idToken },
@@ -69,7 +71,9 @@ function rowToPending(row) {
69
71
  expiresAt: row.expiresAt
70
72
  };
71
73
  if (row.clientId != null) out.clientId = row.clientId;
74
+ if (row.clientName != null) out.clientName = row.clientName;
72
75
  if (row.clientState != null) out.clientState = row.clientState;
76
+ if (row.resource != null) out.resource = row.resource;
73
77
  if (row.scope != null) out.scope = row.scope;
74
78
  if (row.nonce != null) out.nonce = row.nonce;
75
79
  if (row.idToken != null) out.idToken = row.idToken;
@@ -86,7 +90,7 @@ function rowToPending(row) {
86
90
  * back-button replay loses the race and gets `null`. (The delete is the claim,
87
91
  * so no version column is needed — `deleteOne(PK)` is atomic per the DB engine.)
88
92
  */
89
- var AuthCodeStoreAtscriptDb = class extends require_auth_code_store.AuthCodeStore {
93
+ var AuthCodeStoreAtscriptDb = class extends require_dynamic_client_store.AuthCodeStore {
90
94
  table;
91
95
  clock;
92
96
  ttlMs;
@@ -107,6 +111,7 @@ var AuthCodeStoreAtscriptDb = class extends require_auth_code_store.AuthCodeStor
107
111
  expiresAt: this.clock.now() + this.ttlMs,
108
112
  ...rec.clientId !== void 0 && { clientId: rec.clientId },
109
113
  ...rec.scope !== void 0 && { scope: rec.scope },
114
+ ...rec.resource !== void 0 && { resource: rec.resource },
110
115
  ...rec.nonce !== void 0 && { nonce: rec.nonce },
111
116
  ...rec.idToken !== void 0 && { idToken: rec.idToken },
112
117
  ...rec.accessToken !== void 0 && { accessToken: rec.accessToken },
@@ -134,12 +139,83 @@ function rowToAuthCode(row) {
134
139
  };
135
140
  if (row.clientId != null) out.clientId = row.clientId;
136
141
  if (row.scope != null) out.scope = row.scope;
142
+ if (row.resource != null) out.resource = row.resource;
137
143
  if (row.nonce != null) out.nonce = row.nonce;
138
144
  if (row.idToken != null) out.idToken = row.idToken;
139
145
  if (row.accessToken != null) out.accessToken = row.accessToken;
140
146
  if (row.audience != null) out.audience = row.audience;
141
147
  return out;
142
148
  }
149
+ /**
150
+ * Durable {@link DynamicClientStore}. Long-lived rows (a connector caches its
151
+ * `client_id` across grants). `touch` is the portable findOne + replaceOne
152
+ * (same trick as `CredentialStoreAtscriptDb.touch` — no engine-specific patch
153
+ * op); `deleteUnusedBefore` is one mass `deleteMany` over
154
+ * `createdAt < cutoff && lastUsedAt unset` — the never-used GC.
155
+ */
156
+ var DynamicClientStoreAtscriptDb = class extends require_dynamic_client_store.DynamicClientStore {
157
+ table;
158
+ clock;
159
+ constructor(opts) {
160
+ super();
161
+ this.table = opts.table;
162
+ this.clock = opts.clock ?? require_clock.defaultClock;
163
+ }
164
+ async create(rec) {
165
+ const row = {
166
+ clientId: (0, node_crypto.randomUUID)(),
167
+ redirectUris: JSON.stringify(rec.redirectUris),
168
+ tokenEndpointAuthMethod: rec.tokenEndpointAuthMethod,
169
+ grantTypes: JSON.stringify(rec.grantTypes),
170
+ responseTypes: JSON.stringify(rec.responseTypes),
171
+ createdAt: this.clock.now(),
172
+ ...rec.clientName !== void 0 && { clientName: rec.clientName },
173
+ ...rec.scope !== void 0 && { scope: rec.scope }
174
+ };
175
+ await this.table.insertOne(row);
176
+ return rowToDynamicClient(row);
177
+ }
178
+ async get(clientId) {
179
+ const row = await this.table.findOne({ filter: { clientId } });
180
+ return row ? rowToDynamicClient(row) : null;
181
+ }
182
+ async delete(clientId) {
183
+ const { deletedCount } = await this.table.deleteOne(clientId);
184
+ return deletedCount > 0;
185
+ }
186
+ async count() {
187
+ return this.table.count({});
188
+ }
189
+ async touch(clientId, at) {
190
+ const row = await this.table.findOne({ filter: { clientId } });
191
+ if (!row) return;
192
+ await this.table.replaceOne({
193
+ ...row,
194
+ lastUsedAt: at
195
+ });
196
+ }
197
+ async deleteUnusedBefore(cutoff) {
198
+ const { deletedCount } = await this.table.deleteMany({
199
+ createdAt: { $lt: cutoff },
200
+ lastUsedAt: { $exists: false }
201
+ });
202
+ return deletedCount;
203
+ }
204
+ };
205
+ function rowToDynamicClient(row) {
206
+ const out = {
207
+ clientId: row.clientId,
208
+ redirectUris: JSON.parse(row.redirectUris),
209
+ tokenEndpointAuthMethod: row.tokenEndpointAuthMethod,
210
+ grantTypes: JSON.parse(row.grantTypes),
211
+ responseTypes: JSON.parse(row.responseTypes),
212
+ createdAt: row.createdAt
213
+ };
214
+ if (row.clientName != null) out.clientName = row.clientName;
215
+ if (row.scope != null) out.scope = row.scope;
216
+ if (row.lastUsedAt != null) out.lastUsedAt = row.lastUsedAt;
217
+ return out;
218
+ }
143
219
  //#endregion
144
220
  //#region src/atscript-db/index.ts
145
221
  /**
@@ -263,4 +339,5 @@ function rowToState(row) {
263
339
  //#endregion
264
340
  exports.AuthCodeStoreAtscriptDb = AuthCodeStoreAtscriptDb;
265
341
  exports.CredentialStoreAtscriptDb = CredentialStoreAtscriptDb;
342
+ exports.DynamicClientStoreAtscriptDb = DynamicClientStoreAtscriptDb;
266
343
  exports.PendingAuthorizationStoreAtscriptDb = PendingAuthorizationStoreAtscriptDb;
@@ -1,6 +1,6 @@
1
1
  import { a as CredentialState, t as CredentialStore } from "./store-BG6m6oSJ.cjs";
2
2
  import { t as Clock } from "./clock-BjXa0LXb.cjs";
3
- import { a as NewAuthCode, c as PendingAuthorization, l as PendingAuthorizationStore, n as AuthCodeStore, s as NewPendingAuthorization, t as AuthCode } from "./auth-code-store-BZ88d_tq.cjs";
3
+ import { a as NewDynamicClient, f as NewPendingAuthorization, m as PendingAuthorizationStore, n as DynamicClientStore, o as AuthCode, p as PendingAuthorization, s as AuthCodeStore, t as DynamicClient, u as NewAuthCode } from "./dynamic-client-store-DzqdUwnw.cjs";
4
4
 
5
5
  //#region src/atscript-db/authz-stores.d.ts
6
6
  /**
@@ -22,7 +22,9 @@ interface PendingAuthorizationRow {
22
22
  redirectUri: string;
23
23
  codeChallenge: string;
24
24
  clientId?: string;
25
+ clientName?: string;
25
26
  clientState?: string;
27
+ resource?: string;
26
28
  scope?: string;
27
29
  nonce?: string;
28
30
  idToken?: boolean;
@@ -84,6 +86,7 @@ interface AuthCodeRow {
84
86
  redirectUri: string;
85
87
  clientId?: string;
86
88
  scope?: string;
89
+ resource?: string;
87
90
  nonce?: string;
88
91
  idToken?: boolean;
89
92
  accessToken?: boolean;
@@ -130,6 +133,71 @@ declare class AuthCodeStoreAtscriptDb extends AuthCodeStore {
130
133
  }>;
131
134
  consume(code: string): Promise<AuthCode | null>;
132
135
  }
136
+ /**
137
+ * Persisted row — mirrors `AoothDynamicClient` (`dynamic-client.as`). The
138
+ * three array fields are JSON-STRING columns (same opaque-string pattern as
139
+ * `tokenPolicy` above): a `string[]` column would need engine-specific array
140
+ * support, and all matching happens in `DynamicClientPolicy` after parse.
141
+ */
142
+ interface DynamicClientRow {
143
+ clientId: string;
144
+ clientName?: string;
145
+ /** `JSON.stringify(string[])`. */
146
+ redirectUris: string;
147
+ tokenEndpointAuthMethod: string;
148
+ /** `JSON.stringify(string[])`. */
149
+ grantTypes: string;
150
+ /** `JSON.stringify(string[])`. */
151
+ responseTypes: string;
152
+ scope?: string;
153
+ createdAt: number;
154
+ lastUsedAt?: number;
155
+ }
156
+ /** Structural surface of `AtscriptDbTable` for the dynamic-client adapter. */
157
+ interface DynamicClientTable {
158
+ insertOne(row: DynamicClientRow): Promise<{
159
+ insertedId: unknown;
160
+ }>;
161
+ findOne(query: {
162
+ filter: Record<string, unknown>;
163
+ }): Promise<DynamicClientRow | null>;
164
+ count(query: {
165
+ filter?: Record<string, unknown>;
166
+ }): Promise<number>;
167
+ replaceOne(row: DynamicClientRow): Promise<{
168
+ matchedCount: number;
169
+ modifiedCount: number;
170
+ }>;
171
+ deleteOne(idOrPk: unknown): Promise<{
172
+ deletedCount: number;
173
+ }>;
174
+ deleteMany(filter: Record<string, unknown>): Promise<{
175
+ deletedCount: number;
176
+ }>;
177
+ }
178
+ interface DynamicClientStoreAtscriptDbOptions {
179
+ table: DynamicClientTable;
180
+ /** Injectable clock for deterministic `createdAt`. Defaults to {@link defaultClock}. */
181
+ clock?: Clock;
182
+ }
183
+ /**
184
+ * Durable {@link DynamicClientStore}. Long-lived rows (a connector caches its
185
+ * `client_id` across grants). `touch` is the portable findOne + replaceOne
186
+ * (same trick as `CredentialStoreAtscriptDb.touch` — no engine-specific patch
187
+ * op); `deleteUnusedBefore` is one mass `deleteMany` over
188
+ * `createdAt < cutoff && lastUsedAt unset` — the never-used GC.
189
+ */
190
+ declare class DynamicClientStoreAtscriptDb extends DynamicClientStore {
191
+ private readonly table;
192
+ private readonly clock;
193
+ constructor(opts: DynamicClientStoreAtscriptDbOptions);
194
+ create(rec: NewDynamicClient): Promise<DynamicClient>;
195
+ get(clientId: string): Promise<DynamicClient | null>;
196
+ delete(clientId: string): Promise<boolean>;
197
+ count(): Promise<number>;
198
+ touch(clientId: string, at: number): Promise<void>;
199
+ deleteUnusedBefore(cutoff: number): Promise<number>;
200
+ }
133
201
  //#endregion
134
202
  //#region src/atscript-db/index.d.ts
135
203
  /**
@@ -219,4 +287,4 @@ declare class CredentialStoreAtscriptDb<TPayload extends object = object> implem
219
287
  }>>;
220
288
  }
221
289
  //#endregion
222
- export { AuthCodeRow, AuthCodeStoreAtscriptDb, AuthCodeStoreAtscriptDbOptions, AuthCodeTable, AuthCredentialRow, AuthCredentialTable, CredentialStoreAtscriptDb, PendingAuthorizationRow, PendingAuthorizationStoreAtscriptDb, PendingAuthorizationStoreAtscriptDbOptions, PendingAuthorizationTable };
290
+ export { AuthCodeRow, AuthCodeStoreAtscriptDb, AuthCodeStoreAtscriptDbOptions, AuthCodeTable, AuthCredentialRow, AuthCredentialTable, CredentialStoreAtscriptDb, DynamicClientRow, DynamicClientStoreAtscriptDb, DynamicClientStoreAtscriptDbOptions, DynamicClientTable, PendingAuthorizationRow, PendingAuthorizationStoreAtscriptDb, PendingAuthorizationStoreAtscriptDbOptions, PendingAuthorizationTable };
@@ -1,6 +1,6 @@
1
1
  import { a as CredentialState, t as CredentialStore } from "./store-BG6m6oSJ.mjs";
2
2
  import { t as Clock } from "./clock-BjXa0LXb.mjs";
3
- import { a as NewAuthCode, c as PendingAuthorization, l as PendingAuthorizationStore, n as AuthCodeStore, s as NewPendingAuthorization, t as AuthCode } from "./auth-code-store-Om5MXNUT.mjs";
3
+ import { a as NewDynamicClient, f as NewPendingAuthorization, m as PendingAuthorizationStore, n as DynamicClientStore, o as AuthCode, p as PendingAuthorization, s as AuthCodeStore, t as DynamicClient, u as NewAuthCode } from "./dynamic-client-store-oHEsQaJg.mjs";
4
4
 
5
5
  //#region src/atscript-db/authz-stores.d.ts
6
6
  /**
@@ -22,7 +22,9 @@ interface PendingAuthorizationRow {
22
22
  redirectUri: string;
23
23
  codeChallenge: string;
24
24
  clientId?: string;
25
+ clientName?: string;
25
26
  clientState?: string;
27
+ resource?: string;
26
28
  scope?: string;
27
29
  nonce?: string;
28
30
  idToken?: boolean;
@@ -84,6 +86,7 @@ interface AuthCodeRow {
84
86
  redirectUri: string;
85
87
  clientId?: string;
86
88
  scope?: string;
89
+ resource?: string;
87
90
  nonce?: string;
88
91
  idToken?: boolean;
89
92
  accessToken?: boolean;
@@ -130,6 +133,71 @@ declare class AuthCodeStoreAtscriptDb extends AuthCodeStore {
130
133
  }>;
131
134
  consume(code: string): Promise<AuthCode | null>;
132
135
  }
136
+ /**
137
+ * Persisted row — mirrors `AoothDynamicClient` (`dynamic-client.as`). The
138
+ * three array fields are JSON-STRING columns (same opaque-string pattern as
139
+ * `tokenPolicy` above): a `string[]` column would need engine-specific array
140
+ * support, and all matching happens in `DynamicClientPolicy` after parse.
141
+ */
142
+ interface DynamicClientRow {
143
+ clientId: string;
144
+ clientName?: string;
145
+ /** `JSON.stringify(string[])`. */
146
+ redirectUris: string;
147
+ tokenEndpointAuthMethod: string;
148
+ /** `JSON.stringify(string[])`. */
149
+ grantTypes: string;
150
+ /** `JSON.stringify(string[])`. */
151
+ responseTypes: string;
152
+ scope?: string;
153
+ createdAt: number;
154
+ lastUsedAt?: number;
155
+ }
156
+ /** Structural surface of `AtscriptDbTable` for the dynamic-client adapter. */
157
+ interface DynamicClientTable {
158
+ insertOne(row: DynamicClientRow): Promise<{
159
+ insertedId: unknown;
160
+ }>;
161
+ findOne(query: {
162
+ filter: Record<string, unknown>;
163
+ }): Promise<DynamicClientRow | null>;
164
+ count(query: {
165
+ filter?: Record<string, unknown>;
166
+ }): Promise<number>;
167
+ replaceOne(row: DynamicClientRow): Promise<{
168
+ matchedCount: number;
169
+ modifiedCount: number;
170
+ }>;
171
+ deleteOne(idOrPk: unknown): Promise<{
172
+ deletedCount: number;
173
+ }>;
174
+ deleteMany(filter: Record<string, unknown>): Promise<{
175
+ deletedCount: number;
176
+ }>;
177
+ }
178
+ interface DynamicClientStoreAtscriptDbOptions {
179
+ table: DynamicClientTable;
180
+ /** Injectable clock for deterministic `createdAt`. Defaults to {@link defaultClock}. */
181
+ clock?: Clock;
182
+ }
183
+ /**
184
+ * Durable {@link DynamicClientStore}. Long-lived rows (a connector caches its
185
+ * `client_id` across grants). `touch` is the portable findOne + replaceOne
186
+ * (same trick as `CredentialStoreAtscriptDb.touch` — no engine-specific patch
187
+ * op); `deleteUnusedBefore` is one mass `deleteMany` over
188
+ * `createdAt < cutoff && lastUsedAt unset` — the never-used GC.
189
+ */
190
+ declare class DynamicClientStoreAtscriptDb extends DynamicClientStore {
191
+ private readonly table;
192
+ private readonly clock;
193
+ constructor(opts: DynamicClientStoreAtscriptDbOptions);
194
+ create(rec: NewDynamicClient): Promise<DynamicClient>;
195
+ get(clientId: string): Promise<DynamicClient | null>;
196
+ delete(clientId: string): Promise<boolean>;
197
+ count(): Promise<number>;
198
+ touch(clientId: string, at: number): Promise<void>;
199
+ deleteUnusedBefore(cutoff: number): Promise<number>;
200
+ }
133
201
  //#endregion
134
202
  //#region src/atscript-db/index.d.ts
135
203
  /**
@@ -219,4 +287,4 @@ declare class CredentialStoreAtscriptDb<TPayload extends object = object> implem
219
287
  }>>;
220
288
  }
221
289
  //#endregion
222
- export { AuthCodeRow, AuthCodeStoreAtscriptDb, AuthCodeStoreAtscriptDbOptions, AuthCodeTable, AuthCredentialRow, AuthCredentialTable, CredentialStoreAtscriptDb, PendingAuthorizationRow, PendingAuthorizationStoreAtscriptDb, PendingAuthorizationStoreAtscriptDbOptions, PendingAuthorizationTable };
290
+ export { AuthCodeRow, AuthCodeStoreAtscriptDb, AuthCodeStoreAtscriptDbOptions, AuthCodeTable, AuthCredentialRow, AuthCredentialTable, CredentialStoreAtscriptDb, DynamicClientRow, DynamicClientStoreAtscriptDb, DynamicClientStoreAtscriptDbOptions, DynamicClientTable, PendingAuthorizationRow, PendingAuthorizationStoreAtscriptDb, PendingAuthorizationStoreAtscriptDbOptions, PendingAuthorizationTable };
@@ -1,6 +1,6 @@
1
1
  import { t as defaultClock } from "./clock-Bdsep_1j.mjs";
2
2
  import { t as credentialPayloadOf } from "./payload-D-DzH5-J.mjs";
3
- import { a as PendingAuthorizationStore, t as AuthCodeStore } from "./auth-code-store-Dqc4rEOx.mjs";
3
+ import { r as AuthCodeStore, s as PendingAuthorizationStore, t as DynamicClientStore } from "./dynamic-client-store-DLRfntHr.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  //#region src/atscript-db/authz-stores.ts
6
6
  /**
@@ -31,7 +31,9 @@ var PendingAuthorizationStoreAtscriptDb = class extends PendingAuthorizationStor
31
31
  createdAt: now,
32
32
  expiresAt,
33
33
  ...rec.clientId !== void 0 && { clientId: rec.clientId },
34
+ ...rec.clientName !== void 0 && { clientName: rec.clientName },
34
35
  ...rec.clientState !== void 0 && { clientState: rec.clientState },
36
+ ...rec.resource !== void 0 && { resource: rec.resource },
35
37
  ...rec.scope !== void 0 && { scope: rec.scope },
36
38
  ...rec.nonce !== void 0 && { nonce: rec.nonce },
37
39
  ...rec.idToken !== void 0 && { idToken: rec.idToken },
@@ -68,7 +70,9 @@ function rowToPending(row) {
68
70
  expiresAt: row.expiresAt
69
71
  };
70
72
  if (row.clientId != null) out.clientId = row.clientId;
73
+ if (row.clientName != null) out.clientName = row.clientName;
71
74
  if (row.clientState != null) out.clientState = row.clientState;
75
+ if (row.resource != null) out.resource = row.resource;
72
76
  if (row.scope != null) out.scope = row.scope;
73
77
  if (row.nonce != null) out.nonce = row.nonce;
74
78
  if (row.idToken != null) out.idToken = row.idToken;
@@ -106,6 +110,7 @@ var AuthCodeStoreAtscriptDb = class extends AuthCodeStore {
106
110
  expiresAt: this.clock.now() + this.ttlMs,
107
111
  ...rec.clientId !== void 0 && { clientId: rec.clientId },
108
112
  ...rec.scope !== void 0 && { scope: rec.scope },
113
+ ...rec.resource !== void 0 && { resource: rec.resource },
109
114
  ...rec.nonce !== void 0 && { nonce: rec.nonce },
110
115
  ...rec.idToken !== void 0 && { idToken: rec.idToken },
111
116
  ...rec.accessToken !== void 0 && { accessToken: rec.accessToken },
@@ -133,12 +138,83 @@ function rowToAuthCode(row) {
133
138
  };
134
139
  if (row.clientId != null) out.clientId = row.clientId;
135
140
  if (row.scope != null) out.scope = row.scope;
141
+ if (row.resource != null) out.resource = row.resource;
136
142
  if (row.nonce != null) out.nonce = row.nonce;
137
143
  if (row.idToken != null) out.idToken = row.idToken;
138
144
  if (row.accessToken != null) out.accessToken = row.accessToken;
139
145
  if (row.audience != null) out.audience = row.audience;
140
146
  return out;
141
147
  }
148
+ /**
149
+ * Durable {@link DynamicClientStore}. Long-lived rows (a connector caches its
150
+ * `client_id` across grants). `touch` is the portable findOne + replaceOne
151
+ * (same trick as `CredentialStoreAtscriptDb.touch` — no engine-specific patch
152
+ * op); `deleteUnusedBefore` is one mass `deleteMany` over
153
+ * `createdAt < cutoff && lastUsedAt unset` — the never-used GC.
154
+ */
155
+ var DynamicClientStoreAtscriptDb = class extends DynamicClientStore {
156
+ table;
157
+ clock;
158
+ constructor(opts) {
159
+ super();
160
+ this.table = opts.table;
161
+ this.clock = opts.clock ?? defaultClock;
162
+ }
163
+ async create(rec) {
164
+ const row = {
165
+ clientId: randomUUID(),
166
+ redirectUris: JSON.stringify(rec.redirectUris),
167
+ tokenEndpointAuthMethod: rec.tokenEndpointAuthMethod,
168
+ grantTypes: JSON.stringify(rec.grantTypes),
169
+ responseTypes: JSON.stringify(rec.responseTypes),
170
+ createdAt: this.clock.now(),
171
+ ...rec.clientName !== void 0 && { clientName: rec.clientName },
172
+ ...rec.scope !== void 0 && { scope: rec.scope }
173
+ };
174
+ await this.table.insertOne(row);
175
+ return rowToDynamicClient(row);
176
+ }
177
+ async get(clientId) {
178
+ const row = await this.table.findOne({ filter: { clientId } });
179
+ return row ? rowToDynamicClient(row) : null;
180
+ }
181
+ async delete(clientId) {
182
+ const { deletedCount } = await this.table.deleteOne(clientId);
183
+ return deletedCount > 0;
184
+ }
185
+ async count() {
186
+ return this.table.count({});
187
+ }
188
+ async touch(clientId, at) {
189
+ const row = await this.table.findOne({ filter: { clientId } });
190
+ if (!row) return;
191
+ await this.table.replaceOne({
192
+ ...row,
193
+ lastUsedAt: at
194
+ });
195
+ }
196
+ async deleteUnusedBefore(cutoff) {
197
+ const { deletedCount } = await this.table.deleteMany({
198
+ createdAt: { $lt: cutoff },
199
+ lastUsedAt: { $exists: false }
200
+ });
201
+ return deletedCount;
202
+ }
203
+ };
204
+ function rowToDynamicClient(row) {
205
+ const out = {
206
+ clientId: row.clientId,
207
+ redirectUris: JSON.parse(row.redirectUris),
208
+ tokenEndpointAuthMethod: row.tokenEndpointAuthMethod,
209
+ grantTypes: JSON.parse(row.grantTypes),
210
+ responseTypes: JSON.parse(row.responseTypes),
211
+ createdAt: row.createdAt
212
+ };
213
+ if (row.clientName != null) out.clientName = row.clientName;
214
+ if (row.scope != null) out.scope = row.scope;
215
+ if (row.lastUsedAt != null) out.lastUsedAt = row.lastUsedAt;
216
+ return out;
217
+ }
142
218
  //#endregion
143
219
  //#region src/atscript-db/index.ts
144
220
  /**
@@ -260,4 +336,4 @@ function rowToState(row) {
260
336
  return state;
261
337
  }
262
338
  //#endregion
263
- export { AuthCodeStoreAtscriptDb, CredentialStoreAtscriptDb, PendingAuthorizationStoreAtscriptDb };
339
+ export { AuthCodeStoreAtscriptDb, CredentialStoreAtscriptDb, DynamicClientStoreAtscriptDb, PendingAuthorizationStoreAtscriptDb };