@kubun/connector 0.16.0 → 0.17.1

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { clusterModelURNs, resolveModelID } from './cluster-utils.js';
2
2
  export { connectorClusters, connectorModelIDs } from './models/cluster.js';
3
3
  export { externalEntityDefinition } from './models/external-entity.js';
4
- export type { ActionDefinition, ActionHandler, ActionHandlerFactory, ActionResult, ClientDataProvider, ClientProviderFactory, ConnectorAuth, ConnectorDefinition, ConnectorSyncEventPayload, DataProvider, DeviceConnectorAuth, DevicePermission, EntityBatch, EntityRecord, FetchChangesParams, FetchParams, OAuthConnectorAuth, OAuthState, ServerDataProvider, ServerProviderFactory, SyncBoundary, SyncCompleteEvent, SyncErrorEvent, SyncEvent, SyncFailureSample, SyncProgressEvent, SyncState, SyncStateEntry, SyncStateStore, SyncStatus, UserSyncPreferences, WebhookParams, WebhookRegistration, WriteAttachment, } from './types.js';
4
+ export type { ActionDefinition, ActionHandler, ActionHandlerFactory, ActionResult, ClientDataProvider, ClientProviderFactory, ConnectorAuth, ConnectorDefinition, ConnectorSyncEventPayload, DataProvider, DeadLetter, DeviceConnectorAuth, DevicePermission, EntityBatch, EntityRecord, FetchChangesParams, FetchParams, JSONValue, OAuthConnectorAuth, OAuthState, PendingOp, Recovery, ServerDataProvider, ServerProviderFactory, SyncBoundary, SyncCompleteEvent, SyncErrorEvent, SyncEvent, SyncFailureSample, SyncProgressCASCurrent, SyncProgressCASResult, SyncProgressEvent, SyncProgressRecompute, SyncState, SyncStateEntry, SyncStateStore, SyncStatus, UserSyncPreferences, WebhookParams, WebhookRegistration, WriteAttachment, } from './types.js';
@@ -7,5 +7,5 @@ const graph = GraphModel.fromClusters({
7
7
  clusters: connectorClusters
8
8
  });
9
9
  export const connectorModelIDs = {
10
- ExternalEntity: graph.aliases.ExternalEntity
10
+ ExternalEntity: graph.aliasID('ExternalEntity')
11
11
  };
package/lib/types.d.ts CHANGED
@@ -9,6 +9,37 @@ export type OAuthState = {
9
9
  createdAt: number;
10
10
  };
11
11
  export type SyncStatus = 'idle' | 'syncing' | 'error';
