@relayfile/adapter-core 0.1.19 → 0.1.20

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.
Files changed (57) hide show
  1. package/dist/src/index.d.ts +1 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +1 -0
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/pubsub/index.d.ts +2 -0
  6. package/dist/src/pubsub/index.d.ts.map +1 -0
  7. package/dist/src/pubsub/index.js +2 -0
  8. package/dist/src/pubsub/index.js.map +1 -0
  9. package/dist/src/pubsub/storage-bridge.d.ts +3 -0
  10. package/dist/src/pubsub/storage-bridge.d.ts.map +1 -0
  11. package/dist/src/pubsub/storage-bridge.js +2 -0
  12. package/dist/src/pubsub/storage-bridge.js.map +1 -0
  13. package/dist/src/storage-bridge/adapter-worker.d.ts +98 -0
  14. package/dist/src/storage-bridge/adapter-worker.d.ts.map +1 -0
  15. package/dist/src/storage-bridge/adapter-worker.js +169 -0
  16. package/dist/src/storage-bridge/adapter-worker.js.map +1 -0
  17. package/dist/src/storage-bridge/discovery.d.ts +32 -0
  18. package/dist/src/storage-bridge/discovery.d.ts.map +1 -0
  19. package/dist/src/storage-bridge/discovery.js +53 -0
  20. package/dist/src/storage-bridge/discovery.js.map +1 -0
  21. package/dist/src/storage-bridge/event.d.ts +70 -0
  22. package/dist/src/storage-bridge/event.d.ts.map +1 -0
  23. package/dist/src/storage-bridge/event.js +178 -0
  24. package/dist/src/storage-bridge/event.js.map +1 -0
  25. package/dist/src/storage-bridge/index.d.ts +7 -0
  26. package/dist/src/storage-bridge/index.d.ts.map +1 -0
  27. package/dist/src/storage-bridge/index.js +7 -0
  28. package/dist/src/storage-bridge/index.js.map +1 -0
  29. package/dist/src/storage-bridge/nango-fallback.d.ts +36 -0
  30. package/dist/src/storage-bridge/nango-fallback.d.ts.map +1 -0
  31. package/dist/src/storage-bridge/nango-fallback.js +194 -0
  32. package/dist/src/storage-bridge/nango-fallback.js.map +1 -0
  33. package/dist/src/storage-bridge/publisher.d.ts +54 -0
  34. package/dist/src/storage-bridge/publisher.d.ts.map +1 -0
  35. package/dist/src/storage-bridge/publisher.js +90 -0
  36. package/dist/src/storage-bridge/publisher.js.map +1 -0
  37. package/dist/src/storage-bridge/writeback.d.ts +41 -0
  38. package/dist/src/storage-bridge/writeback.d.ts.map +1 -0
  39. package/dist/src/storage-bridge/writeback.js +85 -0
  40. package/dist/src/storage-bridge/writeback.js.map +1 -0
  41. package/dist/src/writeback/index.d.ts +2 -0
  42. package/dist/src/writeback/index.d.ts.map +1 -0
  43. package/dist/src/writeback/index.js +2 -0
  44. package/dist/src/writeback/index.js.map +1 -0
  45. package/dist/src/writeback/storage-bridge.d.ts +5 -0
  46. package/dist/src/writeback/storage-bridge.d.ts.map +1 -0
  47. package/dist/src/writeback/storage-bridge.js +3 -0
  48. package/dist/src/writeback/storage-bridge.js.map +1 -0
  49. package/dist/tests/storage-bridge/all-priority-adapters.test.d.ts +2 -0
  50. package/dist/tests/storage-bridge/all-priority-adapters.test.d.ts.map +1 -0
  51. package/dist/tests/storage-bridge/all-priority-adapters.test.js +1014 -0
  52. package/dist/tests/storage-bridge/all-priority-adapters.test.js.map +1 -0
  53. package/dist/tests/storage-bridge/storage-bridge.test.d.ts +2 -0
  54. package/dist/tests/storage-bridge/storage-bridge.test.d.ts.map +1 -0
  55. package/dist/tests/storage-bridge/storage-bridge.test.js +212 -0
  56. package/dist/tests/storage-bridge/storage-bridge.test.js.map +1 -0
  57. package/package.json +1 -1
