@kubun/store-connector 0.15.0 → 0.17.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/lib/api.d.ts CHANGED
@@ -24,10 +24,21 @@ export type PendingAuthRecord = {
24
24
  /** A recovery recipient's DID in a form that can be encrypted to. See the table type. */
25
25
  controllerWrappableDID?: string | null;
26
26
  createdAt: number;
27
+ /** The callback's redirect target, needed to complete the token exchange. */
28
+ redirectURL: string;
29
+ /** The scopes requested at authorize time. */
30
+ scopes: Array<string>;
31
+ expiresAt: number;
27
32
  };
28
33
  export type ConnectorStoreAPI = {
29
34
  getSyncState(connectorName: string, ownerDID: string): Promise<SyncState | null>;
30
35
  setSyncState(connectorName: string, ownerDID: string, state: SyncState): Promise<void>;
36
+ updateSyncProgress(connectorName: string, ownerDID: string, progress: {
37
+ checkpoint: unknown;
38
+ lastSyncedAt: string;
39
+ entityCount: number;
40
+ error?: string | null;
41
+ }): Promise<void>;
31
42
  deleteSyncState(connectorName: string, ownerDID: string): Promise<void>;
32
43
  listSyncStatesForConnector(connectorName: string): Promise<Array<SyncStateEntry>>;
33
44
  createPendingAuth(record: PendingAuthRecord): Promise<void>;
package/lib/api.js CHANGED
@@ -27,9 +27,8 @@ export function createConnectorStore(db, _adapter) {
27
27
  return rowToSyncState(row);
28
28
  },
29
29
  async setSyncState (connectorName, ownerDID, state) {
30
- // The `lease_expires_at` column is dead (the lease moved to the workflow
31
- // engine); a fresh INSERT sets it null and a conflicting update leaves it
32
- // untouched.
30
+ // `lease_expires_at` is dead: a fresh INSERT sets it null and a conflicting
31
+ // update leaves it untouched.
33
32
  await db.insertInto('kubun_connector_sync_state').values({
34
33
  connector_name: connectorName,
35
34
  owner_did: ownerDID,
@@ -50,6 +49,28 @@ export function createConnectorStore(db, _adapter) {
50
49
  error: state.error ?? null
51
50
  })).execute();
52
51
  },
52
+ async updateSyncProgress (connectorName, ownerDID, progress) {
53
+ const checkpoint = progress.checkpoint != null ? JSON.stringify(progress.checkpoint) : null;
54
+ const error = progress.error ?? null;
55
+ await db.insertInto('kubun_connector_sync_state').values({
56
+ connector_name: connectorName,
57
+ owner_did: ownerDID,
58
+ checkpoint,
59
+ last_synced_at: progress.lastSyncedAt,
60
+ entity_count: progress.entityCount,
61
+ status: 'idle',
62
+ error,
63
+ lease_expires_at: null
64
+ }).onConflict((oc)=>oc.columns([
65
+ 'connector_name',
66
+ 'owner_did'
67
+ ]).doUpdateSet({
68
+ checkpoint,
69
+ last_synced_at: progress.lastSyncedAt,
70
+ entity_count: progress.entityCount,
71
+ error
72
+ })).execute();
73
+ },
53
74
  async deleteSyncState (connectorName, ownerDID) {
54
75
  await db.deleteFrom('kubun_connector_sync_state').where('connector_name', '=', connectorName).where('owner_did', '=', ownerDID).execute();
55
76
  },
@@ -70,7 +91,10 @@ export function createConnectorStore(db, _adapter) {
70
91
  owner_did: record.ownerDID,
71
92
  owner_wrappable_did: record.ownerWrappableDID ?? null,
72
93
  controller_wrappable_did: record.controllerWrappableDID ?? null,
73
- created_at: record.createdAt
94
+ created_at: record.createdAt,
95
+ redirect_url: record.redirectURL,
96
+ scopes: JSON.stringify(record.scopes),
97
+ expires_at: record.expiresAt
74
98
  }).execute();
75
99
  },
76
100
  async consumePendingAuth (state) {
@@ -85,11 +109,23 @@ export function createConnectorStore(db, _adapter) {
85
109
  ownerDID: row.owner_did,
86
110
  ownerWrappableDID: row.owner_wrappable_did,
87
111
  controllerWrappableDID: row.controller_wrappable_did,
88
- createdAt: row.created_at
112
+ createdAt: row.created_at,
113
+ redirectURL: row.redirect_url,
114
+ // ParseJSONResultsPlugin may have already parsed this column, same as
115
+ // `checkpoint` in `rowToSyncState`.
116
+ scopes: typeof row.scopes === 'string' ? JSON.parse(row.scopes) : row.scopes,
117
+ expiresAt: row.expires_at
89
118
  };
90
119
  },
