@ontrove/sdk 0.1.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/define.d.ts +49 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +57 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/manifest.d.ts +52 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +144 -0
- package/dist/runtime.d.ts +70 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +132 -0
- package/dist/types.d.ts +250 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/package.json +52 -0
- package/src/define.ts +70 -0
- package/src/index.ts +57 -0
- package/src/manifest.ts +173 -0
- package/src/runtime.ts +195 -0
- package/src/types.ts +252 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAEV,iBAAiB,EAEjB,SAAS,EACT,cAAc,EACd,SAAS,EACV,MAAM,YAAY,CAAC;AAKpB;;;;;;GAMG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACrD,yEAAyE;IACzE,MAAM,CAAC,EAAE,CAAC,CAAC;IACX,sEAAsE;IACtE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,qFAAqF;IACrF,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,wFAAwF;IACxF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACpC,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,6EAA6E;IAC7E,MAAM,EAAE,SAAS,CAAC;IAClB,6EAA6E;IAC7E,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;IAClB,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AA+ED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,EAC5B,OAAO,GAAE,UAAU,CAAC,CAAC,CAAM,GAC1B,OAAO,CAAC,SAAS,CAAC,CAgCpB"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The local-run harness — what makes `trove connector dev` / `trove connector test`
|
|
3
|
+
* work (connectors/sdk-reference; connectors/cursors-and-sources).
|
|
4
|
+
*
|
|
5
|
+
* {@link runConnector} builds a {@link ConnectorContext} from a `{ config, cursor }`
|
|
6
|
+
* spec, invokes `sync(ctx)`, then normalizes and validates the result: it accepts
|
|
7
|
+
* either a `ConnectorSyncResult` or a bare `ConnectorDocument[]`, deduplicates by
|
|
8
|
+
* `id` (first occurrence wins, matching server-side `(source, id)` dedup), and
|
|
9
|
+
* validates every document's required fields, surfacing problems clearly instead
|
|
10
|
+
* of pushing a malformed payload to the cloud.
|
|
11
|
+
*
|
|
12
|
+
* It is the symmetric sibling of `@ontrove/mcp`'s `dispatch`/`toFetchHandler`: that
|
|
13
|
+
* runs a tool call end-to-end without the hosted runtime; this runs a connector
|
|
14
|
+
* sync end-to-end without the Mac app sync engine.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/** Allowed `contentType` values (matches the GraphQL `ContentType` enum). */
|
|
19
|
+
const CONTENT_TYPES = ['text', 'transcript', 'highlight', 'bookmark'];
|
|
20
|
+
/**
|
|
21
|
+
* Validate one document's required fields, throwing a clear, indexed error on the
|
|
22
|
+
* first problem. Mirrors the wire contract: `id` is required (→ `externalId`),
|
|
23
|
+
* and at least one of `text`/`audioUrl` must be present.
|
|
24
|
+
*
|
|
25
|
+
* @param doc - The document to validate.
|
|
26
|
+
* @param index - Its position in the returned array, for the message.
|
|
27
|
+
* @throws {Error} If a required field is missing or malformed.
|
|
28
|
+
*/
|
|
29
|
+
function validateDocument(doc, index) {
|
|
30
|
+
const where = `document[${String(index)}]`;
|
|
31
|
+
if (doc === null || typeof doc !== 'object') {
|
|
32
|
+
throw new Error(`${where} must be an object`);
|
|
33
|
+
}
|
|
34
|
+
if (typeof doc.id !== 'string' || doc.id.length === 0) {
|
|
35
|
+
throw new Error(`${where} is missing a non-empty string \`id\` (maps to externalId)`);
|
|
36
|
+
}
|
|
37
|
+
const hasText = typeof doc.text === 'string' && doc.text.length > 0;
|
|
38
|
+
const hasAudio = typeof doc.audioUrl === 'string' && doc.audioUrl.length > 0;
|
|
39
|
+
if (!hasText && !hasAudio) {
|
|
40
|
+
throw new Error(`${where} (id "${doc.id}") must provide \`text\` or \`audioUrl\` — at least one is required`);
|
|
41
|
+
}
|
|
42
|
+
if (doc.contentType !== undefined && !CONTENT_TYPES.includes(doc.contentType)) {
|
|
43
|
+
throw new Error(`${where} (id "${doc.id}") has invalid contentType "${String(doc.contentType)}"; ` +
|
|
44
|
+
`expected one of ${CONTENT_TYPES.join(', ')}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Normalize a `sync` return value to a {@link ConnectorSyncResult}, accepting a
|
|
49
|
+
* bare document array for convenience.
|
|
50
|
+
*
|
|
51
|
+
* @param value - The raw `sync` return value.
|
|
52
|
+
* @returns A normalized `{ documents, cursor? }` result.
|
|
53
|
+
* @throws {Error} If the value is neither an array nor a `{ documents }` object.
|
|
54
|
+
*/
|
|
55
|
+
function normalizeResult(value) {
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
return { documents: value };
|
|
58
|
+
}
|
|
59
|
+
if (value !== null && typeof value === 'object' && Array.isArray(value.documents)) {
|
|
60
|
+
return value.cursor === undefined
|
|
61
|
+
? { documents: value.documents }
|
|
62
|
+
: { documents: value.documents, cursor: value.cursor };
|
|
63
|
+
}
|
|
64
|
+
throw new Error('sync must return an array of documents or an object with a `documents` array');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Deduplicate documents by `id`, keeping the first occurrence (matching the
|
|
68
|
+
* server's `(source, id)` dedup, which skips re-returned ids).
|
|
69
|
+
*
|
|
70
|
+
* @param documents - The validated documents.
|
|
71
|
+
* @returns The deduped documents and the count dropped.
|
|
72
|
+
*/
|
|
73
|
+
function dedup(documents) {
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
const unique = [];
|
|
76
|
+
let duplicatesSkipped = 0;
|
|
77
|
+
for (const doc of documents) {
|
|
78
|
+
if (seen.has(doc.id)) {
|
|
79
|
+
duplicatesSkipped += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
seen.add(doc.id);
|
|
83
|
+
unique.push(doc);
|
|
84
|
+
}
|
|
85
|
+
return { unique, duplicatesSkipped };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Run a connector's `sync` against a built `ctx` and collect/validate the result.
|
|
89
|
+
*
|
|
90
|
+
* This is the harness the CLI drives for `trove connector dev` / `trove connector
|
|
91
|
+
* test`: it builds the {@link ConnectorContext} from `{ config, cursor }`, runs
|
|
92
|
+
* `sync(ctx)`, normalizes a bare-array return, validates every document's
|
|
93
|
+
* required fields (clear, indexed errors), and dedups by `id`. Errors thrown by
|
|
94
|
+
* `sync` itself propagate unchanged so the CLI can report a failed run exactly as
|
|
95
|
+
* the Mac app would.
|
|
96
|
+
*
|
|
97
|
+
* @typeParam C - The shape of the connector's typed `ctx.config` preferences.
|
|
98
|
+
* @param connector - The connector whose `sync` to run.
|
|
99
|
+
* @param options - The `{ config, cursor }` spec plus test injection points.
|
|
100
|
+
* @returns The validated, deduped {@link RunResult}.
|
|
101
|
+
* @throws {Error} If `sync` throws, returns a malformed value, or a document fails validation.
|
|
102
|
+
*/
|
|
103
|
+
export async function runConnector(connector, options = {}) {
|
|
104
|
+
const logs = [];
|
|
105
|
+
const cursor = options.cursor ?? { type: 'none' };
|
|
106
|
+
const fetchImpl = options.fetchImpl ?? ((url, init) => globalThis.fetch(url, init));
|
|
107
|
+
const now = options.now ?? (() => new Date());
|
|
108
|
+
const ctx = {
|
|
109
|
+
config: options.config ?? {},
|
|
110
|
+
cursor,
|
|
111
|
+
fetch: fetchImpl,
|
|
112
|
+
log: (...args) => {
|
|
113
|
+
if (options.logSink !== undefined) {
|
|
114
|
+
options.logSink(args);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
logs.push(args);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
now,
|
|
121
|
+
};
|
|
122
|
+
const raw = await connector.sync(ctx);
|
|
123
|
+
const result = normalizeResult(raw);
|
|
124
|
+
result.documents.forEach(validateDocument);
|
|
125
|
+
const { unique, duplicatesSkipped } = dedup(result.documents);
|
|
126
|
+
return {
|
|
127
|
+
documents: unique,
|
|
128
|
+
cursor: result.cursor ?? { type: 'none' },
|
|
129
|
+
logs,
|
|
130
|
+
duplicatesSkipped,
|
|
131
|
+
};
|
|
132
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for the `@ontrove/sdk` connector authoring library.
|
|
3
|
+
*
|
|
4
|
+
* These types describe the connector contract (see the connectors SDK reference
|
|
5
|
+
* and the cursors-and-sources guide): a connector is
|
|
6
|
+
* an object with a `sync(ctx)` method that fetches from a source and returns
|
|
7
|
+
* documents to index. The SDK owns the document shape (which maps 1:1 onto the
|
|
8
|
+
* `IngestDocumentInput` wire type), the typed `ctx` capability object, the
|
|
9
|
+
* watermark/cursor model, and the local-run harness the CLI drives.
|
|
10
|
+
*
|
|
11
|
+
* It is the symmetric sibling of `@ontrove/mcp`: a connector returns documents to be
|
|
12
|
+
* _stored_ (batch `sync`); an MCP server returns tool results to be _read live_.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The default content type Trove assigns a document when it omits `contentType`.
|
|
18
|
+
* Mirrors the GraphQL `ContentType` enum surfaced on `IngestDocumentInput`.
|
|
19
|
+
*/
|
|
20
|
+
export type ConnectorContentType = 'text' | 'transcript' | 'highlight' | 'bookmark';
|
|
21
|
+
/**
|
|
22
|
+
* A single document a connector returns from `sync`. The fields map **1:1** onto
|
|
23
|
+
* the GraphQL `IngestDocumentInput` the Mac app pushes via `ingestDocuments`:
|
|
24
|
+
*
|
|
25
|
+
* | SDK field | `IngestDocumentInput` field |
|
|
26
|
+
* |---------------|-----------------------------|
|
|
27
|
+
* | `id` | `externalId` (required) |
|
|
28
|
+
* | `title` | `title` |
|
|
29
|
+
* | `text` | `text` |
|
|
30
|
+
* | `audioUrl` | `audioUrl` |
|
|
31
|
+
* | `url` | `url` |
|
|
32
|
+
* | `author` | `author` |
|
|
33
|
+
* | `date` | `date` (ISO 8601 DateTime) |
|
|
34
|
+
* | `tags` | `tags` |
|
|
35
|
+
* | `metadata` | `metadata` (JSON) |
|
|
36
|
+
* | `contentType` | `contentType` (ContentType) |
|
|
37
|
+
*
|
|
38
|
+
* Dedup is keyed on `(source, id)`: returning the same `id` twice is safe and is
|
|
39
|
+
* skipped on the server, so `sync` can be idempotent and retried freely. At least
|
|
40
|
+
* one of `text` or `audioUrl` must be present — an audio-only document triggers
|
|
41
|
+
* transcription (Whisper), and the transcript becomes the document text.
|
|
42
|
+
*/
|
|
43
|
+
export interface ConnectorDocument {
|
|
44
|
+
/**
|
|
45
|
+
* The stable external identifier from the source system — the dedup key within
|
|
46
|
+
* the source. Maps to `IngestDocumentInput.externalId`. Use the native id (HN
|
|
47
|
+
* `objectID`, RSS `guid`, Notion page id), never a value that changes between
|
|
48
|
+
* runs.
|
|
49
|
+
*/
|
|
50
|
+
id: string;
|
|
51
|
+
/** Document title. Maps to `IngestDocumentInput.title`. */
|
|
52
|
+
title?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Full plain-text content. Maps to `IngestDocumentInput.text`. Strip HTML/markup
|
|
55
|
+
* for best search quality. Required unless `audioUrl` is set.
|
|
56
|
+
*/
|
|
57
|
+
text?: string;
|
|
58
|
+
/**
|
|
59
|
+
* URL to an audio file. Maps to `IngestDocumentInput.audioUrl`. Trove downloads
|
|
60
|
+
* and transcribes it; the transcript becomes the document text and the document
|
|
61
|
+
* is indexed as `contentType: transcript`. Provide instead of (or alongside)
|
|
62
|
+
* `text`.
|
|
63
|
+
*/
|
|
64
|
+
audioUrl?: string;
|
|
65
|
+
/** Canonical link back to the original. Maps to `IngestDocumentInput.url`. */
|
|
66
|
+
url?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Content author. Maps to `IngestDocumentInput.author`. For podcasts, set this
|
|
69
|
+
* to the show name (Trove convention).
|
|
70
|
+
*/
|
|
71
|
+
author?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Original creation date as an ISO 8601 string (e.g. `"2026-06-14T09:00:00Z"`).
|
|
74
|
+
* Maps to the `DateTime` `IngestDocumentInput.date`. Used for recency ranking
|
|
75
|
+
* and the "published" display.
|
|
76
|
+
*/
|
|
77
|
+
date?: string;
|
|
78
|
+
/** Array of string tags. Maps to `IngestDocumentInput.tags`. */
|
|
79
|
+
tags?: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Arbitrary connector-specific JSON. Maps to `IngestDocumentInput.metadata`.
|
|
82
|
+
* **Never** put credentials or auth headers here.
|
|
83
|
+
*/
|
|
84
|
+
metadata?: Record<string, unknown>;
|
|
85
|
+
/**
|
|
86
|
+
* One of `text`, `transcript`, `highlight`, `bookmark`. Maps to
|
|
87
|
+
* `IngestDocumentInput.contentType`. Defaults to `text` (transcribed audio
|
|
88
|
+
* becomes `transcript` automatically).
|
|
89
|
+
*/
|
|
90
|
+
contentType?: ConnectorContentType;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A typed watermark describing how a source resumes between syncs. The opaque
|
|
94
|
+
* `Source.cursor` string is parsed into one of these (see the cursors-and-sources
|
|
95
|
+
* guide, watermark types).
|
|
96
|
+
*
|
|
97
|
+
* - `date` — the source is time-ordered and supports "since <date>"
|
|
98
|
+
* filtering (RSS, most APIs); the newest `value` you push becomes the next
|
|
99
|
+
* cursor.
|
|
100
|
+
* - `idSet` — the source has monotonic ids but no reliable date filter; advance
|
|
101
|
+
* to the highest id seen (`max`), optionally tracking a recent id set.
|
|
102
|
+
* - `none` — re-fetch everything each run and rely purely on `(source, id)`
|
|
103
|
+
* dedup. Always correct, just less efficient.
|
|
104
|
+
*/
|
|
105
|
+
export type Watermark = {
|
|
106
|
+
readonly type: 'date';
|
|
107
|
+
readonly value: string;
|
|
108
|
+
} | {
|
|
109
|
+
readonly type: 'idSet';
|
|
110
|
+
readonly values: readonly string[];
|
|
111
|
+
readonly max?: string;
|
|
112
|
+
} | {
|
|
113
|
+
readonly type: 'none';
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* The result of a connector `sync`: the documents fetched this run and, optionally,
|
|
117
|
+
* the watermark the source should advance to. A connector may also return a bare
|
|
118
|
+
* `ConnectorDocument[]` for convenience — {@link runConnector} normalizes that to
|
|
119
|
+
* `{ documents }` with no cursor change.
|
|
120
|
+
*/
|
|
121
|
+
export interface ConnectorSyncResult {
|
|
122
|
+
/** The documents fetched this run, mapping 1:1 onto `IngestDocumentInput`. */
|
|
123
|
+
documents: ConnectorDocument[];
|
|
124
|
+
/**
|
|
125
|
+
* The watermark to advance the source cursor to. Omit (or return `{ type: 'none' }`)
|
|
126
|
+
* to leave the cursor unchanged. The cloud only advances if the new value is
|
|
127
|
+
* monotonically ahead (compare-and-swap).
|
|
128
|
+
*/
|
|
129
|
+
cursor?: Watermark;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The standard `fetch` signature the SDK exposes on {@link ConnectorContext.fetch}.
|
|
133
|
+
* Matches the platform `fetch` so existing code ports unchanged.
|
|
134
|
+
*/
|
|
135
|
+
export type FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;
|
|
136
|
+
/**
|
|
137
|
+
* The single argument to `sync` — a capability object: everything a connector
|
|
138
|
+
* reaches the outside world through is on `ctx`. There is no ambient authority.
|
|
139
|
+
*
|
|
140
|
+
* `ctx.credentials` (Keychain-resolved auth material) and `ctx.browser` (a
|
|
141
|
+
* Playwright browser for `needs_browser` connectors) are **PROPOSED**, not part
|
|
142
|
+
* of the shipped contract, and so are intentionally absent here.
|
|
143
|
+
*/
|
|
144
|
+
export interface ConnectorContext<C = Record<string, unknown>> {
|
|
145
|
+
/**
|
|
146
|
+
* The user's preference values, keyed by the field names declared in
|
|
147
|
+
* `manifest.json` `config`. **Preferences only — never credentials.** Feed
|
|
148
|
+
* URLs, usernames, section lists, and filters live here; auth material lives in
|
|
149
|
+
* the macOS Keychain, surfaced (PROPOSED) via `ctx.credentials`, never here.
|
|
150
|
+
*/
|
|
151
|
+
readonly config: C;
|
|
152
|
+
/**
|
|
153
|
+
* The source's current watermark (the position from the previous run), or
|
|
154
|
+
* `{ type: 'none' }` on the first sync. Read-only — advance the cursor by
|
|
155
|
+
* returning a new {@link Watermark} from `sync`, not by mutating this.
|
|
156
|
+
*/
|
|
157
|
+
readonly cursor: Watermark;
|
|
158
|
+
/**
|
|
159
|
+
* Behaves like the standard `fetch`. Prefer `ctx.fetch` over global `fetch` —
|
|
160
|
+
* in the Mac app it routes through per-connector timeouts, retry, and
|
|
161
|
+
* rate-limit handling, surfacing failures in the connector's error log.
|
|
162
|
+
*/
|
|
163
|
+
fetch: FetchLike;
|
|
164
|
+
/**
|
|
165
|
+
* Structured log entry, surfaced in the Mac app's connector logs and the
|
|
166
|
+
* `createSyncRun` audit record. Useful for reporting counts and progress.
|
|
167
|
+
*/
|
|
168
|
+
log(...args: unknown[]): void;
|
|
169
|
+
/**
|
|
170
|
+
* The current wall-clock time as a `Date`. Injected (rather than read from a
|
|
171
|
+
* global) so syncs are deterministic under test and the local-run harness.
|
|
172
|
+
*/
|
|
173
|
+
now(): Date;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The type a connector's default export satisfies. A connector is an object with
|
|
177
|
+
* a `sync` method that fetches from its source and returns documents to index.
|
|
178
|
+
*
|
|
179
|
+
* @typeParam C - The shape of the connector's typed `ctx.config` preferences.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* import { defineConnector } from '@ontrove/sdk';
|
|
184
|
+
*
|
|
185
|
+
* export default defineConnector({
|
|
186
|
+
* async sync(ctx) {
|
|
187
|
+
* const res = await ctx.fetch(ctx.config.feedUrl as string);
|
|
188
|
+
* return parse(await res.text());
|
|
189
|
+
* },
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export interface TroveConnector<C = Record<string, unknown>> {
|
|
194
|
+
/**
|
|
195
|
+
* The batch entry point. Fetches from the source and returns documents to
|
|
196
|
+
* index, optionally with a new cursor. May return a bare `ConnectorDocument[]`
|
|
197
|
+
* for convenience. Throw a plain `Error` to fail the run (the Mac app records
|
|
198
|
+
* the error and retries next tick).
|
|
199
|
+
*/
|
|
200
|
+
sync(ctx: ConnectorContext<C>): Promise<ConnectorSyncResult | ConnectorDocument[]>;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* A single field descriptor inside a manifest `config` object — describes one
|
|
204
|
+
* preference input shown in the connector's setup wizard.
|
|
205
|
+
*/
|
|
206
|
+
export interface ManifestConfigField {
|
|
207
|
+
/** Display label in the setup UI. */
|
|
208
|
+
label?: string;
|
|
209
|
+
/** Input type — `text`, `text[]`, `url`, `url[]`, `path`, `number`, `boolean`. */
|
|
210
|
+
type?: string;
|
|
211
|
+
/** Optional example text shown in the input. */
|
|
212
|
+
placeholder?: string;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* A connector `manifest.json` — declares what the connector is, how often it runs,
|
|
216
|
+
* and which preference fields the user fills in during setup
|
|
217
|
+
* (connectors/manifest reference).
|
|
218
|
+
*/
|
|
219
|
+
export interface ConnectorManifest {
|
|
220
|
+
/** Stable connector-type id; pattern `^[a-z0-9-]+$`. Required. */
|
|
221
|
+
id: string;
|
|
222
|
+
/** Human-readable display name. Required. */
|
|
223
|
+
name: string;
|
|
224
|
+
/** One-line directory description. */
|
|
225
|
+
description?: string;
|
|
226
|
+
/** A single emoji or an HTTPS URL to a square icon. */
|
|
227
|
+
icon?: string;
|
|
228
|
+
/** Semver version string. Required. */
|
|
229
|
+
version: string;
|
|
230
|
+
/** Who wrote the connector. */
|
|
231
|
+
author?: string;
|
|
232
|
+
/** Directory grouping — `reading`, `social`, `finance`, `dev`, `media`, etc. */
|
|
233
|
+
category?: string;
|
|
234
|
+
/**
|
|
235
|
+
* Human-readable sync cadence — `"every 6 hours"`, `"daily"`, `"on demand"`, or
|
|
236
|
+
* `null` for a live-only connector. Optional (default: on demand).
|
|
237
|
+
*/
|
|
238
|
+
schedule?: string | null;
|
|
239
|
+
/** The preference fields shown in the setup wizard. Preferences only — no credentials. */
|
|
240
|
+
config?: Record<string, ManifestConfigField>;
|
|
241
|
+
/** Whether the connector requires a Playwright browser. Default `false`. */
|
|
242
|
+
needs_browser?: boolean;
|
|
243
|
+
/** What kind of source this is — `feed`, `account`, `files`, `api`. */
|
|
244
|
+
kind?: string;
|
|
245
|
+
/** How the connector reaches the source — `http`, `browser`, `fs`. */
|
|
246
|
+
transport?: string;
|
|
247
|
+
/** Default content type for documents this connector produces. */
|
|
248
|
+
document_semantics?: ConnectorContentType;
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for the `@ontrove/sdk` connector authoring library.
|
|
3
|
+
*
|
|
4
|
+
* These types describe the connector contract (see the connectors SDK reference
|
|
5
|
+
* and the cursors-and-sources guide): a connector is
|
|
6
|
+
* an object with a `sync(ctx)` method that fetches from a source and returns
|
|
7
|
+
* documents to index. The SDK owns the document shape (which maps 1:1 onto the
|
|
8
|
+
* `IngestDocumentInput` wire type), the typed `ctx` capability object, the
|
|
9
|
+
* watermark/cursor model, and the local-run harness the CLI drives.
|
|
10
|
+
*
|
|
11
|
+
* It is the symmetric sibling of `@ontrove/mcp`: a connector returns documents to be
|
|
12
|
+
* _stored_ (batch `sync`); an MCP server returns tool results to be _read live_.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ontrove/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Thin standard library for authoring Trove connectors — defineConnector, the sync(ctx) capability object, the document shape, the local-run harness, and manifest validation.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Hollyburn Analytics Inc. <matt@hollyburnanalytics.com>",
|
|
8
|
+
"homepage": "https://ontrove.sh",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"email": "matt@hollyburnanalytics.com"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=20"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"trove",
|
|
17
|
+
"connector",
|
|
18
|
+
"sdk",
|
|
19
|
+
"knowledge-base",
|
|
20
|
+
"ingestion",
|
|
21
|
+
"etl"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"files": ["dist", "src", "CHANGELOG.md", "LICENSE", "README.md"],
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"scripts": {
|
|
37
|
+
"prepack": "tsc -p tsconfig.build.json",
|
|
38
|
+
"build": "tsc -p tsconfig.build.json",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"lint": "biome check .",
|
|
41
|
+
"lint:fix": "biome check --write .",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest --watch",
|
|
44
|
+
"test:coverage": "vitest run --coverage"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@biomejs/biome": "^2.4.8",
|
|
48
|
+
"@vitest/coverage-v8": "^4.1.1",
|
|
49
|
+
"typescript": "^6.0.2",
|
|
50
|
+
"vitest": "^4.1.1"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/define.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineConnector` — the single entry point for authoring a Trove connector
|
|
3
|
+
* (connectors/sdk-reference). It is the symmetric sibling of `defineMcpServer`
|
|
4
|
+
* in `@ontrove/mcp`.
|
|
5
|
+
*
|
|
6
|
+
* It is an identity function with light, eager validation: it confirms the value
|
|
7
|
+
* is a connector object with a `sync` method, so a typo (`snyc`, a missing
|
|
8
|
+
* `sync`, a non-function) fails at authoring/deploy time with a clear message
|
|
9
|
+
* rather than at the first scheduled run. It does **not** execute `sync` — that is
|
|
10
|
+
* {@link runConnector}'s job.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
ConnectorContext,
|
|
17
|
+
ConnectorDocument,
|
|
18
|
+
ConnectorSyncResult,
|
|
19
|
+
TroveConnector,
|
|
20
|
+
} from './types.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate and return a connector definition unchanged.
|
|
24
|
+
*
|
|
25
|
+
* Using `defineConnector(...)` (rather than a bare `satisfies TroveConnector`)
|
|
26
|
+
* gives an eager runtime check: the CLI calls this when loading a connector
|
|
27
|
+
* module so a malformed export is rejected before any sync runs. The returned
|
|
28
|
+
* value is the same object, with full inferred types preserved.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam C - The shape of the connector's typed `ctx.config` preferences.
|
|
31
|
+
* @param connector - The connector object to validate.
|
|
32
|
+
* @returns The same connector object.
|
|
33
|
+
* @throws {Error} If `connector` is not an object with a `sync` function.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* export default defineConnector({
|
|
38
|
+
* async sync(ctx) {
|
|
39
|
+
* const res = await ctx.fetch(ctx.config.feedUrl as string);
|
|
40
|
+
* return parseRss(await res.text());
|
|
41
|
+
* },
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function defineConnector<C = Record<string, unknown>>(
|
|
46
|
+
connector: TroveConnector<C>,
|
|
47
|
+
): TroveConnector<C> {
|
|
48
|
+
if (connector === null || typeof connector !== 'object') {
|
|
49
|
+
throw new Error('defineConnector: expected a connector object with a `sync` method');
|
|
50
|
+
}
|
|
51
|
+
if (typeof (connector as { sync?: unknown }).sync !== 'function') {
|
|
52
|
+
throw new Error('defineConnector: connector must have a `sync(ctx)` function');
|
|
53
|
+
}
|
|
54
|
+
return connector;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A connector authored inline as a single `sync` function, for the common case
|
|
59
|
+
* where the connector has no other members. Equivalent to
|
|
60
|
+
* `defineConnector({ sync })`.
|
|
61
|
+
*
|
|
62
|
+
* @typeParam C - The shape of the connector's typed `ctx.config` preferences.
|
|
63
|
+
* @param sync - The connector's `sync(ctx)` implementation.
|
|
64
|
+
* @returns A validated {@link TroveConnector} wrapping `sync`.
|
|
65
|
+
*/
|
|
66
|
+
export function defineSync<C = Record<string, unknown>>(
|
|
67
|
+
sync: (ctx: ConnectorContext<C>) => Promise<ConnectorSyncResult | ConnectorDocument[]>,
|
|
68
|
+
): TroveConnector<C> {
|
|
69
|
+
return defineConnector<C>({ sync });
|
|
70
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ontrove/sdk` — the thin standard library for authoring **Trove connectors**.
|
|
3
|
+
* You write a `sync(ctx)` that fetches from a source and returns documents; the
|
|
4
|
+
* SDK owns the document shape (which maps 1:1 onto `IngestDocumentInput`), the
|
|
5
|
+
* typed `ctx` capability object, the watermark/cursor model, the local-run
|
|
6
|
+
* harness the CLI drives, and manifest validation.
|
|
7
|
+
*
|
|
8
|
+
* It is the symmetric sibling of `@ontrove/mcp`: a connector returns documents to
|
|
9
|
+
* be _stored_ (`defineConnector` + `sync`); an MCP server returns tool results to
|
|
10
|
+
* be _read live_ (`defineMcpServer`).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { defineConnector } from '@ontrove/sdk';
|
|
15
|
+
*
|
|
16
|
+
* export default defineConnector({
|
|
17
|
+
* async sync(ctx) {
|
|
18
|
+
* const res = await ctx.fetch('https://hn.algolia.com/api/v1/search?tags=front_page');
|
|
19
|
+
* const { hits } = await res.json();
|
|
20
|
+
* return hits.map((hit) => ({
|
|
21
|
+
* id: hit.objectID,
|
|
22
|
+
* title: hit.title,
|
|
23
|
+
* text: hit.story_text ?? hit.title,
|
|
24
|
+
* url: hit.url,
|
|
25
|
+
* author: hit.author,
|
|
26
|
+
* date: new Date(hit.created_at_i * 1000).toISOString(),
|
|
27
|
+
* contentType: 'bookmark',
|
|
28
|
+
* }));
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export { defineConnector, defineSync } from './define.js';
|
|
37
|
+
export {
|
|
38
|
+
isCredentialConfigKey,
|
|
39
|
+
type ManifestValidationResult,
|
|
40
|
+
validateConnectorManifest,
|
|
41
|
+
} from './manifest.js';
|
|
42
|
+
export {
|
|
43
|
+
type RunOptions,
|
|
44
|
+
type RunResult,
|
|
45
|
+
runConnector,
|
|
46
|
+
} from './runtime.js';
|
|
47
|
+
export type {
|
|
48
|
+
ConnectorContentType,
|
|
49
|
+
ConnectorContext,
|
|
50
|
+
ConnectorDocument,
|
|
51
|
+
ConnectorManifest,
|
|
52
|
+
ConnectorSyncResult,
|
|
53
|
+
FetchLike,
|
|
54
|
+
ManifestConfigField,
|
|
55
|
+
TroveConnector,
|
|
56
|
+
Watermark,
|
|
57
|
+
} from './types.js';
|