12
+ export type JSONValue = string | number | boolean | null | {
13
+ [key: string]: JSONValue;
14
+ } | Array<JSONValue>;
15
+ export type PendingOp = {
16
+ docID: string;
17
+ kind: 'upsert' | 'delete';
18
+ payload: JSONValue;
19
+ attempts: number;
20
+ firstFailedAt: string;
21
+ lastError: {
22
+ code?: string;
23
+ message: string;
24
+ };
25
+ };
26
+ export type DeadLetter = {
27
+ docID: string | null;
28
+ sourceService: string;
29
+ sourceID: string;
30
+ kind: 'upsert' | 'delete';
31
+ attempts: number;
32
+ lastError: {
33
+ code?: string;
34
+ message: string;
35
+ };
36
+ deadLetteredAt: string;
37
+ reason: 'max-attempts' | 'payload-evicted' | 'unserializable' | 'unaddressable';
38
+ };
39
+ export type Recovery = {
40
+ pending: Array<PendingOp>;
41
+ deadLetters: Array<DeadLetter>;
42
+ };
12
43
  export type SyncState = {
13
44
  checkpoint: unknown;
14
45
  lastSyncedAt: string;
@@ -16,19 +47,33 @@ export type SyncState = {
16
47
  status: SyncStatus;
17
48
  error?: string;
18
49
  leaseExpiresAt?: number | null;
50
+ recovery?: Recovery | null;
51
+ version?: number;
52
+ incarnation?: string;
19
53
  };
20
54
  export type SyncStateEntry = SyncState & {
21
55
  connectorName: string;
22
56
  ownerDID: string;
23
57
  };
58
+ export type SyncProgressCASCurrent = {
59
+ checkpoint: unknown;
60
+ recovery: Recovery | null;
61
+ version: number;
62
+ incarnation: string;
63
+ };
64
+ export type SyncProgressCASResult = {
65
+ checkpoint: unknown;
66
+ recovery: Recovery | null;
67
+ lastSyncedAt: string;
68
+ entityCount: number;
69
+ error?: string | null;
70
+ };
71
+ export type SyncProgressRecompute = (current: SyncProgressCASCurrent | null) => SyncProgressCASResult | Promise<SyncProgressCASResult>;
24
72
  export type SyncStateStore = {
25
73
  get(connectorName: string, ownerDID: string): Promise<SyncState | null>;
26
74
  set(connectorName: string, ownerDID: string, state: SyncState): Promise<void>;
27
- updateSyncProgress(connectorName: string, ownerDID: string, progress: {
28
- checkpoint: unknown;
29
- lastSyncedAt: string;
30
- entityCount: number;
31
- error?: string;
75
+ updateSyncProgress(connectorName: string, ownerDID: string, recompute: SyncProgressRecompute, options?: {
76
+ maxRetries?: number;
32
77
  }): Promise<void>;
33
78
  delete(connectorName: string, ownerDID: string): Promise<void>;
34
79
  listForConnector(connectorName: string): Promise<Array<SyncStateEntry>>;
@@ -66,6 +111,20 @@ export type FetchParams = {
66
111
  export type FetchChangesParams = FetchParams & {
67
112
  lastSyncedAt?: Date;
68
113
  };
114
+ /**
115
+ * A source of external entities, driven by an opaque `checkpoint`.
116
+ *
117
+ * **Replay-stability contract (required).** Re-opening `fetchAll`/`fetchChanges`
118
+ * from the same `checkpoint` MUST re-emit the same operations — or a superset —
119
+ * addressed by `sourceService`+`sourceID`; it must never emit a narrower set. The
120
+ * sync engine advances the connector checkpoint before the durable workflow
121
+ * checkpoint, so a crash between the two re-drives `fetchChanges`/`fetchAll` from
122
+ * an older checkpoint; a stable replay makes that re-drive idempotent (imports
123
+ * upsert by unique key) with no dropped entities. Concretely: a time window MUST
124
+ * be an absolute bound carried in the checkpoint, never recomputed from `now` on
125
+ * each call — a `now`-relative window silently narrows on replay. (Server-side
126
+ * ordering may vary between calls: harmless, since ops are keyed by identity.)
127
+ */
69
128
  export type DataProvider = {
70
129
  fetchAll(params: FetchParams): AsyncIterable<EntityBatch>;
71
130
  fetchChanges(params: FetchChangesParams): AsyncIterable<EntityBatch>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/connector",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,9 +15,9 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@kubun/credential-types": "^0.16.0",
19
- "@kubun/protocol": "^0.16.0",
20
- "@kubun/scalars": "^0.16.0",
18
+ "@kubun/credential-types": "^0.17.0",
19
+ "@kubun/protocol": "^0.17.0",
20
+ "@kubun/scalars": "^0.17.0",
21
21
  "json-schema-typed": "^8.0.2"
22
22
  },
23
23
  "publishConfig": {
@@ -26,8 +26,8 @@
26
26
  "scripts": {
27
27
  "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
28
28
  "build:clean": "del lib",
29
- "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
30
- "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
29
+ "build:js": "del 'lib/**/*.js' && swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
30
+ "build:types": "del 'lib/**/*.d.ts' 'lib/**/*.d.ts.map' && tsc --emitDeclarationOnly --skipLibCheck",
31
31
  "build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
32
32
  "test": "pnpm run test:types && pnpm run test:unit",
33
33
  "test:types": "tsc --noEmit -p tsconfig.test.json",