91
120
  async deleteExpiredPendingAuth (cutoffMs) {
92
- await db.deleteFrom('kubun_connector_pending_auth').where('created_at', '<', cutoffMs).execute();
121
+ // Strict `<` matches @kokuin/oauth `completeAuthorization`, which treats a
122
+ // record as expired only when `expiresAt < now`. A record at exactly
123
+ // `expiresAt === cutoffMs` is therefore still consumable and is kept here
124
+ // (the next sweep, once `now > expiresAt`, removes it). This intentionally
125
+ // keeps one more record than the `PendingAuthStore.deleteExpired` port doc
126
+ // ("at or before nowMs") so the sweep and the consume check agree on the
127
+ // expiry boundary.
128
+ await db.deleteFrom('kubun_connector_pending_auth').where('expires_at', '<', cutoffMs).execute();
93
129
  }
94
130
  };
95
131
  }
package/lib/migrations.js CHANGED
@@ -19,7 +19,14 @@ export function getConnectorMigrations(ctx) {
19
19
  // callback so the mint can add the controller recovery wrap.
20
20
  .addColumn('controller_wrappable_did', t.text)// ms-epoch (Date.now) overflows int32 on Postgres; bigint holds it and
21
21
  // the adapter's int8 parser reads it back as a JS number.
22
- .addColumn('created_at', t.bigint, (col)=>col.notNull()).execute();
22
+ .addColumn('created_at', t.bigint, (col)=>col.notNull())// The callback's redirect target and the scopes requested at authorize
23
+ // time, both needed to complete the token exchange and to fill in
24
+ // `PendingAuthRecord`'s required fields.
25
+ .addColumn('redirect_url', t.text, (col)=>col.notNull())// JSON-encoded `Array<string>`.
26
+ .addColumn('scopes', t.text, (col)=>col.notNull())// ms-epoch (Date.now) overflows int32 on Postgres; bigint holds it and
27
+ // the adapter's int8 parser reads it back as a JS number. The sweep in
28
+ // `deleteExpiredPendingAuth` keys off this column, not `created_at`.
29
+ .addColumn('expires_at', t.bigint, (col)=>col.notNull()).execute();
23
30
  },
24
31
  async down (db) {
25
32
  await db.schema.dropTable('kubun_connector_pending_auth').ifExists().execute();
package/lib/tables.d.ts CHANGED
@@ -28,6 +28,10 @@ export type ConnectorPendingAuthTable = {
28
28
  */
29
29
  controller_wrappable_did: string | null;
30
30
  created_at: number;
31
+ redirect_url: string;
32
+ /** JSON-encoded `Array<string>`. See `PendingAuthRecord.scopes`. */
33
+ scopes: string;
34
+ expires_at: number;
31
35
  };
32
36
  export type ConnectorPendingAuth = Selectable<ConnectorPendingAuthTable>;
33
37
  export type ConnectorTables = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/store-connector",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,13 +15,13 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@kubun/db": "^0.15.0",
19
- "@kubun/db-adapter": "^0.15.0",
18
+ "@kubun/db": "^0.17.0",
19
+ "@kubun/db-adapter": "^0.17.0",
20
20
  "kysely": "^0.29.5"
21
21
  },
22
22
  "devDependencies": {
23
- "@kubun/db-better-sqlite": "^0.15.0",
24
- "@kubun/db-postgres": "^0.15.0",
23
+ "@kubun/db-better-sqlite": "^0.17.0",
24
+ "@kubun/db-postgres": "^0.17.0",
25
25
  "@testcontainers/postgresql": "^12.1.0"
26
26
  },
27
27
  "publishConfig": {
@@ -30,8 +30,8 @@
30
30
  "scripts": {
31
31
  "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
32
32
  "build:clean": "del lib",
33
- "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
34
- "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
33
+ "build:js": "del 'lib/**/*.js' && swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
34
+ "build:types": "del 'lib/**/*.d.ts' 'lib/**/*.d.ts.map' && tsc --emitDeclarationOnly --skipLibCheck",
35
35
  "build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
36
36
  "test": "pnpm run test:types && pnpm run test:unit",
37
37
  "test:types": "tsc --noEmit -p tsconfig.test.json",