@kubun/connector 0.6.0 → 0.8.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.
@@ -0,0 +1,6 @@
1
+ import type { ClustersRecord } from '@kubun/protocol';
2
+ /**
3
+ * Resolve a model name to its content-addressed model ID across clusters.
4
+ * Use this in providers to tag EntityBatch.modelID at module level.
5
+ */
6
+ export declare function resolveModelID(clusters: ClustersRecord, modelName: string): string;
@@ -0,0 +1 @@
1
+ export function resolveModelID(e,o){for(let r of Object.values(e)){let e=r.models.findIndex(e=>e.name===o);if(-1!==e){for(let[o,t]of Object.entries(r.record))if(t===e)return o}}throw Error(`Model "${o}" not found in any cluster`)}
package/lib/index.d.ts CHANGED
@@ -1,8 +1,4 @@
1
- export { type ConnectorActivatedEvent, type ConnectorDeactivatedEvent, ConnectorManager, type ConnectorManagerEvents, type ConnectorManagerParams, } from './manager.js';
1
+ export { resolveModelID } from './cluster-utils.js';
2
+ export { connectorClusters, connectorModelIDs } from './models/cluster.js';
2
3
  export { externalEntityDefinition } from './models/external-entity.js';
3
- export { createOAuthRoutes, type OAuthRoutesParams, type StateStore } from './oauth/routes.js';
4
- export type { Credential, CredentialProvider, OAuthProviderDefinition, OAuthState, TokenResponse, } from './oauth/types.js';
5
- export { type ConnectorActionParams, type ConnectorActionResult, type ConnectorAuthCompleteParams, type ConnectorAuthCompleteResult, type ConnectorAuthURLParams, type ConnectorAuthURLResult, type ConnectorProtocol, type ConnectorPushCompleteParams, type ConnectorPushCompleteResult, type ConnectorPushParams, type ConnectorPushResult, type ConnectorStatusParams, type ConnectorStatusResult, type ConnectorSyncParams, type ConnectorSyncReceive, type ConnectorSyncResult, connectorProtocol, } from './protocol.js';
6
- export { ConnectorRegistry } from './registry.js';
7
- export { computeEffectiveBoundary, isWithinBoundary, parseDuration, } from './sync/boundary.js';
8
- export type { ActionDefinition, ActionResult, ClientDataProvider, ClientProviderFactory, ConnectorAuth, ConnectorDefinition, DataProvider, DeviceConnectorAuth, DevicePermission, EntityBatch, EntityRecord, FetchChangesParams, FetchParams, OAuthConnectorAuth, ServerDataProvider, ServerProviderFactory, SyncBoundary, SyncCompleteEvent, SyncErrorEvent, SyncEvent, SyncProgressEvent, UserSyncPreferences, WebhookParams, WebhookRegistration, } from './types.js';
4
+ export type { ActionDefinition, ActionHandler, ActionHandlerFactory, ActionResult, ClientDataProvider, ClientProviderFactory, ConnectorAuth, ConnectorDefinition, ConnectorSyncEventPayload, Credential, CredentialProvider, DataProvider, DeviceConnectorAuth, DevicePermission, EntityBatch, EntityRecord, FetchChangesParams, FetchParams, OAuthConnectorAuth, OAuthProviderDefinition, OAuthState, ServerDataProvider, ServerProviderFactory, SyncBoundary, SyncCompleteEvent, SyncErrorEvent, SyncEvent, SyncProgressEvent, SyncState, SyncStateEntry, SyncStateStore, SyncStatus, TokenResponse, UserSyncPreferences, WebhookParams, WebhookRegistration, } from './types.js';
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- export{ConnectorManager}from"./manager.js";export{externalEntityDefinition}from"./models/external-entity.js";export{createOAuthRoutes}from"./oauth/routes.js";export{connectorProtocol}from"./protocol.js";export{ConnectorRegistry}from"./registry.js";export{computeEffectiveBoundary,isWithinBoundary,parseDuration}from"./sync/boundary.js";
1
+ export{resolveModelID}from"./cluster-utils.js";export{connectorClusters,connectorModelIDs}from"./models/cluster.js";export{externalEntityDefinition}from"./models/external-entity.js";
@@ -0,0 +1,4 @@
1
+ export declare const connectorClusters: import("@kubun/protocol").ClustersRecord;
2
+ export declare const connectorModelIDs: {
3
+ readonly ExternalEntity: string;
4
+ };
@@ -0,0 +1 @@
1
+ import{GraphBuilder as t,GraphModel as e}from"@kubun/protocol";import{externalEntityDefinition as o}from"./external-entity.js";let r=new t;r.add(o);export const connectorClusters=r.build();let n=e.fromClusters({clusters:connectorClusters});export const connectorModelIDs={ExternalEntity:n.aliases.ExternalEntity};
@@ -1,4 +1,3 @@
1
- import type { DocumentModelInput } from '@kubun/protocol';
2
1
  /**
3
2
  * ExternalEntity is the base interface for documents synced from external services.
4
3
  *
@@ -8,4 +7,43 @@ import type { DocumentModelInput } from '@kubun/protocol';
8
7
  * - sourceURL: Optional URL back to the entity in the source service
9
8
  * - lastSyncedAt: Timestamp of when this entity was last synced
10
9
  */
