@ontrove/sdk 0.1.0 → 0.3.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/CHANGELOG.md +25 -0
- package/README.md +34 -33
- package/dist/define.d.ts +21 -21
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +25 -25
- package/dist/index.d.ts +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/manifest.d.ts +6 -6
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +7 -7
- package/dist/runtime.d.ts +21 -21
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +15 -15
- package/dist/types.d.ts +57 -55
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -7
- package/package.json +18 -5
- package/src/define.ts +28 -35
- package/src/index.ts +17 -16
- package/src/manifest.ts +10 -10
- package/src/runtime.ts +34 -34
- package/src/types.ts +57 -55
package/src/manifest.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (see the
|
|
2
|
+
* Source manifest validation for `trove source validate`
|
|
3
|
+
* (see the sources manifest reference).
|
|
4
4
|
*
|
|
5
5
|
* Two checks live here:
|
|
6
6
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `id` pattern (`^[a-z0-9-]+$`), and well-formedness of `config`.
|
|
9
9
|
* 2. **Credential-key lint** — the same spirit as the server-side
|
|
10
10
|
* `validateConfig` (`src/graphql/validate-config.ts`, invariant #6): a
|
|
11
|
-
*
|
|
11
|
+
* source's `config` holds **user preferences only, never credentials**.
|
|
12
12
|
* The cloud rejects writes whose config contains credential-shaped keys, so
|
|
13
13
|
* the SDK lints the manifest locally to catch the mistake before deploy. Auth
|
|
14
14
|
* material belongs in the (PROPOSED) `auth` block — surfaced from the macOS
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
* @module
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import type {
|
|
23
|
+
import type { ManifestConfigField, SourceManifest } from './types.js';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Credential-shaped key patterns that may NOT appear in a
|
|
26
|
+
* Credential-shaped key patterns that may NOT appear in a source's `config`
|
|
27
27
|
* (invariant #6). Mirrors the authoritative cloud deny-list in
|
|
28
28
|
* `src/graphql/validate-config.ts`. Matched case-insensitively and
|
|
29
29
|
* separator-insensitively as substrings, so `apiKey`, `api_key`, `access_token`,
|
|
@@ -109,13 +109,13 @@ function validateConfigBlock(config: Record<string, ManifestConfigField>, errors
|
|
|
109
109
|
const unique = [...new Set(offending)];
|
|
110
110
|
errors.push(
|
|
111
111
|
`manifest.config must not contain credential-shaped key(s): ${unique.join(', ')}. ` +
|
|
112
|
-
'
|
|
112
|
+
'Source credentials belong in the macOS Keychain (declared as `auth`), not in `config`.',
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* The outcome of {@link
|
|
118
|
+
* The outcome of {@link validateSourceManifest}.
|
|
119
119
|
*/
|
|
120
120
|
export interface ManifestValidationResult {
|
|
121
121
|
/** True when no errors were found. */
|
|
@@ -125,7 +125,7 @@ export interface ManifestValidationResult {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
* Validate a
|
|
128
|
+
* Validate a source `manifest.json` for `trove source validate`.
|
|
129
129
|
*
|
|
130
130
|
* Checks required fields and the `id`/`version` patterns, validates the `config`
|
|
131
131
|
* descriptors, and runs the credential-key lint (rejecting credential-shaped
|
|
@@ -136,14 +136,14 @@ export interface ManifestValidationResult {
|
|
|
136
136
|
* @param manifest - The parsed manifest object to validate.
|
|
137
137
|
* @returns `{ valid, errors }` — `valid: true` with an empty `errors` array on success.
|
|
138
138
|
*/
|
|
139
|
-
export function
|
|
139
|
+
export function validateSourceManifest(manifest: unknown): ManifestValidationResult {
|
|
140
140
|
const errors: string[] = [];
|
|
141
141
|
|
|
142
142
|
if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
143
143
|
return { valid: false, errors: ['manifest must be a JSON object'] };
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
const m = manifest as Partial<
|
|
146
|
+
const m = manifest as Partial<SourceManifest>;
|
|
147
147
|
|
|
148
148
|
requireString(m.id, 'id', errors);
|
|
149
149
|
if (typeof m.id === 'string' && m.id.length > 0 && !ID_RE.test(m.id)) {
|
package/src/runtime.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The local-run harness — what makes `trove
|
|
3
|
-
* work (
|
|
2
|
+
* The local-run harness — what makes `trove source dev` / `trove source test`
|
|
3
|
+
* work (sources/sdk-reference; the cursors guide).
|
|
4
4
|
*
|
|
5
|
-
* {@link
|
|
5
|
+
* {@link runSource} builds a {@link SourceContext} from a `{ config, cursor }`
|
|
6
6
|
* spec, invokes `sync(ctx)`, then normalizes and validates the result: it accepts
|
|
7
|
-
* either a `
|
|
8
|
-
* `id` (first occurrence wins, matching server-side `(
|
|
7
|
+
* either a `SourceSyncResult` or a bare `SourceDocument[]`, deduplicates by
|
|
8
|
+
* `id` (first occurrence wins, matching server-side `(feed, id)` dedup), and
|
|
9
9
|
* validates every document's required fields, surfacing problems clearly instead
|
|
10
10
|
* of pushing a malformed payload to the cloud.
|
|
11
11
|
*
|
|
12
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
|
|
13
|
+
* runs a tool call end-to-end without the hosted runtime; this runs a source
|
|
14
14
|
* sync end-to-end without the Mac app sync engine.
|
|
15
15
|
*
|
|
16
16
|
* @module
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type {
|
|
20
|
-
ConnectorContext,
|
|
21
|
-
ConnectorDocument,
|
|
22
|
-
ConnectorSyncResult,
|
|
23
20
|
FetchLike,
|
|
24
|
-
|
|
21
|
+
SourceContext,
|
|
22
|
+
SourceDocument,
|
|
23
|
+
SourceSyncResult,
|
|
24
|
+
TroveSource,
|
|
25
25
|
Watermark,
|
|
26
26
|
} from './types.js';
|
|
27
27
|
|
|
@@ -29,16 +29,16 @@ import type {
|
|
|
29
29
|
const CONTENT_TYPES: readonly string[] = ['text', 'transcript', 'highlight', 'bookmark'];
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* The options {@link
|
|
33
|
-
* source's stored config and current cursor; tests inject `fetch`,
|
|
34
|
-
* `now` for determinism.
|
|
32
|
+
* The options {@link runSource} builds a `ctx` from. The CLI passes the
|
|
33
|
+
* source's stored config and the feed's current cursor; tests inject `fetch`,
|
|
34
|
+
* `log`, and `now` for determinism.
|
|
35
35
|
*
|
|
36
|
-
* @typeParam C - The shape of the
|
|
36
|
+
* @typeParam C - The shape of the source's typed `ctx.config` preferences.
|
|
37
37
|
*/
|
|
38
38
|
export interface RunOptions<C = Record<string, unknown>> {
|
|
39
39
|
/** The source's preference values (no credentials). Defaults to `{}`. */
|
|
40
40
|
config?: C;
|
|
41
|
-
/** The
|
|
41
|
+
/** The feed's current watermark. Defaults to `{ type: 'none' }`. */
|
|
42
42
|
cursor?: Watermark;
|
|
43
43
|
/** The fetch implementation to expose as `ctx.fetch`. Defaults to global `fetch`. */
|
|
44
44
|
fetchImpl?: FetchLike;
|
|
@@ -49,13 +49,13 @@ export interface RunOptions<C = Record<string, unknown>> {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* The outcome of {@link
|
|
52
|
+
* The outcome of {@link runSource}: the validated, deduped documents, the
|
|
53
53
|
* resolved cursor, and the captured log lines. Mirrors enough of what the cloud
|
|
54
|
-
* ingest reports for `trove
|
|
54
|
+
* ingest reports for `trove source test` to print a useful summary.
|
|
55
55
|
*/
|
|
56
56
|
export interface RunResult {
|
|
57
57
|
/** The validated, deduped documents `sync` returned. */
|
|
58
|
-
documents:
|
|
58
|
+
documents: SourceDocument[];
|
|
59
59
|
/** The cursor `sync` returned, or `{ type: 'none' }` if it returned none. */
|
|
60
60
|
cursor: Watermark;
|
|
61
61
|
/** Captured `ctx.log(...)` lines (when no custom `logSink` was supplied). */
|
|
@@ -73,7 +73,7 @@ export interface RunResult {
|
|
|
73
73
|
* @param index - Its position in the returned array, for the message.
|
|
74
74
|
* @throws {Error} If a required field is missing or malformed.
|
|
75
75
|
*/
|
|
76
|
-
function validateDocument(doc:
|
|
76
|
+
function validateDocument(doc: SourceDocument, index: number): void {
|
|
77
77
|
const where = `document[${String(index)}]`;
|
|
78
78
|
if (doc === null || typeof doc !== 'object') {
|
|
79
79
|
throw new Error(`${where} must be an object`);
|
|
@@ -97,14 +97,14 @@ function validateDocument(doc: ConnectorDocument, index: number): void {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Normalize a `sync` return value to a {@link
|
|
100
|
+
* Normalize a `sync` return value to a {@link SourceSyncResult}, accepting a
|
|
101
101
|
* bare document array for convenience.
|
|
102
102
|
*
|
|
103
103
|
* @param value - The raw `sync` return value.
|
|
104
104
|
* @returns A normalized `{ documents, cursor? }` result.
|
|
105
105
|
* @throws {Error} If the value is neither an array nor a `{ documents }` object.
|
|
106
106
|
*/
|
|
107
|
-
function normalizeResult(value:
|
|
107
|
+
function normalizeResult(value: SourceSyncResult | SourceDocument[]): SourceSyncResult {
|
|
108
108
|
if (Array.isArray(value)) {
|
|
109
109
|
return { documents: value };
|
|
110
110
|
}
|
|
@@ -118,17 +118,17 @@ function normalizeResult(value: ConnectorSyncResult | ConnectorDocument[]): Conn
|
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Deduplicate documents by `id`, keeping the first occurrence (matching the
|
|
121
|
-
* server's `(
|
|
121
|
+
* server's `(feed, id)` dedup, which skips re-returned ids).
|
|
122
122
|
*
|
|
123
123
|
* @param documents - The validated documents.
|
|
124
124
|
* @returns The deduped documents and the count dropped.
|
|
125
125
|
*/
|
|
126
|
-
function dedup(documents:
|
|
127
|
-
unique:
|
|
126
|
+
function dedup(documents: SourceDocument[]): {
|
|
127
|
+
unique: SourceDocument[];
|
|
128
128
|
duplicatesSkipped: number;
|
|
129
129
|
} {
|
|
130
130
|
const seen = new Set<string>();
|
|
131
|
-
const unique:
|
|
131
|
+
const unique: SourceDocument[] = [];
|
|
132
132
|
let duplicatesSkipped = 0;
|
|
133
133
|
for (const doc of documents) {
|
|
134
134
|
if (seen.has(doc.id)) {
|
|
@@ -142,23 +142,23 @@ function dedup(documents: ConnectorDocument[]): {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
|
-
* Run a
|
|
145
|
+
* Run a source's `sync` against a built `ctx` and collect/validate the result.
|
|
146
146
|
*
|
|
147
|
-
* This is the harness the CLI drives for `trove
|
|
148
|
-
* test`: it builds the {@link
|
|
147
|
+
* This is the harness the CLI drives for `trove source dev` / `trove source
|
|
148
|
+
* test`: it builds the {@link SourceContext} from `{ config, cursor }`, runs
|
|
149
149
|
* `sync(ctx)`, normalizes a bare-array return, validates every document's
|
|
150
150
|
* required fields (clear, indexed errors), and dedups by `id`. Errors thrown by
|
|
151
151
|
* `sync` itself propagate unchanged so the CLI can report a failed run exactly as
|
|
152
152
|
* the Mac app would.
|
|
153
153
|
*
|
|
154
|
-
* @typeParam C - The shape of the
|
|
155
|
-
* @param
|
|
154
|
+
* @typeParam C - The shape of the source's typed `ctx.config` preferences.
|
|
155
|
+
* @param source - The source whose `sync` to run.
|
|
156
156
|
* @param options - The `{ config, cursor }` spec plus test injection points.
|
|
157
157
|
* @returns The validated, deduped {@link RunResult}.
|
|
158
158
|
* @throws {Error} If `sync` throws, returns a malformed value, or a document fails validation.
|
|
159
159
|
*/
|
|
160
|
-
export async function
|
|
161
|
-
|
|
160
|
+
export async function runSource<C = Record<string, unknown>>(
|
|
161
|
+
source: TroveSource<C>,
|
|
162
162
|
options: RunOptions<C> = {},
|
|
163
163
|
): Promise<RunResult> {
|
|
164
164
|
const logs: unknown[][] = [];
|
|
@@ -166,7 +166,7 @@ export async function runConnector<C = Record<string, unknown>>(
|
|
|
166
166
|
const fetchImpl: FetchLike = options.fetchImpl ?? ((url, init) => globalThis.fetch(url, init));
|
|
167
167
|
const now = options.now ?? (() => new Date());
|
|
168
168
|
|
|
169
|
-
const ctx:
|
|
169
|
+
const ctx: SourceContext<C> = {
|
|
170
170
|
config: options.config ?? ({} as C),
|
|
171
171
|
cursor,
|
|
172
172
|
fetch: fetchImpl,
|
|
@@ -180,7 +180,7 @@ export async function runConnector<C = Record<string, unknown>>(
|
|
|
180
180
|
now,
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
-
const raw = await
|
|
183
|
+
const raw = await source.sync(ctx);
|
|
184
184
|
const result = normalizeResult(raw);
|
|
185
185
|
|
|
186
186
|
result.documents.forEach(validateDocument);
|
package/src/types.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public type surface for the `@ontrove/sdk`
|
|
2
|
+
* Public type surface for the `@ontrove/sdk` source authoring library.
|
|
3
3
|
*
|
|
4
|
-
* These types describe the
|
|
5
|
-
* and the cursors
|
|
6
|
-
* an object with a `sync(ctx)` method that fetches
|
|
7
|
-
* documents to index. The SDK owns the document shape (which maps 1:1 onto the
|
|
4
|
+
* These types describe the source contract (see the sources SDK reference
|
|
5
|
+
* and the cursors guide on the docs site): a source is
|
|
6
|
+
* an object with a `sync(ctx)` method that fetches new content from the
|
|
7
|
+
* upstream system and returns documents to index. The SDK owns the document shape (which maps 1:1 onto the
|
|
8
8
|
* `IngestDocumentInput` wire type), the typed `ctx` capability object, the
|
|
9
9
|
* watermark/cursor model, and the local-run harness the CLI drives.
|
|
10
10
|
*
|
|
11
|
-
* It is the symmetric sibling of `@ontrove/mcp
|
|
12
|
-
*
|
|
11
|
+
* It is the symmetric sibling of `@ontrove/mcp` (the toolkit-authoring library —
|
|
12
|
+
* every toolkit runs as a full MCP server on Trove's cloud): a source returns
|
|
13
|
+
* documents to be _stored_ (batch `sync`); a toolkit's tools return results to
|
|
14
|
+
* be _read live_.
|
|
13
15
|
*
|
|
14
16
|
* @module
|
|
15
17
|
*/
|
|
@@ -18,10 +20,10 @@
|
|
|
18
20
|
* The default content type Trove assigns a document when it omits `contentType`.
|
|
19
21
|
* Mirrors the GraphQL `ContentType` enum surfaced on `IngestDocumentInput`.
|
|
20
22
|
*/
|
|
21
|
-
export type
|
|
23
|
+
export type SourceContentType = 'text' | 'transcript' | 'highlight' | 'bookmark';
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
|
-
* A single document a
|
|
26
|
+
* A single document a source returns from `sync`. The fields map **1:1** onto
|
|
25
27
|
* the GraphQL `IngestDocumentInput` the Mac app pushes via `ingestDocuments`:
|
|
26
28
|
*
|
|
27
29
|
* | SDK field | `IngestDocumentInput` field |
|
|
@@ -37,15 +39,15 @@ export type ConnectorContentType = 'text' | 'transcript' | 'highlight' | 'bookma
|
|
|
37
39
|
* | `metadata` | `metadata` (JSON) |
|
|
38
40
|
* | `contentType` | `contentType` (ContentType) |
|
|
39
41
|
*
|
|
40
|
-
* Dedup is keyed on `(
|
|
42
|
+
* Dedup is keyed on `(feed, id)`: returning the same `id` twice is safe and is
|
|
41
43
|
* skipped on the server, so `sync` can be idempotent and retried freely. At least
|
|
42
44
|
* one of `text` or `audioUrl` must be present — an audio-only document triggers
|
|
43
45
|
* transcription (Whisper), and the transcript becomes the document text.
|
|
44
46
|
*/
|
|
45
|
-
export interface
|
|
47
|
+
export interface SourceDocument {
|
|
46
48
|
/**
|
|
47
|
-
* The stable external identifier from the
|
|
48
|
-
* the
|
|
49
|
+
* The stable external identifier from the upstream system — the dedup key
|
|
50
|
+
* within the feed. Maps to `IngestDocumentInput.externalId`. Use the native id (HN
|
|
49
51
|
* `objectID`, RSS `guid`, Notion page id), never a value that changes between
|
|
50
52
|
* runs.
|
|
51
53
|
*/
|
|
@@ -80,7 +82,7 @@ export interface ConnectorDocument {
|
|
|
80
82
|
/** Array of string tags. Maps to `IngestDocumentInput.tags`. */
|
|
81
83
|
tags?: string[];
|
|
82
84
|
/**
|
|
83
|
-
* Arbitrary
|
|
85
|
+
* Arbitrary source-specific JSON. Maps to `IngestDocumentInput.metadata`.
|
|
84
86
|
* **Never** put credentials or auth headers here.
|
|
85
87
|
*/
|
|
86
88
|
metadata?: Record<string, unknown>;
|
|
@@ -89,20 +91,20 @@ export interface ConnectorDocument {
|
|
|
89
91
|
* `IngestDocumentInput.contentType`. Defaults to `text` (transcribed audio
|
|
90
92
|
* becomes `transcript` automatically).
|
|
91
93
|
*/
|
|
92
|
-
contentType?:
|
|
94
|
+
contentType?: SourceContentType;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
/**
|
|
96
|
-
* A typed watermark describing how a
|
|
97
|
-
* `
|
|
98
|
-
*
|
|
98
|
+
* A typed watermark describing how a feed resumes between syncs. The opaque
|
|
99
|
+
* `Feed.cursor` string is parsed into one of these (see the cursors guide,
|
|
100
|
+
* watermark types).
|
|
99
101
|
*
|
|
100
|
-
* - `date` — the
|
|
102
|
+
* - `date` — the feed is time-ordered and supports "since <date>"
|
|
101
103
|
* filtering (RSS, most APIs); the newest `value` you push becomes the next
|
|
102
104
|
* cursor.
|
|
103
|
-
* - `idSet` — the
|
|
105
|
+
* - `idSet` — the feed has monotonic ids but no reliable date filter; advance
|
|
104
106
|
* to the highest id seen (`max`), optionally tracking a recent id set.
|
|
105
|
-
* - `none` — re-fetch everything each run and rely purely on `(
|
|
107
|
+
* - `none` — re-fetch everything each run and rely purely on `(feed, id)`
|
|
106
108
|
* dedup. Always correct, just less efficient.
|
|
107
109
|
*/
|
|
108
110
|
export type Watermark =
|
|
@@ -111,16 +113,16 @@ export type Watermark =
|
|
|
111
113
|
| { readonly type: 'none' };
|
|
112
114
|
|
|
113
115
|
/**
|
|
114
|
-
* The result of a
|
|
115
|
-
* the watermark the
|
|
116
|
-
* `
|
|
116
|
+
* The result of a source `sync`: the documents fetched this run and, optionally,
|
|
117
|
+
* the watermark the feed should advance to. A source may also return a bare
|
|
118
|
+
* `SourceDocument[]` for convenience — {@link runSource} normalizes that to
|
|
117
119
|
* `{ documents }` with no cursor change.
|
|
118
120
|
*/
|
|
119
|
-
export interface
|
|
121
|
+
export interface SourceSyncResult {
|
|
120
122
|
/** The documents fetched this run, mapping 1:1 onto `IngestDocumentInput`. */
|
|
121
|
-
documents:
|
|
123
|
+
documents: SourceDocument[];
|
|
122
124
|
/**
|
|
123
|
-
* The watermark to advance the
|
|
125
|
+
* The watermark to advance the feed's cursor to. Omit (or return `{ type: 'none' }`)
|
|
124
126
|
* to leave the cursor unchanged. The cloud only advances if the new value is
|
|
125
127
|
* monotonically ahead (compare-and-swap).
|
|
126
128
|
*/
|
|
@@ -128,20 +130,20 @@ export interface ConnectorSyncResult {
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
/**
|
|
131
|
-
* The standard `fetch` signature the SDK exposes on {@link
|
|
133
|
+
* The standard `fetch` signature the SDK exposes on {@link SourceContext.fetch}.
|
|
132
134
|
* Matches the platform `fetch` so existing code ports unchanged.
|
|
133
135
|
*/
|
|
134
136
|
export type FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;
|
|
135
137
|
|
|
136
138
|
/**
|
|
137
|
-
* The single argument to `sync` — a capability object: everything a
|
|
139
|
+
* The single argument to `sync` — a capability object: everything a source
|
|
138
140
|
* reaches the outside world through is on `ctx`. There is no ambient authority.
|
|
139
141
|
*
|
|
140
142
|
* `ctx.credentials` (Keychain-resolved auth material) and `ctx.browser` (a
|
|
141
|
-
* Playwright browser for `needs_browser`
|
|
143
|
+
* Playwright browser for `needs_browser` sources) are **PROPOSED**, not part
|
|
142
144
|
* of the shipped contract, and so are intentionally absent here.
|
|
143
145
|
*/
|
|
144
|
-
export interface
|
|
146
|
+
export interface SourceContext<C = Record<string, unknown>> {
|
|
145
147
|
/**
|
|
146
148
|
* The user's preference values, keyed by the field names declared in
|
|
147
149
|
* `manifest.json` `config`. **Preferences only — never credentials.** Feed
|
|
@@ -150,19 +152,19 @@ export interface ConnectorContext<C = Record<string, unknown>> {
|
|
|
150
152
|
*/
|
|
151
153
|
readonly config: C;
|
|
152
154
|
/**
|
|
153
|
-
* The
|
|
155
|
+
* The feed's current watermark (the position from the previous run), or
|
|
154
156
|
* `{ type: 'none' }` on the first sync. Read-only — advance the cursor by
|
|
155
157
|
* returning a new {@link Watermark} from `sync`, not by mutating this.
|
|
156
158
|
*/
|
|
157
159
|
readonly cursor: Watermark;
|
|
158
160
|
/**
|
|
159
161
|
* Behaves like the standard `fetch`. Prefer `ctx.fetch` over global `fetch` —
|
|
160
|
-
* in the Mac app it routes through per-
|
|
161
|
-
* rate-limit handling, surfacing failures in the
|
|
162
|
+
* in the Mac app it routes through per-source timeouts, retry, and
|
|
163
|
+
* rate-limit handling, surfacing failures in the source's error log.
|
|
162
164
|
*/
|
|
163
165
|
fetch: FetchLike;
|
|
164
166
|
/**
|
|
165
|
-
* Structured log entry, surfaced in the Mac app's
|
|
167
|
+
* Structured log entry, surfaced in the Mac app's source logs and the
|
|
166
168
|
* `createSyncRun` audit record. Useful for reporting counts and progress.
|
|
167
169
|
*/
|
|
168
170
|
log(...args: unknown[]): void;
|
|
@@ -174,16 +176,16 @@ export interface ConnectorContext<C = Record<string, unknown>> {
|
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
/**
|
|
177
|
-
* The type a
|
|
178
|
-
* a `sync` method that fetches
|
|
179
|
+
* The type a source's default export satisfies. A source is an object with
|
|
180
|
+
* a `sync` method that fetches new content and returns documents to index.
|
|
179
181
|
*
|
|
180
|
-
* @typeParam C - The shape of the
|
|
182
|
+
* @typeParam C - The shape of the source's typed `ctx.config` preferences.
|
|
181
183
|
*
|
|
182
184
|
* @example
|
|
183
185
|
* ```ts
|
|
184
|
-
* import {
|
|
186
|
+
* import { defineSource } from '@ontrove/sdk';
|
|
185
187
|
*
|
|
186
|
-
* export default
|
|
188
|
+
* export default defineSource({
|
|
187
189
|
* async sync(ctx) {
|
|
188
190
|
* const res = await ctx.fetch(ctx.config.feedUrl as string);
|
|
189
191
|
* return parse(await res.text());
|
|
@@ -191,19 +193,19 @@ export interface ConnectorContext<C = Record<string, unknown>> {
|
|
|
191
193
|
* });
|
|
192
194
|
* ```
|
|
193
195
|
*/
|
|
194
|
-
export interface
|
|
196
|
+
export interface TroveSource<C = Record<string, unknown>> {
|
|
195
197
|
/**
|
|
196
|
-
* The batch entry point. Fetches
|
|
197
|
-
* index, optionally with a new cursor. May return a bare `
|
|
198
|
+
* The batch entry point. Fetches new content and returns documents to
|
|
199
|
+
* index, optionally with a new cursor. May return a bare `SourceDocument[]`
|
|
198
200
|
* for convenience. Throw a plain `Error` to fail the run (the Mac app records
|
|
199
201
|
* the error and retries next tick).
|
|
200
202
|
*/
|
|
201
|
-
sync(ctx:
|
|
203
|
+
sync(ctx: SourceContext<C>): Promise<SourceSyncResult | SourceDocument[]>;
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
/**
|
|
205
207
|
* A single field descriptor inside a manifest `config` object — describes one
|
|
206
|
-
* preference input shown in the
|
|
208
|
+
* preference input shown in the source's setup wizard.
|
|
207
209
|
*/
|
|
208
210
|
export interface ManifestConfigField {
|
|
209
211
|
/** Display label in the setup UI. */
|
|
@@ -215,12 +217,12 @@ export interface ManifestConfigField {
|
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
/**
|
|
218
|
-
* A
|
|
220
|
+
* A source `manifest.json` — declares what the source is, how often it runs,
|
|
219
221
|
* and which preference fields the user fills in during setup
|
|
220
|
-
* (
|
|
222
|
+
* (sources/manifest reference).
|
|
221
223
|
*/
|
|
222
|
-
export interface
|
|
223
|
-
/** Stable
|
|
224
|
+
export interface SourceManifest {
|
|
225
|
+
/** Stable source-type id; pattern `^[a-z0-9-]+$`. Required. */
|
|
224
226
|
id: string;
|
|
225
227
|
/** Human-readable display name. Required. */
|
|
226
228
|
name: string;
|
|
@@ -230,23 +232,23 @@ export interface ConnectorManifest {
|
|
|
230
232
|
icon?: string;
|
|
231
233
|
/** Semver version string. Required. */
|
|
232
234
|
version: string;
|
|
233
|
-
/** Who wrote the
|
|
235
|
+
/** Who wrote the source. */
|
|
234
236
|
author?: string;
|
|
235
237
|
/** Directory grouping — `reading`, `social`, `finance`, `dev`, `media`, etc. */
|
|
236
238
|
category?: string;
|
|
237
239
|
/**
|
|
238
240
|
* Human-readable sync cadence — `"every 6 hours"`, `"daily"`, `"on demand"`, or
|
|
239
|
-
* `null` for a live-only
|
|
241
|
+
* `null` for a live-only source. Optional (default: on demand).
|
|
240
242
|
*/
|
|
241
243
|
schedule?: string | null;
|
|
242
244
|
/** The preference fields shown in the setup wizard. Preferences only — no credentials. */
|
|
243
245
|
config?: Record<string, ManifestConfigField>;
|
|
244
|
-
/** Whether the
|
|
246
|
+
/** Whether the source requires a Playwright browser. Default `false`. */
|
|
245
247
|
needs_browser?: boolean;
|
|
246
248
|
/** What kind of source this is — `feed`, `account`, `files`, `api`. */
|
|
247
249
|
kind?: string;
|
|
248
|
-
/** How the
|
|
250
|
+
/** How the source reaches the upstream system — `http`, `browser`, `fs`. */
|
|
249
251
|
transport?: string;
|
|
250
|
-
/** Default content type for documents this
|
|
251
|
-
document_semantics?:
|
|
252
|
+
/** Default content type for documents this source produces. */
|
|
253
|
+
document_semantics?: SourceContentType;
|
|
252
254
|
}
|