@vornrun/connector-sdk 0.7.0 → 0.7.1-beta.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/README.md CHANGED
@@ -217,6 +217,8 @@ auth: {
217
217
  }
218
218
  ```
219
219
 
220
+ Add `headers` to `check` for a site whose reads need one, such as a CSRF flag. Headers the browser sets itself or that carry who you are, such as `cookie`, `authorization`, `origin` or any `sec-` header, are refused.
221
+
220
222
  Vorn opens a window on a browser profile that belongs to one connection, and the person signs in there. The connector's code then gets `ctx.session.fetch` beside `ctx.fetch`. A call through `ctx.session.fetch` runs inside that signed-in window as a same-origin request, so the service sees its own page asking and no cookie reaches the connector. Calls outside `origins` are refused. `ctx.fetch` stays a plain fetch for public reads, such as a feed, which work before anyone signs in. A `browser` connector's declared `request` actions go through the window.
221
223
 
222
224
  `--mock` serves signed-in calls from the same routes as every other call. A `--live` run from a terminal has no window, so it skips them.
@@ -389,6 +389,8 @@ interface BrowserSignIn {
389
389
  check: {
390
390
  url: string;
391
391
  identity: string[];
392
+ /** Sent with the check, for a site whose reads need a header such as a CSRF flag. */
393
+ headers?: Record<string, string>;
392
394
  };
393
395
  }
394
396
  /** The signed-in window, offered to a `browser` connector's code. */
@@ -122,6 +122,101 @@ function withinOrigins(origins, url) {
122
122
  return wildcard ? target.endsWith(`.${host}`) : target === host;
123
123
  });
124
124
  }
125
+ var FORBIDDEN_SESSION_HEADERS = /* @__PURE__ */ new Set([
126
+ "cookie",
127
+ "cookie2",
128
+ "authorization",
129
+ "host",
130
+ "origin",
131
+ "referer",
132
+ "content-length"
133
+ ]);
134
+ var HEADER_NAME = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
135
+ function allowedSessionHeader(name) {
136
+ const lower = name.toLowerCase();
137
+ return HEADER_NAME.test(name) && !FORBIDDEN_SESSION_HEADERS.has(lower) && !lower.startsWith("sec-") && !lower.startsWith("proxy-");
138
+ }
139
+
140
+ // src/post-receive.ts
141
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
142
+ function isRecord(value) {
143
+ return typeof value === "object" && value !== null && !Array.isArray(value);
144
+ }
145
+ function segments(path) {
146
+ return path.split(".").map((part) => part.trim()).filter((part) => part !== "");
147
+ }
148
+ function valueAt(value, path) {
149
+ let current = value;
150
+ for (const key of segments(path)) {
151
+ if (UNSAFE_KEYS.has(key)) return void 0;
152
+ if (Array.isArray(current)) {
153
+ const index = Number(key);
154
+ if (!Number.isInteger(index)) return void 0;
155
+ current = current[index];
156
+ continue;
157
+ }
158
+ if (!isRecord(current)) return void 0;
159
+ current = Object.prototype.hasOwnProperty.call(current, key) ? current[key] : void 0;
160
+ }
161
+ return current;
162
+ }
163
+ function withValueAt(value, path, next) {
164
+ const keys = segments(path);
165
+ if (keys.length === 0) return next;
166
+ const [head, ...rest] = keys;
167
+ if (UNSAFE_KEYS.has(head)) return value;
168
+ if (Array.isArray(value)) {
169
+ const index = Number(head);
170
+ if (!Number.isInteger(index)) return value;
171
+ const copy = [...value];
172
+ copy[index] = rest.length === 0 ? next : withValueAt(copy[index], rest.join("."), next);
173
+ return copy;
174
+ }
175
+ const base = isRecord(value) ? value : {};
176
+ return {
177
+ ...base,
178
+ [head]: rest.length === 0 ? next : withValueAt(base[head], rest.join("."), next)
179
+ };
180
+ }
181
+ function pick(value, keys) {
182
+ if (Array.isArray(value)) return value.map((entry) => pick(entry, keys));
183
+ if (!isRecord(value)) return value;
184
+ const out = {};
185
+ for (const key of keys) {
186
+ if (UNSAFE_KEYS.has(key)) continue;
187
+ if (Object.prototype.hasOwnProperty.call(value, key)) out[key] = value[key];
188
+ }
189
+ return out;
190
+ }
191
+ function rename(value, from, to) {
192
+ if (Array.isArray(value)) return value.map((entry) => rename(entry, from, to));
193
+ if (!isRecord(value)) return value;
194
+ if (UNSAFE_KEYS.has(from) || UNSAFE_KEYS.has(to)) return value;
195
+ if (!Object.prototype.hasOwnProperty.call(value, from)) return value;
196
+ const out = {};
197
+ for (const [key, entry] of Object.entries(value)) {
198
+ if (key === from) out[to] = entry;
199
+ else if (key !== to) out[key] = entry;
200
+ }
201
+ return out;
202
+ }
203
+ function applyOp(value, op) {
204
+ if (op.op === "flatten") return valueAt(value, op.path);
205
+ const target = op.path === void 0 ? value : valueAt(value, op.path);
206
+ if (op.path !== void 0 && target === void 0) return value;
207
+ let next;
208
+ if (op.op === "pick") next = pick(target, op.keys);
209
+ else if (op.op === "rename") next = rename(target, op.from, op.to);
210
+ else if (op.op === "filter") {
211
+ next = Array.isArray(target) ? target.filter((entry) => isRecord(entry) && valueAt(entry, op.key) === op.equals) : target;
212
+ } else {
213
+ next = Array.isArray(target) ? target.map((entry) => applyPostReceive(entry, op.ops)) : target;
214
+ }
215
+ return op.path === void 0 ? next : withValueAt(value, op.path, next);
216
+ }
217
+ function applyPostReceive(value, ops) {
218
+ return (ops ?? []).reduce(applyOp, value);
219
+ }
125
220
 
126
221
  // src/define.ts
127
222
  var KEY_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
@@ -270,6 +365,16 @@ function assertBrowserSignIn(id, browser) {
270
365
  if (!Array.isArray(identity) || identity.some((path) => typeof path !== "string" || !path.trim())) {
271
366
  throw new Error(`Connector ${id} must name its identity fields as non-empty strings`);
272
367
  }
368
+ const headers = browser.check?.headers;
369
+ if (headers === void 0) return;
370
+ const badHeader = isRecord(headers) ? Object.entries(headers).find(
371
+ ([name, value]) => typeof value !== "string" || !allowedSessionHeader(name)
372
+ ) : void 0;
373
+ if (!isRecord(headers) || badHeader) {
374
+ throw new Error(
375
+ `Connector ${id} may add only plain string headers to its signed-in check` + (badHeader ? `; ${JSON.stringify(badHeader[0])} is not one` : "")
376
+ );
377
+ }
273
378
  }
274
379
  function assertIdentity(kind, definition) {
275
380
  if (!KEY_PATTERN.test(definition.id ?? "")) {
@@ -1121,7 +1226,7 @@ var RESERVED_KEYS = [
1121
1226
  "assignee",
1122
1227
  "updatedAt"
1123
1228
  ];
1124
- var UNSAFE_KEYS = ["__proto__", "constructor", "prototype"];
1229
+ var UNSAFE_KEYS2 = ["__proto__", "constructor", "prototype"];
1125
1230
  function itemExternalId(item) {
1126
1231
  return String(item.externalId ?? "").trim();
1127
1232
  }
@@ -1147,7 +1252,7 @@ function normalizeItem(item, polledAt) {
1147
1252
  const extra = {};
1148
1253
  for (const [key, value] of Object.entries(item.data ?? {})) {
1149
1254
  if (RESERVED_KEYS.includes(key)) continue;
1150
- if (UNSAFE_KEYS.includes(key)) continue;
1255
+ if (UNSAFE_KEYS2.includes(key)) continue;
1151
1256
  extra[key] = value;
1152
1257
  }
1153
1258
  return {
@@ -1292,87 +1397,6 @@ async function runFetch(type, fetchItems, context) {
1292
1397
  return fetched;
1293
1398
  }
1294
1399
 
1295
- // src/post-receive.ts
1296
- var UNSAFE_KEYS2 = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1297
- function isRecord(value) {
1298
- return typeof value === "object" && value !== null && !Array.isArray(value);
1299
- }
1300
- function segments(path) {
1301
- return path.split(".").map((part) => part.trim()).filter((part) => part !== "");
1302
- }
1303
- function valueAt(value, path) {
1304
- let current = value;
1305
- for (const key of segments(path)) {
1306
- if (UNSAFE_KEYS2.has(key)) return void 0;
1307
- if (Array.isArray(current)) {
1308
- const index = Number(key);
1309
- if (!Number.isInteger(index)) return void 0;
1310
- current = current[index];
1311
- continue;
1312
- }
1313
- if (!isRecord(current)) return void 0;
1314
- current = Object.prototype.hasOwnProperty.call(current, key) ? current[key] : void 0;
1315
- }
1316
- return current;
1317
- }
1318
- function withValueAt(value, path, next) {
1319
- const keys = segments(path);
1320
- if (keys.length === 0) return next;
1321
- const [head, ...rest] = keys;
1322
- if (UNSAFE_KEYS2.has(head)) return value;
1323
- if (Array.isArray(value)) {
1324
- const index = Number(head);
1325
- if (!Number.isInteger(index)) return value;
1326
- const copy = [...value];
1327
- copy[index] = rest.length === 0 ? next : withValueAt(copy[index], rest.join("."), next);
1328
- return copy;
1329
- }
1330
- const base = isRecord(value) ? value : {};
1331
- return {
1332
- ...base,
1333
- [head]: rest.length === 0 ? next : withValueAt(base[head], rest.join("."), next)
1334
- };
1335
- }
1336
- function pick(value, keys) {
1337
- if (Array.isArray(value)) return value.map((entry) => pick(entry, keys));
1338
- if (!isRecord(value)) return value;
1339
- const out = {};
1340
- for (const key of keys) {
1341
- if (UNSAFE_KEYS2.has(key)) continue;
1342
- if (Object.prototype.hasOwnProperty.call(value, key)) out[key] = value[key];
1343
- }
1344
- return out;
1345
- }
1346
- function rename(value, from, to) {
1347
- if (Array.isArray(value)) return value.map((entry) => rename(entry, from, to));
1348
- if (!isRecord(value)) return value;
1349
- if (UNSAFE_KEYS2.has(from) || UNSAFE_KEYS2.has(to)) return value;
1350
- if (!Object.prototype.hasOwnProperty.call(value, from)) return value;
1351
- const out = {};
1352
- for (const [key, entry] of Object.entries(value)) {
1353
- if (key === from) out[to] = entry;
1354
- else if (key !== to) out[key] = entry;
1355
- }
1356
- return out;
1357
- }
1358
- function applyOp(value, op) {
1359
- if (op.op === "flatten") return valueAt(value, op.path);
1360
- const target = op.path === void 0 ? value : valueAt(value, op.path);
1361
- if (op.path !== void 0 && target === void 0) return value;
1362
- let next;
1363
- if (op.op === "pick") next = pick(target, op.keys);
1364
- else if (op.op === "rename") next = rename(target, op.from, op.to);
1365
- else if (op.op === "filter") {
1366
- next = Array.isArray(target) ? target.filter((entry) => isRecord(entry) && valueAt(entry, op.key) === op.equals) : target;
1367
- } else {
1368
- next = Array.isArray(target) ? target.map((entry) => applyPostReceive(entry, op.ops)) : target;
1369
- }
1370
- return op.path === void 0 ? next : withValueAt(value, op.path, next);
1371
- }
1372
- function applyPostReceive(value, ops) {
1373
- return (ops ?? []).reduce(applyOp, value);
1374
- }
1375
-
1376
1400
  // src/request.ts
1377
1401
  var MAX_ERROR_BODY = 500;
1378
1402
  var PLACEHOLDER = /\{\{\s*(args|config)\.([A-Za-z0-9_.-]+)\s*\}\}/g;
@@ -3450,6 +3474,8 @@ export {
3450
3474
  createSessionFetch,
3451
3475
  ORIGIN_PATTERN,
3452
3476
  withinOrigins,
3477
+ valueAt,
3478
+ applyPostReceive,
3453
3479
  EXTENSION_PERMISSIONS,
3454
3480
  HOST_PERMISSIONS,
3455
3481
  envNameFor,
@@ -3477,8 +3503,6 @@ export {
3477
3503
  normalizeItem,
3478
3504
  normalizeItems,
3479
3505
  pollWithDedupe,
3480
- valueAt,
3481
- applyPostReceive,
3482
3506
  resolveTemplates,
3483
3507
  resolveRequest,
3484
3508
  asOutput,
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { B as BundleRequest, a as BundleOutput, C as CheckFinding } from './check-62s2GvcO.js';
2
+ import { B as BundleRequest, a as BundleOutput, C as CheckFinding } from './check-CzSTEQD8.js';
3
3
 
4
4
  interface CliDeps {
5
5
  load(modulePath: string): Promise<unknown>;
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  runPoll,
11
11
  scaffoldFiles,
12
12
  serveConnector
13
- } from "./chunk-ZKHXHE3O.js";
13
+ } from "./chunk-CCY5QT5R.js";
14
14
 
15
15
  // src/cli.ts
16
16
  import { fileURLToPath, pathToFileURL } from "url";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as ExtensionPermission, b as ExtensionHostMethod, c as ConnectorDefinition, d as Connector, e as ExtensionDefinition, f as ConnectorConfig, g as ExtensionHost, T as TriggerDefinition, P as PollContext, h as PollOutcome, i as ConnectorItem, N as NormalizedItem, j as PostReceiveOp, A as ActionRequest, B as BundleRequest, a as BundleOutput, C as CheckFinding } from './check-62s2GvcO.js';
2
- export { k as ActionContext, l as ActionDefinition, m as ActionInputField, n as ActionInputOption, o as ActionInputType, p as ActionOutputField, q as ActivationPredicate, r as AuthRung, s as BrowserSignIn, t as CHECK_OWNERS, u as CheckCode, v as CheckOptions, w as ConformanceRun, x as ConnectionSetup, y as ConnectorAuth, z as ConnectorConfigField, D as ConnectorHarness, F as ConnectorIcon, G as ConnectorKind, H as ConnectorManifest, I as ConnectorVerification, J as DedupeStrategy, K as DefaultWorkflow, L as ExtensionAgent, M as ExtensionContext, O as ExtensionContributions, Q as ExtensionPlatform, R as ExtensionUsage, S as ExtensionUsageWindow, U as FetchContext, V as FooterContribution, W as FooterItem, X as HarnessOptions, Y as LinkContext, Z as LinkHandled, _ as LinkHandlerContribution, $ as MANIFEST_TOOL, a0 as MAX_PACK_BYTES, a1 as MAX_POLL_PAGES, a2 as ManifestContributions, a3 as MockCall, a4 as MockHostAnswers, a5 as MockHostRun, a6 as MockRoute, a7 as MockRouteMissError, a8 as MockRun, a9 as OPTIONS_TOOL, aa as OptionsContext, ab as OptionsLoader, ac as PREFLIGHT_TOOL, ad as PaginationStrategy, ae as PaneContribution, af as PollPage, ag as PreflightResult, ah as ResilientFetchOptions, ai as RetryPolicy, aj as RunActionOptions, ak as RunPollOptions, al as SessionContext, am as StatusSuggestion, an as backoffMs, ao as bundleDependencyFindings, ap as bundledRequireFindings, aq as checkConnector, ar as connectionSetup, as as connectorManifest, at as createConnectorHarness, au as drainPoll, av as esbuildBundle, aw as escapedMockHttp, ax as footerToolName, ay as formatFindings, az as handlerToolName, aA as lifecycleScriptFindings, aB as mockExtensionHost, aC as pollToolName, aD as readNearestPackageJson, aE as resilientFetch, aF as retryAfterMs, aG as runAction, aH as runConformance, aI as runOptions, aJ as runPoll, aK as withMockHttp } from './check-62s2GvcO.js';
1
+ import { E as ExtensionPermission, b as ExtensionHostMethod, c as ConnectorDefinition, d as Connector, e as ExtensionDefinition, f as ConnectorConfig, g as ExtensionHost, T as TriggerDefinition, P as PollContext, h as PollOutcome, i as ConnectorItem, N as NormalizedItem, j as PostReceiveOp, A as ActionRequest, B as BundleRequest, a as BundleOutput, C as CheckFinding } from './check-CzSTEQD8.js';
2
+ export { k as ActionContext, l as ActionDefinition, m as ActionInputField, n as ActionInputOption, o as ActionInputType, p as ActionOutputField, q as ActivationPredicate, r as AuthRung, s as BrowserSignIn, t as CHECK_OWNERS, u as CheckCode, v as CheckOptions, w as ConformanceRun, x as ConnectionSetup, y as ConnectorAuth, z as ConnectorConfigField, D as ConnectorHarness, F as ConnectorIcon, G as ConnectorKind, H as ConnectorManifest, I as ConnectorVerification, J as DedupeStrategy, K as DefaultWorkflow, L as ExtensionAgent, M as ExtensionContext, O as ExtensionContributions, Q as ExtensionPlatform, R as ExtensionUsage, S as ExtensionUsageWindow, U as FetchContext, V as FooterContribution, W as FooterItem, X as HarnessOptions, Y as LinkContext, Z as LinkHandled, _ as LinkHandlerContribution, $ as MANIFEST_TOOL, a0 as MAX_PACK_BYTES, a1 as MAX_POLL_PAGES, a2 as ManifestContributions, a3 as MockCall, a4 as MockHostAnswers, a5 as MockHostRun, a6 as MockRoute, a7 as MockRouteMissError, a8 as MockRun, a9 as OPTIONS_TOOL, aa as OptionsContext, ab as OptionsLoader, ac as PREFLIGHT_TOOL, ad as PaginationStrategy, ae as PaneContribution, af as PollPage, ag as PreflightResult, ah as ResilientFetchOptions, ai as RetryPolicy, aj as RunActionOptions, ak as RunPollOptions, al as SessionContext, am as StatusSuggestion, an as backoffMs, ao as bundleDependencyFindings, ap as bundledRequireFindings, aq as checkConnector, ar as connectionSetup, as as connectorManifest, at as createConnectorHarness, au as drainPoll, av as esbuildBundle, aw as escapedMockHttp, ax as footerToolName, ay as formatFindings, az as handlerToolName, aA as lifecycleScriptFindings, aB as mockExtensionHost, aC as pollToolName, aD as readNearestPackageJson, aE as resilientFetch, aF as retryAfterMs, aG as runAction, aH as runConformance, aI as runOptions, aJ as runPoll, aK as withMockHttp } from './check-CzSTEQD8.js';
3
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
 
5
5
  /** Everything an extension may ask the host for; anything else is not grantable. */
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ import {
66
66
  valueAt,
67
67
  withMockHttp,
68
68
  withinOrigins
69
- } from "./chunk-ZKHXHE3O.js";
69
+ } from "./chunk-CCY5QT5R.js";
70
70
  export {
71
71
  BROWSER_HOST_ENV,
72
72
  BROWSER_TOKEN_ENV,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vornrun/connector-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.7.1-beta.1",
4
4
  "description": "Build and share Vorn pull connectors as ordinary npm packages",
5
5
  "type": "module",
6
6
  "license": "MIT",