11
- export declare const externalEntityDefinition: DocumentModelInput;
10
+ export declare const externalEntityDefinition: {
11
+ readonly name: "ExternalEntity";
12
+ readonly behavior: "interface";
13
+ readonly schema: {
14
+ readonly type: "object";
15
+ readonly properties: {
16
+ readonly sourceService: {
17
+ readonly $ref: "#/definitions/SourceService";
18
+ };
19
+ readonly sourceID: {
20
+ readonly $ref: "#/definitions/SourceID";
21
+ };
22
+ readonly sourceURL: {
23
+ readonly $ref: "#/definitions/SourceURL";
24
+ };
25
+ readonly lastSyncedAt: {
26
+ readonly $ref: "#/definitions/DateTime";
27
+ };
28
+ };
29
+ readonly required: ["sourceService", "sourceID", "lastSyncedAt"];
30
+ readonly additionalProperties: true;
31
+ readonly definitions: {
32
+ readonly SourceService: {
33
+ readonly type: "string";
34
+ readonly title: "SourceService";
35
+ readonly minLength: 1;
36
+ readonly maxLength: 50;
37
+ };
38
+ readonly SourceID: {
39
+ readonly type: "string";
40
+ readonly title: "SourceID";
41
+ readonly minLength: 1;
42
+ readonly maxLength: 500;
43
+ };
44
+ readonly SourceURL: import("json-schema-typed").JSONSchema.String;
45
+ readonly DateTime: import("json-schema-typed").JSONSchema.String;
46
+ };
47
+ };
48
+ readonly fieldsMeta: {};
49
+ };
package/lib/types.d.ts CHANGED
@@ -1,5 +1,58 @@
1
- import type { ClusterModel } from '@kubun/protocol';
1
+ import type { ClustersRecord } from '@kubun/protocol';
2
2
  import type { JSONSchema } from 'json-schema-typed';
