@oddington/sdk 0.1.36 → 0.1.37

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
@@ -79,6 +79,12 @@ declared `review` handler crosses the Inbox publication seam as `review`; Inbox
79
79
  retains the canonical `invoice-import` routine identity separately, without
80
80
  deploying or executing that handler.
81
81
 
82
+ Microsoft Connection configuration has a separate, optional `access` description.
83
+ It preserves supplied strings, including existing `read` and `read-write` labels,
84
+ and does not grant or restrict Microsoft permissions. Management results omit
85
+ this metadata when absent. Setup and safe inspection are documented in the
86
+ [CLI Connections guide](../../apps/cli/README.md#connections).
87
+
82
88
  With `access: { ai: true }`, `os.ai.generateText` returns non-empty `{ text }`.
83
89
  `os.ai.generateObject` returns `{ object }` validated against the requested
84
90
  `schema`, with its inferred type. An invalid answer throws `RoutineAIError`
@@ -504,6 +510,44 @@ revision and URL facts. Distinct objects and custom provider values remain.
504
510
  Helpers need no initialization and perform no I/O, choose no ref, and add no
505
511
  changes. File retention and Data validation/publication remain explicit.
506
512
 
513
+ Microsoft helpers are also exported at the package root, with the corresponding
514
+ `OutlookSourceLocation` and `SharePointSourceLocation` types:
515
+
516
+ ```ts
517
+ const messageLocation = source.location.outlook({
518
+ connection: 'outlook/invoices', message: message.id, url: message.webLink,
519
+ // attachment: attachment.id, // Include for attachment bytes; omit for message MIME.
520
+ });
521
+ const itemLocation = source.location.sharepoint({
522
+ connection: 'sharepoint/operations', site: siteId, drive: driveId, item: item.id,
523
+ version: version.id, path: lastKnownPath, url: item.webUrl,
524
+ });
525
+ ```
526
+
527
+ Construct these only after successful provider reads establish the coordinates
528
+ for the retained bytes, and save them alongside `source.location.os(file)` using
529
+ ordinary Data publication. Request `Prefer: IdType="ImmutableId"` consistently
530
+ when obtaining Outlook message and attachment IDs. These IDs are case-sensitive
531
+ and survive folder moves within the same mailbox, but not archive-mailbox moves
532
+ or export/reimport. Subjects, filenames and `internetMessageId` are not substitutes.
533
+ Omit `attachment` for an exact message MIME File; supply it for an attachment File.
534
+
535
+ For SharePoint, use the saved Graph site ID, the document library's drive ID and
536
+ the file's driveItem ID. Optional `version` is an actual driveItemVersion ID
537
+ established for those bytes, never an eTag or a guessed version. Omit it when no
538
+ such version is established. Names, paths and URLs do not establish identity.
539
+ Optional `path` is last-known context; `url` is the ordinary HTTPS message web
540
+ link or SharePoint webUrl. Never retain temporary download, upload-session or
541
+ copy-monitor URLs, authorization or credentials in Locations.
542
+
543
+ Outlook merge identity is connection/message/attachment, with whole messages
544
+ distinct from attachments. SharePoint identity is connection/site/drive/item/
545
+ version, with absent versions distinct from present ones. Matching observations
546
+ replace path/link context, including removing omitted context; distinct IDs,
547
+ versions and copies remain separate. Incomplete coordinates gain no inferred
548
+ identity. The retained OS File and hash own exact content; external edits do
549
+ not change an existing Source's bytes. These helpers perform no provider I/O.
550
+
507
551
  Read a named repository through the authenticated host context:
508
552
 
509
553
  ```ts
package/dist/browser.js CHANGED
@@ -1960,6 +1960,37 @@ var files = {
1960
1960
  function validDropboxRootPath(value) {
1961
1961
  return typeof value == "string" && value.length <= 2048 && value.startsWith("/") && !/[\\\x00-\x1f\x7f]/.test(value) && value.slice(1).split("/").every((segment) => segment.length > 0 && segment.trim() === segment && segment !== "." && segment !== "..");
1962
1962
  }
1963
+ function validMicrosoftId(value) {
1964
+ return typeof value == "string" && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(value);
1965
+ }
1966
+ function validMicrosoftMailbox(value) {
1967
+ return typeof value == "string" && value.length <= 254 && /^[^\s@/?#\\\x00-\x1f\x7f]+@[^\s@/?#\\\x00-\x1f\x7f]+\.[^\s@/?#\\\x00-\x1f\x7f]+$/.test(value);
1968
+ }
1969
+ function sharePointHost(value) {
1970
+ return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.sharepoint\.com$/.test(value);
1971
+ }
1972
+ function canonicalSharePointSiteUrl(value) {
1973
+ if (!(typeof value != "string" || value.length > 2048 || /[\s\\?#\x00-\x1f\x7f]/.test(value) || !/^https:\/\/[^/:@]+(?:\/|$)/i.test(value)))
1974
+ try {
1975
+ let url = new URL(value);
1976
+ if (url.protocol !== "https:" || !sharePointHost(url.hostname) || url.username || url.password || url.port) return;
1977
+ let rawPath = value.replace(/^https:\/\/[^/]+/i, "") || "/", path = rawPath.endsWith("/") ? rawPath.slice(0, -1) : rawPath, segments = path.slice(1).split("/");
1978
+ if (path && segments.some((segment) => !segment)) return;
1979
+ let encoded = path ? segments.map((segment) => {
1980
+ let decoded = decodeURIComponent(segment);
1981
+ if (decoded === "." || decoded === ".." || /[/%\\?#\x00-\x1f\x7f]/.test(decoded)) throw new Error();
1982
+ return encodeURIComponent(decoded);
1983
+ }).join("/") : "", canonical = `https://${url.hostname}/${encoded}`;
1984
+ return canonical.length <= 2048 ? canonical : void 0;
1985
+ } catch {
1986
+ return;
1987
+ }
1988
+ }
1989
+ function validSharePointSiteId(value, siteUrl) {
1990
+ if (typeof value != "string" || value.length > 512) return !1;
1991
+ let [host, collection, site, extra] = value.split(",");
1992
+ return extra === void 0 && host === new URL(siteUrl).hostname && validMicrosoftId(collection) && validMicrosoftId(site);
1993
+ }
1963
1994
  function validGoogleScopes(value) {
1964
1995
  return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
1965
1996
  }