@@ -0,0 +1,178 @@
1
+ import { createHash } from "node:crypto";
2
+ export const STORAGE_BRIDGE_SOURCES = [
3
+ "google-drive",
4
+ "gcs",
5
+ "sharepoint",
6
+ "onedrive",
7
+ "azure-blob",
8
+ "dropbox",
9
+ "gmail",
10
+ "s3",
11
+ "box",
12
+ "postgres",
13
+ "redis",
14
+ ];
15
+ export const STORAGE_BRIDGE_CHANGE_TYPES = [
16
+ "created",
17
+ "updated",
18
+ "deleted",
19
+ ];
20
+ export class StorageBridgeEventValidationError extends Error {
21
+ issues;
22
+ constructor(issues) {
23
+ super(`Invalid StorageBridgeEvent: ${issues.join("; ")}`);
24
+ this.name = "StorageBridgeEventValidationError";
25
+ this.issues = issues;
26
+ }
27
+ }
28
+ export function createStorageBridgeEvent(input) {
29
+ const occurredAt = toIsoTimestamp(input.occurredAt ?? new Date());
30
+ const detectedAt = toIsoTimestamp(input.detectedAt ?? new Date());
31
+ const metadata = {
32
+ ...(input.metadata ?? {}),
33
+ ...(input.sourceMetadata
34
+ ? {
35
+ source: storageBridgeSourceMetadataToJson(createStorageBridgeSourceMetadata(input.source, input.sourceMetadata)),
36
+ }
37
+ : {}),
38
+ };
39
+ const event = {
40
+ eventId: input.eventId ??
41
+ buildStorageBridgeEventId({
42
+ source: input.source,
43
+ changeType: input.changeType,
44
+ relayfilePath: input.relayfilePath,
45
+ resourceId: input.resourceId,
46
+ occurredAt,
47
+ fingerprint: input.fingerprint ?? null,
48
+ }),
49
+ occurredAt,
50
+ detectedAt,
51
+ source: input.source,
52
+ changeType: input.changeType,
53
+ relayfilePath: input.relayfilePath,
54
+ resourceId: input.resourceId,
55
+ sizeBytes: input.sizeBytes ?? null,
56
+ fingerprint: input.fingerprint ?? null,
57
+ metadata,
58
+ workspaceId: input.workspaceId ?? null,
59
+ };
60
+ return validateStorageBridgeEvent(event);
61
+ }
62
+ export function createStorageBridgeSourceMetadata(source, metadata = {}) {
63
+ return compactObject({
64
+ source,
65
+ providerConfigKey: metadata.providerConfigKey,
66
+ accountId: metadata.accountId,
67
+ connectionId: metadata.connectionId,
68
+ subscriptionId: metadata.subscriptionId,
69
+ nativeEventId: metadata.nativeEventId,
70
+ cursor: metadata.cursor,
71
+ raw: metadata.raw,
72
+ });
73
+ }
74
+ export function storageBridgeSourceMetadataToJson(metadata) {
75
+ return compactObject({
76
+ source: metadata.source,
77
+ providerConfigKey: metadata.providerConfigKey,
78
+ accountId: metadata.accountId,
79
+ connectionId: metadata.connectionId,
80
+ subscriptionId: metadata.subscriptionId,
81
+ nativeEventId: metadata.nativeEventId,
82
+ cursor: metadata.cursor,
83
+ raw: metadata.raw,
84
+ });
85
+ }
86
+ export function storageBridgeWebhookEventType(changeType) {
87
+ return `file.${changeType}`;
88
+ }
89
+ export function validateStorageBridgeEvent(value) {
90
+ const issues = [];
91
+ if (!isRecord(value)) {
92
+ throw new StorageBridgeEventValidationError(["event must be an object"]);
93
+ }
94
+ requireNonEmptyString(value, "eventId", issues);
95
+ requireIsoTimestamp(value, "occurredAt", issues);
96
+ requireIsoTimestamp(value, "detectedAt", issues);
97
+ requireOneOf(value, "source", STORAGE_BRIDGE_SOURCES, issues);
98
+ requireOneOf(value, "changeType", STORAGE_BRIDGE_CHANGE_TYPES, issues);
99
+ requireAbsolutePath(value, "relayfilePath", issues);
100
+ requireNonEmptyString(value, "resourceId", issues);
101
+ if (value.sizeBytes !== null) {
102
+ if (typeof value.sizeBytes !== "number" ||
103
+ !Number.isFinite(value.sizeBytes) ||
104
+ value.sizeBytes < 0) {
105
+ issues.push("sizeBytes must be a non-negative number or null");
106
+ }
107
+ }
108
+ if (value.fingerprint !== null && typeof value.fingerprint !== "string") {
109
+ issues.push("fingerprint must be a string or null");
110
+ }
111
+ if (!isRecord(value.metadata)) {
112
+ issues.push("metadata must be an object");
113
+ }
114
+ if (value.workspaceId !== null && typeof value.workspaceId !== "string") {
115
+ issues.push("workspaceId must be a string or null");
116
+ }
117
+ if (issues.length > 0) {
118
+ throw new StorageBridgeEventValidationError(issues);
119
+ }
120
+ return value;
121
+ }
122
+ export function isStorageBridgeEvent(value) {
123
+ try {
124
+ validateStorageBridgeEvent(value);
125
+ return true;
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ }
131
+ export function buildStorageBridgeEventId(input) {
132
+ const digest = createHash("sha256")
133
+ .update([
134
+ input.source,
135
+ input.changeType,
136
+ input.relayfilePath,
137
+ input.resourceId,
138
+ input.occurredAt,
139
+ input.fingerprint ?? "",
140
+ ].join("\0"))
141
+ .digest("hex")
142
+ .slice(0, 32);
143
+ return `${input.source}:${digest}`;
144
+ }
145
+ export function toIsoTimestamp(value) {
146
+ const date = value instanceof Date ? value : new Date(value);
147
+ if (Number.isNaN(date.getTime())) {
148
+ throw new StorageBridgeEventValidationError(["timestamp is not a valid date"]);
149
+ }
150
+ return date.toISOString();
151
+ }
152
+ function compactObject(input) {
153
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
154
+ }
155
+ function isRecord(value) {
156
+ return typeof value === "object" && value !== null && !Array.isArray(value);
157
+ }
158
+ function requireNonEmptyString(value, key, issues) {
159
+ if (typeof value[key] !== "string" || value[key].trim() === "") {
160
+ issues.push(`${key} must be a non-empty string`);
161
+ }
162
+ }
163
+ function requireAbsolutePath(value, key, issues) {
164
+ if (typeof value[key] !== "string" || !value[key].startsWith("/")) {
165
+ issues.push(`${key} must be an absolute relayfile path`);
166
+ }
167
+ }
168
+ function requireIsoTimestamp(value, key, issues) {
169
+ if (typeof value[key] !== "string" || Number.isNaN(Date.parse(value[key]))) {
170
+ issues.push(`${key} must be an ISO timestamp string`);
171
+ }
172
+ }
173
+ function requireOneOf(value, key, allowed, issues) {
174
+ if (typeof value[key] !== "string" || !allowed.includes(value[key])) {
175
+ issues.push(`${key} must be one of ${allowed.join(", ")}`);
176
+ }
177
+ }
178
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../../src/storage-bridge/event.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from "./event.js";
2
+ export * from "./publisher.js";
3
+ export * from "./adapter-worker.js";
4
+ export * from "./nango-fallback.js";
5
+ export * from "./discovery.js";
6
+ export * from "./writeback.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage-bridge/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from "./event.js";
2
+ export * from "./publisher.js";
3
+ export * from "./adapter-worker.js";
4
+ export * from "./nango-fallback.js";
5
+ export * from "./discovery.js";
6
+ export * from "./writeback.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage-bridge/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { type JsonValue, type StorageBridgeEvent, type StorageBridgeSource } from "./event.js";
2
+ export declare const NANGO_FALLBACK_PROVIDER_CONFIG_KEYS: readonly ["google-drive", "sharepoint-online", "sharepoint", "one-drive", "onedrive", "dropbox", "box", "google-mail", "gmail"];
3
+ export type NangoFallbackProviderConfigKey = (typeof NANGO_FALLBACK_PROVIDER_CONFIG_KEYS)[number];
4
+ export interface NangoSyncWebhookPayload {
5
+ readonly providerConfigKey: string;
6
+ readonly connectionId?: string;
7
+ readonly syncName?: string;
8
+ readonly model?: string;
9
+ readonly responseResults?: {
10
+ readonly added?: number;
11
+ readonly updated?: number;
12
+ readonly deleted?: number;
13
+ };
14
+ readonly records?: readonly NangoSyncRecord[];
15
+ readonly modifiedAfter?: string;
16
+ readonly metadata?: Record<string, JsonValue>;
17
+ }
18
+ export type NangoSyncRecord = Record<string, unknown>;
19
+ export interface MapNangoSyncRecordInput {
20
+ readonly providerConfigKey: string;
21
+ readonly record: NangoSyncRecord;
22
+ readonly workspaceId?: string | null;
23
+ readonly accountId?: string;
24
+ readonly connectionId?: string;
25
+ readonly syncName?: string;
26
+ readonly detectedAt?: string | Date;
27
+ }
28
+ export declare function mapNangoProviderToSource(providerConfigKey: string): StorageBridgeSource;
29
+ export declare function mapNangoSyncRecord(input: MapNangoSyncRecordInput): StorageBridgeEvent;
30
+ export declare function computeResourceId(source: StorageBridgeSource, record: NangoSyncRecord): string;
31
+ export declare function computeRelayfilePath(input: {
32
+ readonly source: StorageBridgeSource;
33
+ readonly record: NangoSyncRecord;
34
+ readonly accountId: string;
35
+ }): string;
36
+ //# sourceMappingURL=nango-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nango-fallback.d.ts","sourceRoot":"","sources":["../../../src/storage-bridge/nango-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EAGzB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,mCAAmC,iIAUtC,CAAC;AAEX,MAAM,MAAM,8BAA8B,GACxC,CAAC,OAAO,mCAAmC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,CAAC,EAAE;QACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,wBAAgB,wBAAwB,CACtC,iBAAiB,EAAE,MAAM,GACxB,mBAAmB,CAoBrB;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAC7B,kBAAkB,CA4DpB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,eAAe,GACtB,MAAM,CAkBR;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,GAAG,MAAM,CAyCT"}
@@ -0,0 +1,194 @@
1
+ import { buildStorageBridgeEventId, createStorageBridgeEvent, } from "./event.js";
2
+ export const NANGO_FALLBACK_PROVIDER_CONFIG_KEYS = [
3
+ "google-drive",
4
+ "sharepoint-online",
5
+ "sharepoint",
6
+ "one-drive",
7
+ "onedrive",
8
+ "dropbox",
9
+ "box",
10
+ "google-mail",
11
+ "gmail",
12
+ ];
13
+ export function mapNangoProviderToSource(providerConfigKey) {
14
+ switch (providerConfigKey) {
15
+ case "google-drive":
16
+ return "google-drive";
17
+ case "sharepoint":
18
+ case "sharepoint-online":
19
+ return "sharepoint";
20
+ case "one-drive":
21
+ case "onedrive":
22
+ return "onedrive";
23
+ case "dropbox":
24
+ return "dropbox";
25
+ case "box":
26
+ return "box";
27
+ case "google-mail":
28
+ case "gmail":
29
+ return "gmail";
30
+ default:
31
+ throw new Error(`Unsupported Nango providerConfigKey "${providerConfigKey}"`);
32
+ }
33
+ }
34
+ export function mapNangoSyncRecord(input) {
35
+ const source = mapNangoProviderToSource(input.providerConfigKey);
36
+ const resourceId = computeResourceId(source, input.record);
37
+ const relayfilePath = computeRelayfilePath({
38
+ source,
39
+ record: input.record,
40
+ accountId: input.accountId ?? input.connectionId ?? "default",
41
+ });
42
+ const occurredAt = readString(input.record, "updatedAt") ??
43
+ readString(input.record, "updated_at") ??
44
+ readString(input.record, "modifiedTime") ??
45
+ readString(input.record, "lastModifiedDateTime") ??
46
+ new Date().toISOString();
47
+ const changeType = computeNangoChangeType(input.record);
48
+ const fingerprint = readString(input.record, "etag") ??
49
+ readString(input.record, "eTag") ??
50
+ readString(input.record, "rev") ??
51
+ readString(input.record, "sha1") ??
52
+ null;
53
+ const sizeBytes = readNumber(input.record, "size") ??
54
+ readNumber(input.record, "sizeBytes") ??
55
+ null;
56
+ const eventInput = {
57
+ eventId: buildStorageBridgeEventId({
58
+ source,
59
+ changeType,
60
+ relayfilePath,
61
+ resourceId,
62
+ occurredAt,
63
+ fingerprint,
64
+ }),
65
+ occurredAt,
66
+ detectedAt: input.detectedAt,
67
+ source,
68
+ changeType,
69
+ relayfilePath,
70
+ resourceId,
71
+ sizeBytes,
72
+ fingerprint,
73
+ workspaceId: input.workspaceId ?? null,
74
+ metadata: {
75
+ nango: toJsonObject({
76
+ providerConfigKey: input.providerConfigKey,
77
+ connectionId: input.connectionId,
78
+ syncName: input.syncName,
79
+ record: sanitizeJson(input.record),
80
+ }),
81
+ },
82
+ sourceMetadata: {
83
+ providerConfigKey: input.providerConfigKey,
84
+ connectionId: input.connectionId,
85
+ accountId: input.accountId,
86
+ raw: sanitizeJson(input.record),
87
+ },
88
+ };
89
+ return createStorageBridgeEvent(eventInput);
90
+ }
91
+ export function computeResourceId(source, record) {
92
+ const direct = readString(record, "id") ??
93
+ readString(record, "fileId") ??
94
+ readString(record, "itemId") ??
95
+ readString(record, "threadId") ??
96
+ readString(record, "messageId") ??
97
+ readString(record, "path_lower") ??
98
+ readString(record, "path") ??
99
+ readString(record, "name");
100
+ if (direct)
101
+ return direct;
102
+ const stableRecord = JSON.stringify(sanitizeJson(record));
103
+ if (stableRecord && stableRecord !== "{}") {
104
+ return `${source}:${stableRecord}`;
105
+ }
106
+ throw new Error(`Cannot compute resource id for ${source} Nango record`);
107
+ }
108
+ export function computeRelayfilePath(input) {
109
+ const { source, record, accountId } = input;
110
+ switch (source) {
111
+ case "google-drive": {
112
+ const id = encodeSegment(computeResourceId(source, record));
113
+ const filePath = readString(record, "path") ?? readString(record, "name");
114
+ return filePath
115
+ ? `/google-drive/${encodeSegment(accountId)}/${trimLeadingSlash(filePath)}`
116
+ : `/google-drive/${encodeSegment(accountId)}/files/${id}.json`;
117
+ }
118
+ case "sharepoint": {
119
+ const siteId = encodeSegment(readString(record, "siteId") ?? "default-site");
120
+ const driveId = encodeSegment(readString(record, "driveId") ?? "default-drive");
121
+ const id = encodeSegment(computeResourceId(source, record));
122
+ return `/sharepoint/${siteId}/${driveId}/items/${id}.json`;
123
+ }
124
+ case "onedrive": {
125
+ const id = encodeSegment(computeResourceId(source, record));
126
+ return `/onedrive/${encodeSegment(accountId)}/items/${id}.json`;
127
+ }
128
+ case "dropbox": {
129
+ const path = readString(record, "path_display") ??
130
+ readString(record, "path_lower") ??
131
+ readString(record, "path") ??
132
+ `${computeResourceId(source, record)}.json`;
133
+ return `/dropbox/${encodeSegment(accountId)}/files/${trimLeadingSlash(path)}`;
134
+ }
135
+ case "box": {
136
+ const id = encodeSegment(computeResourceId(source, record));
137
+ return `/box/files/${id}.json`;
138
+ }
139
+ case "gmail": {
140
+ const threadId = encodeSegment(readString(record, "threadId") ?? computeResourceId(source, record));
141
+ return `/gmail/${encodeSegment(accountId)}/threads/${threadId}.json`;
142
+ }
143
+ default:
144
+ throw new Error(`Nango fallback is not supported for ${source}`);
145
+ }
146
+ }
147
+ function computeNangoChangeType(record) {
148
+ const deleted = readBoolean(record, "deleted") ??
149
+ readBoolean(record, "_deleted") ??
150
+ readBoolean(record, "isDeleted");
151
+ if (deleted)
152
+ return "deleted";
153
+ const createdAt = readString(record, "createdAt") ?? readString(record, "created_at");
154
+ const updatedAt = readString(record, "updatedAt") ?? readString(record, "updated_at");
155
+ return createdAt && updatedAt && createdAt === updatedAt ? "created" : "updated";
156
+ }
157
+ function readString(record, key) {
158
+ const value = record[key];
159
+ return typeof value === "string" && value.trim() ? value : undefined;
160
+ }
161
+ function readNumber(record, key) {
162
+ const value = record[key];
163
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
164
+ }
165
+ function readBoolean(record, key) {
166
+ const value = record[key];
167
+ return typeof value === "boolean" ? value : undefined;
168
+ }
169
+ function encodeSegment(segment) {
170
+ return encodeURIComponent(segment);
171
+ }
172
+ function trimLeadingSlash(path) {
173
+ return path.replace(/^\/+/, "");
174
+ }
175
+ function toJsonObject(input) {
176
+ return Object.fromEntries(Object.entries(input)
177
+ .filter(([, value]) => value !== undefined)
178
+ .map(([key, value]) => [key, sanitizeJson(value)]));
179
+ }
180
+ function sanitizeJson(value) {
181
+ if (value === null ||
182
+ typeof value === "string" ||
183
+ typeof value === "number" ||
184
+ typeof value === "boolean") {
185
+ return value;
186
+ }
187
+ if (Array.isArray(value))
188
+ return value.map(sanitizeJson);
189
+ if (typeof value === "object" && value !== null) {
190
+ return toJsonObject(value);
191
+ }
192
+ return String(value);
193
+ }
194
+ //# sourceMappingURL=nango-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nango-fallback.js","sourceRoot":"","sources":["../../../src/storage-bridge/nango-fallback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { type StorageBridgeEvent, type StorageBridgeEventForSource, type StorageBridgeSource } from "./event.js";
2
+ export interface StorageBridgePublishResult {
3
+ readonly eventId: string;
4
+ readonly published: boolean;
5
+ readonly duplicate: boolean;
6
+ }
7
+ export interface StorageBridgeEventSubscriber {
8
+ (event: StorageBridgeEvent): void | Promise<void>;
9
+ }
10
+ export interface StorageBridgeSubscription {
11
+ unsubscribe(): void;
12
+ }
13
+ export interface StorageBridgeEventPublisher {
14
+ publish(event: StorageBridgeEvent): Promise<StorageBridgePublishResult>;
15
+ subscribe(subscriber: StorageBridgeEventSubscriber): StorageBridgeSubscription;
16
+ }
17
+ export interface StorageBridgeEventPublisherForSource<Source extends StorageBridgeSource> {
18
+ publish(event: StorageBridgeEventForSource<Source>): Promise<StorageBridgePublishResult> | StorageBridgePublishResult;
19
+ }
20
+ export interface IdempotencyStore {
21
+ claim(key: string, ttlMs?: number): Promise<boolean> | boolean;
22
+ release?(key: string): Promise<void> | void;
23
+ }
24
+ export declare class InMemoryIdempotencyStore implements IdempotencyStore {
25
+ private readonly seen;
26
+ claim(key: string, ttlMs?: number): boolean;
27
+ release(key: string): void;
28
+ clear(): void;
29
+ private pruneExpired;
30
+ }
31
+ export interface InMemoryPublisherOptions {
32
+ readonly idempotencyStore?: IdempotencyStore;
33
+ readonly idempotencyTtlMs?: number;
34
+ readonly onSubscriberError?: (error: unknown, event: StorageBridgeEvent) => void | Promise<void>;
35
+ }
36
+ export declare class InMemoryStorageBridgeEventPublisher implements StorageBridgeEventPublisher {
37
+ private readonly subscribers;
38
+ private readonly idempotencyStore;
39
+ private readonly idempotencyTtlMs?;
40
+ private readonly onSubscriberError?;
41
+ readonly events: StorageBridgeEvent[];
42
+ constructor(options?: InMemoryPublisherOptions);
43
+ publish(event: StorageBridgeEvent): Promise<StorageBridgePublishResult>;
44
+ subscribe(subscriber: StorageBridgeEventSubscriber): StorageBridgeSubscription;
45
+ publishUnchecked(event: StorageBridgeEvent): Promise<void>;
46
+ }
47
+ export interface PublishStorageBridgeEventOptions {
48
+ readonly idempotencyStore?: IdempotencyStore;
49
+ readonly idempotencyTtlMs?: number;
50
+ readonly idempotencyKey?: string;
51
+ readonly skipPublisherIdempotency?: boolean;
52
+ }
53
+ export declare function publishStorageBridgeEvent(publisher: StorageBridgeEventPublisher, event: StorageBridgeEvent, options?: PublishStorageBridgeEventOptions): Promise<StorageBridgePublishResult>;
54
+ //# sourceMappingURL=publisher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../../src/storage-bridge/publisher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { validateStorageBridgeEvent, } from "./event.js";
2
+ export class InMemoryIdempotencyStore {
3
+ seen = new Map();
4
+ claim(key, ttlMs) {
5
+ this.pruneExpired();
6
+ if (this.seen.has(key))
7
+ return false;
8
+ this.seen.set(key, ttlMs ? Date.now() + ttlMs : Number.POSITIVE_INFINITY);
9
+ return true;
10
+ }
11
+ release(key) {
12
+ this.seen.delete(key);
13
+ }
14
+ clear() {
15
+ this.seen.clear();
16
+ }
17
+ pruneExpired() {
18
+ const now = Date.now();
19
+ for (const [key, expiresAt] of this.seen) {
20
+ if (expiresAt <= now)
21
+ this.seen.delete(key);
22
+ }
23
+ }
24
+ }
25
+ export class InMemoryStorageBridgeEventPublisher {
26
+ subscribers = new Set();
27
+ idempotencyStore;
28
+ idempotencyTtlMs;
29
+ onSubscriberError;
30
+ events = [];
31
+ constructor(options = {}) {
32
+ this.idempotencyStore =
33
+ options.idempotencyStore ?? new InMemoryIdempotencyStore();
34
+ this.idempotencyTtlMs = options.idempotencyTtlMs;
35
+ this.onSubscriberError = options.onSubscriberError;
36
+ }
37
+ async publish(event) {
38
+ return publishStorageBridgeEvent(this, event, {
39
+ idempotencyStore: this.idempotencyStore,
40
+ idempotencyTtlMs: this.idempotencyTtlMs,
41
+ skipPublisherIdempotency: true,
42
+ });
43
+ }
44
+ subscribe(subscriber) {
45
+ this.subscribers.add(subscriber);
46
+ return {
47
+ unsubscribe: () => {
48
+ this.subscribers.delete(subscriber);
49
+ },
50
+ };
51
+ }
52
+ async publishUnchecked(event) {
53
+ this.events.push(event);
54
+ await Promise.all([...this.subscribers].map(async (subscriber) => {
55
+ try {
56
+ await subscriber(event);
57
+ }
58
+ catch (error) {
59
+ await this.onSubscriberError?.(error, event);
60
+ }
61
+ }));
62
+ }
63
+ }
64
+ export async function publishStorageBridgeEvent(publisher, event, options = {}) {
65
+ const validEvent = validateStorageBridgeEvent(event);
66
+ const key = options.idempotencyKey ?? `storage-bridge:publish:${validEvent.eventId}`;
67
+ if (options.idempotencyStore) {
68
+ const claimed = await options.idempotencyStore.claim(key, options.idempotencyTtlMs);
69
+ if (!claimed) {
70
+ return {
71
+ eventId: validEvent.eventId,
72
+ published: false,
73
+ duplicate: true,
74
+ };
75
+ }
76
+ }
77
+ if (options.skipPublisherIdempotency &&
78
+ publisher instanceof InMemoryStorageBridgeEventPublisher) {
79
+ await publisher.publishUnchecked(validEvent);
80
+ }
81
+ else {
82
+ await publisher.publish(validEvent);
83
+ }
84
+ return {
85
+ eventId: validEvent.eventId,
86
+ published: true,
87
+ duplicate: false,
88
+ };
89
+ }
90
+ //# sourceMappingURL=publisher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publisher.js","sourceRoot":"","sources":["../../../src/storage-bridge/publisher.ts"],"names":[],"mappings":"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"}