3
+ export type OAuthProviderDefinition = {
4
+ name: string;
5
+ authorizationEndpoint: string;
6
+ tokenEndpoint: string;
7
+ baseScopes?: Array<string>;
8
+ clientID?: string;
9
+ clientSecret?: string;
10
+ };
11
+ export type Credential = {
12
+ accessToken: string;
13
+ refreshToken?: string;
14
+ expiresAt?: Date;
15
+ scopes: Array<string>;
16
+ accountLabel?: string;
17
+ metadata?: Record<string, unknown>;
18
+ };
19
+ export type CredentialProvider = {
20
+ get(providerName: string, ownerDID: string): Promise<Credential | null>;
21
+ set(providerName: string, ownerDID: string, credential: Credential): Promise<void>;
22
+ delete(providerName: string, ownerDID: string): Promise<void>;
23
+ };
24
+ export type OAuthState = {
25
+ provider: string;
26
+ redirectURL: string;
27
+ connectors?: Array<string>;
28
+ nonce: string;
29
+ createdAt: number;
30
+ };
31
+ export type TokenResponse = {
32
+ access_token: string;
33
+ refresh_token?: string;
34
+ expires_in?: number;
35
+ token_type: string;
36
+ scope?: string;
37
+ };
38
+ export type SyncStatus = 'idle' | 'syncing' | 'error';
39
+ export type SyncState = {
40
+ checkpoint: unknown;
41
+ lastSyncedAt: string;
42
+ entityCount: number;
43
+ status: SyncStatus;
44
+ error?: string;
45
+ };
46
+ export type SyncStateEntry = SyncState & {
47
+ connectorName: string;
48
+ ownerDID: string;
49
+ };
50
+ export type SyncStateStore = {
51
+ get(connectorName: string, ownerDID: string): Promise<SyncState | null>;
52
+ set(connectorName: string, ownerDID: string, state: SyncState): Promise<void>;
53
+ delete(connectorName: string, ownerDID: string): Promise<void>;
54
+ listForConnector(connectorName: string): Promise<Array<SyncStateEntry>>;
55
+ };
3
56
  export type SyncBoundary = {
4
57
  maxAge?: string;
5
58
  since?: string;
@@ -16,6 +69,7 @@ export type EntityRecord = Record<string, unknown> & {
16
69
  sourceID: string;
17
70
  };
18
71
  export type EntityBatch = {
72
+ modelID: string;
19
73
  entities: Array<EntityRecord>;
20
74
  deleted?: Array<{
21
75
  sourceService: string;
@@ -59,6 +113,7 @@ export type DevicePermission = {
59
113
  export type OAuthConnectorAuth = {
60
114
  provider: string;
61
115
  scopes: Array<string>;
116
+ writeScopes?: Array<string>;
62
117
  };
63
118
  export type DeviceConnectorAuth = {
64
119
  provider: 'device';
@@ -79,6 +134,13 @@ export type ActionResult = {
79
134
  documentID?: string;
80
135
  deletedDocumentID?: string;
81
136
  };
137
+ export type ActionHandler = {
138
+ create(model: string, params: Record<string, unknown>): Promise<EntityRecord>;
139
+ update(model: string, sourceID: string, params: Record<string, unknown>): Promise<EntityRecord>;
140
+ };
141
+ export type ActionHandlerFactory = (config: {
142
+ credential: Credential;
143
+ }) => ActionHandler;
82
144
  export type ServerProviderFactory = (config: {
83
145
  credential: unknown;
84
146
  boundary: SyncBoundary;
@@ -89,9 +151,10 @@ export type ClientProviderFactory = (config: {
89
151
  export type ConnectorDefinition = {
90
152
  name: string;
91
153
  version: string;
92
- cluster: ClusterModel;
154
+ clusters: ClustersRecord;
93
155
  syncStrategies: Array<'polling' | 'webhook' | 'on-demand'>;
94
156
  actions?: Array<ActionDefinition>;
157
+ actionHandler?: ActionHandlerFactory;
95
158
  serverProvider?: ServerProviderFactory;
96
159
  clientProvider?: ClientProviderFactory;
97
160
  graphql?: {
@@ -121,3 +184,13 @@ export type SyncErrorEvent = {
121
184
  error: string;
122
185
  };
123
186
  export type SyncEvent = SyncProgressEvent | SyncCompleteEvent | SyncErrorEvent;
187
+ export type ConnectorSyncEventPayload = {
188
+ type: 'started' | 'progress' | 'completed' | 'error';
189
+ connectorName: string;
190
+ entitiesProcessed?: number;
191
+ entitiesFailed?: number;
192
+ totalProcessed?: number;
193
+ totalFailed?: number;
194
+ duration?: number;
195
+ error?: string;
196
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/connector",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "license": "see LICENSE.md",
5
5
  "keywords": [],
6
6
  "type": "module",
@@ -15,25 +15,17 @@
15
15
  ],
16
16
  "sideEffects": false,
17
17
  "dependencies": {
18
- "@enkaku/event": "^0.13.0",
19
- "@enkaku/protocol": "^0.13.0",
20
- "@enkaku/schema": "^0.13.0",
21
- "hono": "^4.11.9",
22
18
  "json-schema-typed": "^8.0.2",
23
- "@kubun/db": "^0.6.0",
24
- "@kubun/scalars": "^0.6.0",
25
- "@kubun/models": "^0.6.0",
26
- "@kubun/protocol": "^0.6.0",
27
- "@kubun/logger": "^0.6.0"
19
+ "@kubun/protocol": "^0.8.0",
20
+ "@kubun/scalars": "^0.8.0"
28
21
  },
29
- "devDependencies": {},
30
22
  "scripts": {
31
23
  "build:clean": "del lib",
32
24
  "build:js": "swc src -d ./lib --config-file ../../swc.json --strip-leading-paths",
33
25
  "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
34
26
  "build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
35
27
  "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
36
- "test:types": "tsc --noEmit",
28
+ "test:types": "tsc --noEmit -p tsconfig.test.json",
37
29
  "test:unit": "vitest run",
38
30
  "test": "pnpm run test:types && pnpm run test:unit"
39
31
  }
package/lib/manager.d.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { Logger } from '@kubun/logger';
2
- import type { ConnectorRegistry } from './registry.js';
3
- import type { SyncBoundary } from './types.js';
4
- export type ConnectorActivatedEvent = {
5
- connectorName: string;
6
- graphID: string;
7
- };
8
- export type ConnectorDeactivatedEvent = {
9
- connectorName: string;
10
- };
11
- export type ConnectorManagerEvents = {
12
- 'connector:activated': ConnectorActivatedEvent;
13
- 'connector:deactivated': ConnectorDeactivatedEvent;
14
- };
15
- export type ConnectorManagerParams = {
16
- registry: ConnectorRegistry;
17
- logger: Logger;
18
- defaults?: {
19
- boundary?: SyncBoundary;
20
- pollingInterval?: string;
21
- };
22
- };
23
- export declare class ConnectorManager {
24
- #private;
25
- constructor(params: ConnectorManagerParams);
26
- get defaults(): {
27
- boundary: SyncBoundary;
28
- pollingInterval: string;
29
- };
30
- activateForGraph(graphID: string, connectorNames: Array<string>): void;
31
- deactivateForGraph(graphID: string, connectorNames: Array<string>): void;
32
- isActive(connectorName: string): boolean;
33
- getActiveGraphs(connectorName: string): Array<string>;
34
- listActive(): Array<string>;
35
- onConnectorActivated(callback: (event: ConnectorActivatedEvent) => void): () => void;
36
- onConnectorDeactivated(callback: (event: ConnectorDeactivatedEvent) => void): () => void;
37
- }
package/lib/manager.js DELETED
@@ -1 +0,0 @@
1
- import{EventEmitter as e}from"@enkaku/event";export class ConnectorManager{#e;#t;#r=new Map;#a=new e;#i;constructor(e){this.#e=e.registry,this.#t=e.logger,this.#i={boundary:e.defaults?.boundary??{maxAge:"P90D"},pollingInterval:e.defaults?.pollingInterval??"PT5M"}}get defaults(){return this.#i}activateForGraph(e,t){for(let r of t){if(!this.#e.has(r))throw Error(`Connector "${r}" is not registered`);let t=this.#r.get(r),a=null==t||0===t.size;null==t&&(t=new Set,this.#r.set(r,t)),t.add(e),a&&(this.#t.info("Connector {name} activated by graph {graphID}",{name:r,graphID:e}),this.#a.emit("connector:activated",{connectorName:r,graphID:e}))}}deactivateForGraph(e,t){for(let r of t){let t=this.#r.get(r);null!=t&&(t.delete(e),0===t.size&&(this.#r.delete(r),this.#t.info("Connector {name} deactivated",{name:r}),this.#a.emit("connector:deactivated",{connectorName:r})))}}isActive(e){let t=this.#r.get(e);return null!=t&&t.size>0}getActiveGraphs(e){let t=this.#r.get(e);return t?Array.from(t):[]}listActive(){return Array.from(this.#r.keys()).filter(e=>this.isActive(e))}onConnectorActivated(e){return this.#a.on("connector:activated",e)}onConnectorDeactivated(e){return this.#a.on("connector:deactivated",e)}}
@@ -1,19 +0,0 @@
1
- import { Hono } from 'hono';
2
- import type { Credential, OAuthProviderDefinition, OAuthState } from './types.js';
3
- export type StateStore = {
4
- set(nonce: string, state: OAuthState): Promise<void> | void;
5
- get(nonce: string): Promise<OAuthState | null> | OAuthState | null;
6
- delete(nonce: string): Promise<void> | void;
7
- };
8
- export type OAuthRoutesParams = {
9
- providers: Array<OAuthProviderDefinition>;
10
- stateStore: StateStore;
11
- /** Maximum age of OAuth state in milliseconds (default: 10 minutes) */
12
- stateTTL?: number;
13
- onTokenReceived?: (params: {
14
- provider: OAuthProviderDefinition;
15
- state: OAuthState;
16
- credential: Credential;
17
- }) => Promise<void> | void;
18
- };
19
- export declare function createOAuthRoutes(params: OAuthRoutesParams): Hono;
@@ -1 +0,0 @@
1
- import{Hono as e}from"hono";export function createOAuthRoutes(r){let{providers:t,stateStore:a,stateTTL:i=6e5,onTokenReceived:o}=r,n=new Map(t.map(e=>[e.name,e])),s=new e;return s.get("/auth/:provider/start",async e=>{let r,t=e.req.param("provider"),i=n.get(t);if(!i)return e.json({error:`Unknown provider: ${t}`},404);if(!i.clientID)return e.json({error:`Provider ${t} is not configured with a clientID`},500);let o=e.req.query("redirect_uri");if(!o)return e.json({error:"redirect_uri is required"},400);let s=e.req.query("scope"),c=s?s.split(","):[],d=[...i.baseScopes??[],...c],p=(r=new Uint8Array(32),crypto.getRandomValues(r),Array.from(r,e=>e.toString(16).padStart(2,"0")).join("")),u={provider:t,redirectURL:o,nonce:p,createdAt:Date.now()};await a.set(p,u);let l=new URL(i.authorizationEndpoint);return l.searchParams.set("client_id",i.clientID),l.searchParams.set("redirect_uri",o),l.searchParams.set("response_type","code"),l.searchParams.set("state",p),d.length>0&&l.searchParams.set("scope",d.join(" ")),e.redirect(l.toString())}),s.get("/auth/:provider/callback",async e=>{let r=e.req.param("provider"),t=n.get(r);if(!t)return e.json({error:`Unknown provider: ${r}`},404);let s=e.req.query("code"),c=e.req.query("state"),d=e.req.query("error");if(d){let r=e.req.query("error_description");return e.json({error:d,error_description:r},400)}if(!s||!c)return e.json({error:"Missing code or state"},400);let p=await a.get(c);if(!p)return e.json({error:"Invalid or expired state"},400);if(Date.now()-p.createdAt>i)return await a.delete(c),e.json({error:"State has expired"},400);if(p.provider!==r)return e.json({error:"State provider mismatch"},400);let u=await fetch(t.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:s,redirect_uri:p.redirectURL,client_id:t.clientID??"",client_secret:t.clientSecret??""})});if(!u.ok){let r=await u.text();return e.json({error:"Token exchange failed",details:r},500)}let l=await u.json(),h={accessToken:l.access_token,refreshToken:l.refresh_token,expiresAt:l.expires_in?new Date(Date.now()+1e3*l.expires_in):void 0,scopes:l.scope?l.scope.split(" "):[]};await a.delete(c),o&&await o({provider:t,state:p,credential:h});let w=new URL(p.redirectURL);return w.searchParams.set("oauth_status","success"),w.searchParams.set("provider",r),e.redirect(w.toString())}),s}
@@ -1,35 +0,0 @@
1
- export type OAuthProviderDefinition = {
2
- name: string;
3
- authorizationEndpoint: string;
4
- tokenEndpoint: string;
5
- baseScopes?: Array<string>;
6
- clientID?: string;
7
- clientSecret?: string;
8
- };
9
- export type Credential = {
10
- accessToken: string;
11
- refreshToken?: string;
12
- expiresAt?: Date;
13
- scopes: Array<string>;
14
- accountLabel?: string;
15
- metadata?: Record<string, unknown>;
16
- };
17
- export type CredentialProvider = {
18
- get(providerName: string, ownerDID: string): Promise<Credential | null>;
19
- set(providerName: string, ownerDID: string, credential: Credential): Promise<void>;
20
- delete(providerName: string, ownerDID: string): Promise<void>;
21
- };
22
- export type OAuthState = {
23
- provider: string;
24
- redirectURL: string;
25
- connectors?: Array<string>;
26
- nonce: string;
27
- createdAt: number;
28
- };
29
- export type TokenResponse = {
30
- access_token: string;
31
- refresh_token?: string;
32
- expires_in?: number;
33
- token_type: string;
34
- scope?: string;
35
- };
@@ -1 +0,0 @@
1
- export{};
package/lib/protocol.d.ts DELETED
@@ -1,337 +0,0 @@
1
- import type { FromSchema } from '@enkaku/schema';
2
- export declare const connectorProtocol: {
3
- readonly 'connector/sync': {
4
- readonly type: "stream";
5
- readonly param: {
6
- readonly type: "object";
7
- readonly properties: {
8
- readonly connector: {
9
- readonly type: "string";
10
- readonly description: "Connector name";
11
- };
12
- readonly full: {
13
- readonly type: "boolean";
14
- readonly description: "Force full sync instead of incremental";
15
- };
16
- };
17
- readonly required: readonly ["connector"];
18
- readonly additionalProperties: false;
19
- };
20
- readonly receive: {
21
- readonly type: "object";
22
- readonly properties: {
23
- readonly type: {
24
- readonly type: "string";
25
- readonly enum: readonly ["progress", "entity", "error"];
26
- };
27
- readonly connectorName: {
28
- readonly type: "string";
29
- };
30
- readonly phase: {
31
- readonly type: "string";
32
- readonly enum: readonly ["initial", "incremental"];
33
- };
34
- readonly entitiesProcessed: {
35
- readonly type: "number";
36
- };
37
- readonly entitiesFailed: {
38
- readonly type: "number";
39
- };
40
- readonly currentBatch: {
41
- readonly type: "number";
42
- };
43
- readonly checkpoint: {};
44
- readonly entity: {
45
- readonly type: "object";
46
- };
47
- readonly documentID: {
48
- readonly type: "string";
49
- };
50
- readonly error: {
51
- readonly type: "string";
52
- };
53
- };
54
- readonly additionalProperties: false;
55
- };
56
- readonly result: {
57
- readonly type: "object";
58
- readonly properties: {
59
- readonly success: {
60
- readonly type: "boolean";
61
- };
62
- readonly totalProcessed: {
63
- readonly type: "number";
64
- };
65
- readonly totalFailed: {
66
- readonly type: "number";
67
- };
68
- readonly duration: {
69
- readonly type: "number";
70
- };
71
- };
72
- readonly required: readonly ["success", "totalProcessed", "totalFailed", "duration"];
73
- readonly additionalProperties: false;
74
- };
75
- };
76
- readonly 'connector/status': {
77
- readonly type: "request";
78
- readonly param: {
79
- readonly type: "object";
80
- readonly properties: {
81
- readonly connector: {
82
- readonly type: "string";
83
- readonly description: "Optional connector name to filter";
84
- };
85
- };
86
- readonly additionalProperties: false;
87
- };
88
- readonly result: {
89
- readonly type: "object";
90
- readonly properties: {
91
- readonly connectors: {
92
- readonly type: "array";
93
- readonly items: {
94
- readonly type: "object";
95
- readonly properties: {
96
- readonly name: {
97
- readonly type: "string";
98
- };
99
- readonly lastSyncedAt: {
100
- readonly type: "string";
101
- };
102
- readonly entityCount: {
103
- readonly type: "number";
104
- };
105
- readonly status: {
106
- readonly type: "string";
107
- readonly enum: readonly ["active", "inactive", "syncing", "error"];
108
- };
109
- readonly error: {
110
- readonly type: "string";
111
- };
112
- };
113
- readonly required: readonly ["name", "status"];
114
- readonly additionalProperties: false;
115
- };
116
- };
117
- };
118
- readonly required: readonly ["connectors"];
119
- readonly additionalProperties: false;
120
- };
121
- };
122
- readonly 'connector/action': {
123
- readonly type: "request";
124
- readonly param: {
125
- readonly type: "object";
126
- readonly properties: {
127
- readonly connector: {
128
- readonly type: "string";
129
- };
130
- readonly action: {
131
- readonly type: "string";
132
- };
133
- readonly input: {
134
- readonly type: "object";
135
- };
136
- };
137
- readonly required: readonly ["connector", "action", "input"];
138
- readonly additionalProperties: false;
139
- };
140
- readonly result: {
141
- readonly type: "object";
142
- readonly properties: {
143
- readonly raw: {
144
- readonly type: "object";
145
- };
146
- readonly entity: {
147
- readonly type: "object";
148
- };
149
- readonly documentID: {
150
- readonly type: "string";
151
- };
152
- readonly deletedDocumentID: {
153
- readonly type: "string";
154
- };
155
- };
156
- readonly additionalProperties: false;
157
- };
158
- };
159
- readonly 'connector/push': {
160
- readonly type: "request";
161
- readonly param: {
162
- readonly type: "object";
163
- readonly properties: {
164
- readonly connector: {
165
- readonly type: "string";
166
- };
167
- readonly batch: {
168
- readonly type: "object";
169
- readonly properties: {
170
- readonly entities: {
171
- readonly type: "array";
172
- readonly items: {
173
- readonly type: "object";
174
- };
175
- };
176
- readonly deleted: {
177
- readonly type: "array";
178
- readonly items: {
179
- readonly type: "object";
180
- readonly properties: {
181
- readonly sourceService: {
182
- readonly type: "string";
183
- };
184
- readonly sourceID: {
185
- readonly type: "string";
186
- };
187
- };
188
- readonly required: readonly ["sourceService", "sourceID"];
189
- };
190
- };
191
- readonly checkpoint: {};
192
- readonly hasMore: {
193
- readonly type: "boolean";
194
- };
195
- };
196
- readonly required: readonly ["entities", "hasMore"];
197
- readonly additionalProperties: false;
198
- };
199
- readonly checkpoint: {};
200
- };
201
- readonly required: readonly ["connector", "batch"];
202
- readonly additionalProperties: false;
203
- };
204
- readonly result: {
205
- readonly type: "object";
206
- readonly properties: {
207
- readonly processed: {
208
- readonly type: "number";
209
- };
210
- readonly failed: {
211
- readonly type: "number";
212
- };
213
- readonly documentIDs: {
214
- readonly type: "array";
215
- readonly items: {
216
- readonly type: "string";
217
- };
218
- };
219
- };
220
- readonly required: readonly ["processed", "failed", "documentIDs"];
221
- readonly additionalProperties: false;
222
- };
223
- };
224
- readonly 'connector/push-complete': {
225
- readonly type: "request";
226
- readonly param: {
227
- readonly type: "object";
228
- readonly properties: {
229
- readonly connector: {
230
- readonly type: "string";
231
- };
232
- readonly checkpoint: {};
233
- };
234
- readonly required: readonly ["connector", "checkpoint"];
235
- readonly additionalProperties: false;
236
- };
237
- readonly result: {
238
- readonly type: "object";
239
- readonly properties: {
240
- readonly acknowledged: {
241
- readonly type: "boolean";
242
- };
243
- };
244
- readonly required: readonly ["acknowledged"];
245
- readonly additionalProperties: false;
246
- };
247
- };
248
- readonly 'connector/auth-url': {
249
- readonly type: "request";
250
- readonly param: {
251
- readonly type: "object";
252
- readonly properties: {
253
- readonly provider: {
254
- readonly type: "string";
255
- };
256
- readonly redirectURL: {
257
- readonly type: "string";
258
- };
259
- readonly connectors: {
260
- readonly type: "array";
261
- readonly items: {
262
- readonly type: "string";
263
- };
264
- };
265
- };
266
- readonly required: readonly ["provider", "redirectURL"];
267
- readonly additionalProperties: false;
268
- };
269
- readonly result: {
270
- readonly type: "object";
271
- readonly properties: {
272
- readonly url: {
273
- readonly type: "string";
274
- };
275
- readonly state: {
276
- readonly type: "string";
277
- };
278
- };
279
- readonly required: readonly ["url", "state"];
280
- readonly additionalProperties: false;
281
- };
282
- };
283
- readonly 'connector/auth-complete': {
284
- readonly type: "request";
285
- readonly param: {
286
- readonly type: "object";
287
- readonly properties: {
288
- readonly provider: {
289
- readonly type: "string";
290
- };
291
- readonly code: {
292
- readonly type: "string";
293
- };
294
- readonly state: {
295
- readonly type: "string";
296
- };
297
- };
298
- readonly required: readonly ["provider", "code", "state"];
299
- readonly additionalProperties: false;
300
- };
301
- readonly result: {
302
- readonly type: "object";
303
- readonly properties: {
304
- readonly providerName: {
305
- readonly type: "string";
306
- };
307
- readonly accountLabel: {
308
- readonly type: "string";
309
- };
310
- readonly scopes: {
311
- readonly type: "array";
312
- readonly items: {
313
- readonly type: "string";
314
- };
315
- };
316
- };
317
- readonly required: readonly ["providerName", "scopes"];
318
- readonly additionalProperties: false;
319
- };
320
- };
321
- };
322
- export type ConnectorProtocol = typeof connectorProtocol;
323
- export type ConnectorSyncParams = FromSchema<ConnectorProtocol['connector/sync']['param']>;
324
- export type ConnectorSyncReceive = FromSchema<ConnectorProtocol['connector/sync']['receive']>;
325
- export type ConnectorSyncResult = FromSchema<ConnectorProtocol['connector/sync']['result']>;
326
- export type ConnectorStatusParams = FromSchema<ConnectorProtocol['connector/status']['param']>;
327
- export type ConnectorStatusResult = FromSchema<ConnectorProtocol['connector/status']['result']>;
328
- export type ConnectorActionParams = FromSchema<ConnectorProtocol['connector/action']['param']>;
329
- export type ConnectorActionResult = FromSchema<ConnectorProtocol['connector/action']['result']>;
330
- export type ConnectorPushParams = FromSchema<ConnectorProtocol['connector/push']['param']>;
331
- export type ConnectorPushResult = FromSchema<ConnectorProtocol['connector/push']['result']>;
332
- export type ConnectorPushCompleteParams = FromSchema<ConnectorProtocol['connector/push-complete']['param']>;
333
- export type ConnectorPushCompleteResult = FromSchema<ConnectorProtocol['connector/push-complete']['result']>;
334
- export type ConnectorAuthURLParams = FromSchema<ConnectorProtocol['connector/auth-url']['param']>;
335
- export type ConnectorAuthURLResult = FromSchema<ConnectorProtocol['connector/auth-url']['result']>;
336
- export type ConnectorAuthCompleteParams = FromSchema<ConnectorProtocol['connector/auth-complete']['param']>;
337
- export type ConnectorAuthCompleteResult = FromSchema<ConnectorProtocol['connector/auth-complete']['result']>;
package/lib/protocol.js DELETED
@@ -1 +0,0 @@
1
- export const connectorProtocol={"connector/sync":{type:"stream",param:{type:"object",properties:{connector:{type:"string",description:"Connector name"},full:{type:"boolean",description:"Force full sync instead of incremental"}},required:["connector"],additionalProperties:!1},receive:{type:"object",properties:{type:{type:"string",enum:["progress","entity","error"]},connectorName:{type:"string"},phase:{type:"string",enum:["initial","incremental"]},entitiesProcessed:{type:"number"},entitiesFailed:{type:"number"},currentBatch:{type:"number"},checkpoint:{},entity:{type:"object"},documentID:{type:"string"},error:{type:"string"}},additionalProperties:!1},result:{type:"object",properties:{success:{type:"boolean"},totalProcessed:{type:"number"},totalFailed:{type:"number"},duration:{type:"number"}},required:["success","totalProcessed","totalFailed","duration"],additionalProperties:!1}},"connector/status":{type:"request",param:{type:"object",properties:{connector:{type:"string",description:"Optional connector name to filter"}},additionalProperties:!1},result:{type:"object",properties:{connectors:{type:"array",items:{type:"object",properties:{name:{type:"string"},lastSyncedAt:{type:"string"},entityCount:{type:"number"},status:{type:"string",enum:["active","inactive","syncing","error"]},error:{type:"string"}},required:["name","status"],additionalProperties:!1}}},required:["connectors"],additionalProperties:!1}},"connector/action":{type:"request",param:{type:"object",properties:{connector:{type:"string"},action:{type:"string"},input:{type:"object"}},required:["connector","action","input"],additionalProperties:!1},result:{type:"object",properties:{raw:{type:"object"},entity:{type:"object"},documentID:{type:"string"},deletedDocumentID:{type:"string"}},additionalProperties:!1}},"connector/push":{type:"request",param:{type:"object",properties:{connector:{type:"string"},batch:{type:"object",properties:{entities:{type:"array",items:{type:"object"}},deleted:{type:"array",items:{type:"object",properties:{sourceService:{type:"string"},sourceID:{type:"string"}},required:["sourceService","sourceID"]}},checkpoint:{},hasMore:{type:"boolean"}},required:["entities","hasMore"],additionalProperties:!1},checkpoint:{}},required:["connector","batch"],additionalProperties:!1},result:{type:"object",properties:{processed:{type:"number"},failed:{type:"number"},documentIDs:{type:"array",items:{type:"string"}}},required:["processed","failed","documentIDs"],additionalProperties:!1}},"connector/push-complete":{type:"request",param:{type:"object",properties:{connector:{type:"string"},checkpoint:{}},required:["connector","checkpoint"],additionalProperties:!1},result:{type:"object",properties:{acknowledged:{type:"boolean"}},required:["acknowledged"],additionalProperties:!1}},"connector/auth-url":{type:"request",param:{type:"object",properties:{provider:{type:"string"},redirectURL:{type:"string"},connectors:{type:"array",items:{type:"string"}}},required:["provider","redirectURL"],additionalProperties:!1},result:{type:"object",properties:{url:{type:"string"},state:{type:"string"}},required:["url","state"],additionalProperties:!1}},"connector/auth-complete":{type:"request",param:{type:"object",properties:{provider:{type:"string"},code:{type:"string"},state:{type:"string"}},required:["provider","code","state"],additionalProperties:!1},result:{type:"object",properties:{providerName:{type:"string"},accountLabel:{type:"string"},scopes:{type:"array",items:{type:"string"}}},required:["providerName","scopes"],additionalProperties:!1}}};
package/lib/registry.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { ConnectorDefinition } from './types.js';
2
- export declare class ConnectorRegistry {
3
- #private;
4
- register(connector: ConnectorDefinition): void;
5
- unregister(name: string): void;
6
- get(name: string): ConnectorDefinition | undefined;
7
- has(name: string): boolean;
8
- list(): Array<string>;
9
- getAll(): Array<ConnectorDefinition>;
10
- }
package/lib/registry.js DELETED
@@ -1 +0,0 @@
1
- export class ConnectorRegistry{#e=new Map;register(e){if(this.#e.has(e.name))throw Error(`Connector "${e.name}" is already registered`);this.#e.set(e.name,e)}unregister(e){this.#e.delete(e)}get(e){return this.#e.get(e)}has(e){return this.#e.has(e)}list(){return Array.from(this.#e.keys())}getAll(){return Array.from(this.#e.values())}}
@@ -1,4 +0,0 @@
1
- import type { SyncBoundary } from '../types.js';
2
- export declare function parseDuration(duration: string): number | undefined;
3
- export declare function computeEffectiveBoundary(serverBoundary: SyncBoundary, userBoundary: SyncBoundary | undefined): SyncBoundary;
4
- export declare function isWithinBoundary(date: Date, boundary: SyncBoundary): boolean;
@@ -1 +0,0 @@
1
- let e=/^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;export function parseDuration(n){let i=e.exec(n);if(!i)return;let t=Number.parseInt(i[1]??"0",10),l=Number.parseInt(i[2]??"0",10),a=Number.parseInt(i[3]??"0",10),u=Number.parseInt(i[4]??"0",10);if(0!==t||0!==l||0!==a||0!==u)return((24*t+l)*60+a)*6e4+1e3*u}export function computeEffectiveBoundary(e,n){if(null==n)return e;let i={};if(null!=e.maxAge||null!=n.maxAge){let t=e.maxAge?parseDuration(e.maxAge):void 0,l=n.maxAge?parseDuration(n.maxAge):void 0;null!=t&&null!=l?i.maxAge=t<=l?e.maxAge:n.maxAge:i.maxAge=e.maxAge??n.maxAge}if(null!=e.since||null!=n.since){let t=e.since?new Date(e.since):void 0,l=n.since?new Date(n.since):void 0;null!=t&&null!=l?i.since=t>=l?e.since:n.since:i.since=e.since??n.since}if(null!=e.maxEntities||null!=n.maxEntities){let t=e.maxEntities,l=n.maxEntities;null!=t&&null!=l?i.maxEntities=Math.min(t,l):i.maxEntities=e.maxEntities??n.maxEntities}return i}export function isWithinBoundary(e,n){let i=Date.now();if(null!=n.maxAge){let t=parseDuration(n.maxAge);if(null!=t&&e.getTime()<i-t)return!1}if(null!=n.since){let i=new Date(n.since);if(e.getTime()<i.getTime())return!1}return!0}