@@ -1970,13 +2001,32 @@ function validDropboxFileScopes(value) {
1970
2001
 
1971
2002
  var ROUTINE_CONNECTION_CREDENTIALS_ENV = "OS_ROUTINE_CONNECTION_CREDENTIALS";
1972
2003
  function parseRoutineConnectionNames(value) {
1973
- if (!Array.isArray(value) || value.some((name2) => typeof name2 != "string" || !/^(gmail|airtable|dropbox)\/[a-z0-9][a-z0-9-]{0,62}$/.test(name2)) || new Set(value).size !== value.length)
2004
+ if (!Array.isArray(value) || value.some((name2) => typeof name2 != "string" || !/^(gmail|airtable|dropbox|outlook|sharepoint)\/[a-z0-9][a-z0-9-]{0,62}$/.test(name2)) || new Set(value).size !== value.length)
1974
2005
  throw new Error("Invalid routine connections");
1975
2006
  return value;
1976
2007
  }
1977
2008
  var connectionUuid = (v) => typeof v == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(v);
1978
2009
  function runtimeConnection(lease) {
1979
2010
  switch (lease.provider) {
2011
+ case "outlook":
2012
+ return {
2013
+ id: `outlook/${lease.name}`,
2014
+ provider: lease.provider,
2015
+ name: lease.name,
2016
+ mailboxEmail: lease.mailboxEmail,
2017
+ accessToken: lease.accessToken,
2018
+ expiresAt: lease.expiresAt
2019
+ };
2020
+ case "sharepoint":
2021
+ return {
2022
+ id: `sharepoint/${lease.name}`,
2023
+ provider: lease.provider,
2024
+ name: lease.name,
2025
+ siteId: lease.siteId,
2026
+ siteUrl: lease.siteUrl,
2027
+ accessToken: lease.accessToken,
2028
+ expiresAt: lease.expiresAt
2029
+ };
1980
2030
  case "gmail":
1981
2031
  return {
1982
2032
  id: `gmail/${lease.name}`,
@@ -2014,6 +2064,11 @@ function parseConnectionLease(raw, now = Date.now()) {
2014
2064
  let v = raw;
2015
2065
  if (!v || typeof v != "object" || Array.isArray(v) || !connectionUuid(v.connectionId) || !connectionUuid(v.runId) || typeof v.name != "string" || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(v.name) || typeof v.accessToken != "string" || !/^[A-Za-z0-9._~+/-]{1,8192}={0,2}$/.test(v.accessToken))
2016
2066
  throw new Error("Invalid connection credential");
2067
+ if (v.provider === "outlook" || v.provider === "sharepoint") {
2068
+ if (!(v.provider === "outlook" ? Object.keys(v).sort().join(",") === "accessToken,connectionId,expiresAt,mailboxEmail,name,provider,runId" && validMicrosoftMailbox(v.mailboxEmail) : Object.keys(v).sort().join(",") === "accessToken,connectionId,expiresAt,name,provider,runId,siteId,siteUrl" && typeof v.siteUrl == "string" && canonicalSharePointSiteUrl(v.siteUrl) === v.siteUrl && validSharePointSiteId(v.siteId, v.siteUrl)) || typeof v.expiresAt != "string" || !Number.isFinite(Date.parse(v.expiresAt)) || Date.parse(v.expiresAt) <= now + 6e4 || Date.parse(v.expiresAt) > now + 36e5)
2069
+ throw new Error("Invalid connection credential");
2070
+ return v;
2071
+ }
2017
2072
  if (v.provider === "gmail") {
2018
2073
  if (Object.keys(v).sort().join(",") !== "accessToken,accountEmail,connectionId,expiresAt,name,provider,runId,scopes" || typeof v.accountEmail != "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.accountEmail) || !validGoogleScopes(v.scopes) || typeof v.expiresAt != "string" || !Number.isFinite(Date.parse(v.expiresAt)) || Date.parse(v.expiresAt) <= now + 6e4 || Date.parse(v.expiresAt) > now + 36e5)
2019
2074
  throw new Error("Invalid connection credential");
@@ -2281,7 +2336,17 @@ var connections = /* @__PURE__ */ runtimeConnections();
2281
2336
  var LOCAL_ROUTINE_VALUE_BYTES = 256 * 1024;
2282
2337
 
2283
2338
  function locationIdentity(location) {
2284
- let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] : void 0;
2339
+ let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] : location.provider === "outlook" ? [
2340
+ location.connection,
2341
+ location.message,
2342
+ ...location.attachment === void 0 ? [] : [location.attachment]
2343
+ ] : location.provider === "sharepoint" ? [
2344
+ location.connection,
2345
+ location.site,
2346
+ location.drive,
2347
+ location.item,
2348
+ ...location.version === void 0 ? [] : [location.version]
2349
+ ] : void 0;
2285
2350
  if (coordinates?.every((value) => typeof value == "string" && value.length > 0))
2286
2351
  return JSON.stringify([location.provider, ...coordinates]);
2287
2352
  }
@@ -2320,6 +2385,27 @@ var source = {
2320
2385
  ...input.path === void 0 ? {} : { path: input.path },
2321
2386
  ...input.url === void 0 ? {} : { url: input.url }
2322
2387
  };
2388
+ },
2389
+ outlook(input) {
2390
+ return {
2391
+ provider: "outlook",
2392
+ connection: input.connection,
2393
+ message: input.message,
2394
+ ...input.attachment === void 0 ? {} : { attachment: input.attachment },
2395
+ ...input.url === void 0 ? {} : { url: input.url }
2396
+ };
2397
+ },
2398
+ sharepoint(input) {
2399
+ return {
2400
+ provider: "sharepoint",
2401
+ connection: input.connection,
2402
+ site: input.site,
2403
+ drive: input.drive,
2404
+ item: input.item,
2405
+ ...input.version === void 0 ? {} : { version: input.version },
2406
+ ...input.path === void 0 ? {} : { path: input.path },
2407
+ ...input.url === void 0 ? {} : { url: input.url }
2408
+ };
2323
2409
  }
2324
2410
  },
2325
2411
  locations: {