@fayz-ai/core 0.10.0 → 0.12.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/dist/chunk-7GJTLBG3.js +566 -0
- package/dist/chunk-7GJTLBG3.js.map +1 -0
- package/dist/chunk-ED6NBH6U.js +348 -0
- package/dist/chunk-ED6NBH6U.js.map +1 -0
- package/dist/chunk-EWKX4VUM.js +429 -0
- package/dist/chunk-EWKX4VUM.js.map +1 -0
- package/dist/chunk-IKZR3WSK.js +289 -0
- package/dist/chunk-IKZR3WSK.js.map +1 -0
- package/dist/chunk-KFBEKFII.js +320 -0
- package/dist/chunk-KFBEKFII.js.map +1 -0
- package/dist/{chunk-732GZEUI.js → chunk-RRYFXUQX.js} +22 -367
- package/dist/chunk-RRYFXUQX.js.map +1 -0
- package/dist/chunk-S5FOFKR4.js +1618 -0
- package/dist/chunk-S5FOFKR4.js.map +1 -0
- package/dist/{chunk-M27JPX3K.js → chunk-U3252GZO.js} +366 -24
- package/dist/chunk-U3252GZO.js.map +1 -0
- package/dist/connector-runtime/index.d.ts +15 -0
- package/dist/connector-runtime/index.d.ts.map +1 -0
- package/dist/connector-runtime/index.js +3 -0
- package/dist/connector-runtime/index.js.map +1 -0
- package/dist/data/archetype.d.ts.map +1 -1
- package/dist/data/bulk.d.ts +24 -0
- package/dist/data/bulk.d.ts.map +1 -1
- package/dist/data/cached.d.ts +5 -1
- package/dist/data/cached.d.ts.map +1 -1
- package/dist/data/custom-fields.d.ts +5 -0
- package/dist/data/custom-fields.d.ts.map +1 -0
- package/dist/data/errors.d.ts +44 -0
- package/dist/data/errors.d.ts.map +1 -0
- package/dist/data/index.d.ts +10 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +3 -1
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/offline.d.ts +45 -0
- package/dist/data/offline.d.ts.map +1 -0
- package/dist/data/platform-api.d.ts.map +1 -1
- package/dist/data/query.d.ts +91 -0
- package/dist/data/query.d.ts.map +1 -0
- package/dist/data/resolve.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/types.d.ts +21 -0
- package/dist/data/types.d.ts.map +1 -1
- package/dist/events/bus.d.ts +12 -0
- package/dist/events/bus.d.ts.map +1 -0
- package/dist/events/index.d.ts +3 -11
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/log.d.ts.map +1 -1
- package/dist/fields/store.d.ts +45 -0
- package/dist/fields/store.d.ts.map +1 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/shell-translations.en.d.ts.map +1 -1
- package/dist/i18n/shell-translations.pt-br.d.ts.map +1 -1
- package/dist/index.d.ts +35 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +326 -252
- package/dist/index.js.map +1 -1
- package/dist/integrations/events.d.ts +7 -0
- package/dist/integrations/events.d.ts.map +1 -0
- package/dist/integrations/index.d.ts +663 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +3 -1
- package/dist/integrations/permissions.d.ts +132 -0
- package/dist/integrations/permissions.d.ts.map +1 -0
- package/dist/integrations/request.d.ts +170 -0
- package/dist/integrations/request.d.ts.map +1 -0
- package/dist/integrations/runtime-token.d.ts +33 -0
- package/dist/integrations/runtime-token.d.ts.map +1 -0
- package/dist/integrations/runtime.d.ts +398 -0
- package/dist/integrations/runtime.d.ts.map +1 -0
- package/dist/integrations/settings.d.ts +102 -0
- package/dist/integrations/settings.d.ts.map +1 -0
- package/dist/integrations/store.d.ts +145 -0
- package/dist/integrations/store.d.ts.map +1 -0
- package/dist/lib/cache-idb.d.ts +54 -0
- package/dist/lib/cache-idb.d.ts.map +1 -0
- package/dist/lib/cache.d.ts +2 -0
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/plugin/index.js +2 -1
- package/dist/plugin/runtime.d.ts.map +1 -1
- package/dist/registry/entities.d.ts +41 -15
- package/dist/registry/entities.d.ts.map +1 -1
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/search/engine.d.ts.map +1 -1
- package/dist/search/types.d.ts +7 -0
- package/dist/search/types.d.ts.map +1 -1
- package/dist/{shell-translations.pt-br-2GWRQRRP.js → shell-translations.pt-br-ANQ64XTT.js} +75 -3
- package/dist/shell-translations.pt-br-ANQ64XTT.js.map +1 -0
- package/dist/telemetry/errors.d.ts +87 -0
- package/dist/telemetry/errors.d.ts.map +1 -0
- package/dist/testing/index.js +4 -1
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/types/analytics.d.ts +39 -0
- package/dist/types/analytics.d.ts.map +1 -1
- package/dist/types/crud.d.ts +35 -1
- package/dist/types/crud.d.ts.map +1 -1
- package/dist/types/plugins.d.ts +94 -0
- package/dist/types/plugins.d.ts.map +1 -1
- package/package.json +7 -3
- package/dist/chunk-6RBW2Y5K.js +0 -42
- package/dist/chunk-6RBW2Y5K.js.map +0 -1
- package/dist/chunk-732GZEUI.js.map +0 -1
- package/dist/chunk-CPMHCTX3.js +0 -77
- package/dist/chunk-CPMHCTX3.js.map +0 -1
- package/dist/chunk-JO7Z2AOE.js +0 -863
- package/dist/chunk-JO7Z2AOE.js.map +0 -1
- package/dist/chunk-M27JPX3K.js.map +0 -1
- package/dist/integrations.d.ts +0 -159
- package/dist/integrations.d.ts.map +0 -1
- package/dist/shell-translations.pt-br-2GWRQRRP.js.map +0 -1
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { getSupabaseClientOptional, getActiveTenantId } from './chunk-KFBEKFII.js';
|
|
2
|
+
|
|
3
|
+
// src/integrations/events.ts
|
|
4
|
+
function eventKeyMatches(pattern, key) {
|
|
5
|
+
if (pattern === "*") return true;
|
|
6
|
+
if (!pattern.endsWith("*")) return pattern === key;
|
|
7
|
+
const prefix = pattern.slice(0, -1);
|
|
8
|
+
return key.startsWith(prefix);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/integrations/permissions.ts
|
|
12
|
+
var DATA_ACTIONS = ["read", "write"];
|
|
13
|
+
var EVENT_ACTIONS = ["read", "listen", "emit"];
|
|
14
|
+
function permissionActions(kind) {
|
|
15
|
+
return kind === "data" ? DATA_ACTIONS : EVENT_ACTIONS;
|
|
16
|
+
}
|
|
17
|
+
function isDataPermission(permission) {
|
|
18
|
+
return permission.kind === "data";
|
|
19
|
+
}
|
|
20
|
+
function isEventPermission(permission) {
|
|
21
|
+
return permission.kind === "event";
|
|
22
|
+
}
|
|
23
|
+
function grants(permission, action) {
|
|
24
|
+
return permission.actions.includes(action);
|
|
25
|
+
}
|
|
26
|
+
function connectorPermissions(def) {
|
|
27
|
+
return def?.permissions ?? [];
|
|
28
|
+
}
|
|
29
|
+
function connectorEntities(def) {
|
|
30
|
+
const declared = connectorPermissions(def).filter(isDataPermission).map((p) => p.resource);
|
|
31
|
+
if (declared.length > 0) return unique(declared);
|
|
32
|
+
return unique(def?.entities ?? []);
|
|
33
|
+
}
|
|
34
|
+
function connectorSubscribesTo(def) {
|
|
35
|
+
const declared = connectorPermissions(def).filter((p) => isEventPermission(p) && grants(p, "listen")).map((p) => p.resource);
|
|
36
|
+
if (declared.length > 0) return unique(declared);
|
|
37
|
+
return unique(def?.subscribesTo ?? []);
|
|
38
|
+
}
|
|
39
|
+
function connectorSubscribes(def, key) {
|
|
40
|
+
return connectorSubscribesTo(def).some((pattern) => eventKeyMatches(pattern, key));
|
|
41
|
+
}
|
|
42
|
+
function permissionsWrite(def) {
|
|
43
|
+
return connectorPermissions(def).some((p) => grants(p, "write") || grants(p, "emit"));
|
|
44
|
+
}
|
|
45
|
+
function hasSensitivePermission(def) {
|
|
46
|
+
return connectorPermissions(def).some((p) => p.sensitive === true);
|
|
47
|
+
}
|
|
48
|
+
function sortedPermissions(def) {
|
|
49
|
+
return [...connectorPermissions(def)].sort((a, b) => {
|
|
50
|
+
if (a.kind !== b.kind) return a.kind === "data" ? -1 : 1;
|
|
51
|
+
const sensitivity = Number(b.sensitive ?? false) - Number(a.sensitive ?? false);
|
|
52
|
+
if (sensitivity !== 0) return sensitivity;
|
|
53
|
+
const mutating = Number(mutates(b)) - Number(mutates(a));
|
|
54
|
+
if (mutating !== 0) return mutating;
|
|
55
|
+
return a.resource.localeCompare(b.resource);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function mutates(permission) {
|
|
59
|
+
return grants(permission, "write") || grants(permission, "emit");
|
|
60
|
+
}
|
|
61
|
+
function groupedPermissions(def) {
|
|
62
|
+
const sorted = sortedPermissions(def);
|
|
63
|
+
return {
|
|
64
|
+
data: sorted.filter(isDataPermission),
|
|
65
|
+
events: sorted.filter(isEventPermission)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function connectorGrantScopes(def) {
|
|
69
|
+
const scopes = [];
|
|
70
|
+
for (const permission of connectorPermissions(def)) {
|
|
71
|
+
for (const action of permission.actions) scopes.push(`${permission.kind}:${permission.resource}:${action}`);
|
|
72
|
+
}
|
|
73
|
+
return unique(scopes).sort();
|
|
74
|
+
}
|
|
75
|
+
function unique(values) {
|
|
76
|
+
return [...new Set(values)];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/integrations/runtime-token.ts
|
|
80
|
+
var ConnectorRuntimeTokenUnavailableError = class extends Error {
|
|
81
|
+
constructor(message) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.name = "ConnectorRuntimeTokenUnavailableError";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var _source = null;
|
|
87
|
+
function setConnectorRuntimeTokenSource(source) {
|
|
88
|
+
_source = source;
|
|
89
|
+
}
|
|
90
|
+
function getConnectorRuntimeTokenSource() {
|
|
91
|
+
return _source;
|
|
92
|
+
}
|
|
93
|
+
async function connectorRuntimeToken(request) {
|
|
94
|
+
const source = _source;
|
|
95
|
+
if (!source) {
|
|
96
|
+
throw new ConnectorRuntimeTokenUnavailableError(
|
|
97
|
+
"This app installed no connector runtime-token source, so the data plane cannot prove which tenant it is for"
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const value = typeof source === "function" ? await source(request) : source;
|
|
101
|
+
const token = typeof value === "string" ? value.trim() : "";
|
|
102
|
+
if (!token) {
|
|
103
|
+
throw new ConnectorRuntimeTokenUnavailableError(
|
|
104
|
+
"No runtime token is available for this session, so the call was not made"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return token;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/integrations/settings.ts
|
|
111
|
+
function isSettingSet(value) {
|
|
112
|
+
if (value === void 0 || value === null) return false;
|
|
113
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
114
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
function connectorSettings(def) {
|
|
118
|
+
return def.settings ?? [];
|
|
119
|
+
}
|
|
120
|
+
function missingRequiredSettings(settings, values) {
|
|
121
|
+
return (settings ?? []).filter((setting) => setting.requiredToOperate && !isSettingSet(values?.[setting.key])).map((setting) => setting.key);
|
|
122
|
+
}
|
|
123
|
+
function settingsStatus(settings, connection) {
|
|
124
|
+
if (!connection?.active) return { needsSetup: false };
|
|
125
|
+
const missing = missingRequiredSettings(settings, connection.settings);
|
|
126
|
+
return missing.length > 0 ? { needsSetup: true, missingSettings: missing } : { needsSetup: false };
|
|
127
|
+
}
|
|
128
|
+
function setupStatusDetail(missing) {
|
|
129
|
+
return `setup: ${missing.join(", ")}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/integrations/store.ts
|
|
133
|
+
var CONNECTIONS_TABLE = "plg_connections";
|
|
134
|
+
var SYNC_RUNS_TABLE = "plg_sync_runs";
|
|
135
|
+
var ConnectionSettingsError = class extends Error {
|
|
136
|
+
constructor(message) {
|
|
137
|
+
super(message);
|
|
138
|
+
this.name = "ConnectionSettingsError";
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function assertSettingsAllowed(settings, options, connectorId = "connector") {
|
|
142
|
+
const secret = new Set((options.fields ?? []).filter((f) => f.type === "password").map((f) => f.key));
|
|
143
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
144
|
+
...(options.fields ?? []).filter((f) => f.type !== "password").map((f) => f.key),
|
|
145
|
+
// A declared setting IS its own allow-list entry. Requiring it in
|
|
146
|
+
// `settingsKeys` as well is two places to add a key and one place to forget.
|
|
147
|
+
...(options.settings ?? []).map((s) => s.key),
|
|
148
|
+
...options.settingsKeys ?? []
|
|
149
|
+
]);
|
|
150
|
+
for (const key of Object.keys(settings)) {
|
|
151
|
+
if (secret.has(key)) {
|
|
152
|
+
throw new ConnectionSettingsError(
|
|
153
|
+
`[${connectorId}] "${key}" is a credential field \u2014 credentials never reach plg_connections.settings.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (!allowed.has(key)) {
|
|
157
|
+
throw new ConnectionSettingsError(
|
|
158
|
+
`[${connectorId}] unknown settings key "${key}". Declare it in the connector's fields or settingsKeys.`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function toConnection(row) {
|
|
164
|
+
return {
|
|
165
|
+
id: row.id,
|
|
166
|
+
tenantId: row.tenant_id,
|
|
167
|
+
connectorId: row.connector_id,
|
|
168
|
+
instanceKey: row.instance_key ?? "",
|
|
169
|
+
displayName: row.display_name ?? null,
|
|
170
|
+
active: row.active,
|
|
171
|
+
status: row.status,
|
|
172
|
+
statusDetail: row.status_detail ?? null,
|
|
173
|
+
settings: row.settings ?? {},
|
|
174
|
+
providerAccountKey: row.provider_account_key ?? null,
|
|
175
|
+
cursors: row.cursors ?? {},
|
|
176
|
+
lastSyncAt: row.last_sync_at ?? null,
|
|
177
|
+
createdAt: row.created_at,
|
|
178
|
+
updatedAt: row.updated_at
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function toRun(row) {
|
|
182
|
+
const stats = row.stats ?? {};
|
|
183
|
+
return {
|
|
184
|
+
id: row.id,
|
|
185
|
+
tenantId: row.tenant_id,
|
|
186
|
+
connectionId: row.connection_id,
|
|
187
|
+
connectorId: row.connector_id,
|
|
188
|
+
direction: row.direction,
|
|
189
|
+
triggerKind: row.trigger_kind,
|
|
190
|
+
status: row.status,
|
|
191
|
+
stream: row.stream ?? null,
|
|
192
|
+
fetched: row.fetched ?? 0,
|
|
193
|
+
written: row.written ?? 0,
|
|
194
|
+
stats,
|
|
195
|
+
cursor: row.cursor ?? null,
|
|
196
|
+
correlationId: row.correlation_id ?? null,
|
|
197
|
+
// `stats.message` is the pre-031 home; read it so a pool that has not
|
|
198
|
+
// applied 031 yet still shows the sentence rather than a blank row.
|
|
199
|
+
message: row.message ?? stats.message ?? null,
|
|
200
|
+
error: row.error ?? null,
|
|
201
|
+
startedAt: row.started_at,
|
|
202
|
+
finishedAt: row.finished_at ?? null
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function unwrap(answer, what) {
|
|
206
|
+
if (answer.error) throw new Error(`[@fayz-ai/core] ${what}: ${answer.error.message}`);
|
|
207
|
+
return answer.data;
|
|
208
|
+
}
|
|
209
|
+
function createConnectionStore(connectorId, options = {}) {
|
|
210
|
+
const instanceKey = options.instanceKey ?? "";
|
|
211
|
+
function client() {
|
|
212
|
+
const c = options.client ?? getSupabaseClientOptional();
|
|
213
|
+
if (!c) throw new Error("[@fayz-ai/core] Supabase client not available. Call createFayzApp with a provider first.");
|
|
214
|
+
return c;
|
|
215
|
+
}
|
|
216
|
+
function tenantId() {
|
|
217
|
+
const id = options.tenantId ?? getActiveTenantId();
|
|
218
|
+
if (!id) throw new Error(`[@fayz-ai/core] createConnectionStore(${connectorId}): no active tenant.`);
|
|
219
|
+
return id;
|
|
220
|
+
}
|
|
221
|
+
function scoped(q) {
|
|
222
|
+
return q.eq("tenant_id", tenantId()).eq("connector_id", connectorId).eq("instance_key", instanceKey);
|
|
223
|
+
}
|
|
224
|
+
const store = {
|
|
225
|
+
connectorId,
|
|
226
|
+
instanceKey,
|
|
227
|
+
async get() {
|
|
228
|
+
const row = unwrap(
|
|
229
|
+
await scoped(client().from(CONNECTIONS_TABLE).select("*")).maybeSingle(),
|
|
230
|
+
`read ${CONNECTIONS_TABLE}`
|
|
231
|
+
);
|
|
232
|
+
return row ? toConnection(row) : null;
|
|
233
|
+
},
|
|
234
|
+
async save(input = {}) {
|
|
235
|
+
const patch = input.settings ?? {};
|
|
236
|
+
assertSettingsAllowed(patch, options, connectorId);
|
|
237
|
+
const current = await store.get();
|
|
238
|
+
const settings = { ...current?.settings ?? {}, ...patch };
|
|
239
|
+
const row = {
|
|
240
|
+
tenant_id: tenantId(),
|
|
241
|
+
connector_id: connectorId,
|
|
242
|
+
instance_key: instanceKey,
|
|
243
|
+
settings,
|
|
244
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
245
|
+
};
|
|
246
|
+
if (input.displayName !== void 0) row.display_name = input.displayName;
|
|
247
|
+
if (input.active !== void 0) row.active = input.active;
|
|
248
|
+
if (input.providerAccountKey !== void 0) row.provider_account_key = input.providerAccountKey;
|
|
249
|
+
if (current?.id) row.id = current.id;
|
|
250
|
+
const missing = options.settings?.length ? missingRequiredSettings(options.settings, settings) : [];
|
|
251
|
+
const reactivating = input.active === true;
|
|
252
|
+
if (missing.length > 0) {
|
|
253
|
+
row.status = "pending";
|
|
254
|
+
row.status_detail = setupStatusDetail(missing);
|
|
255
|
+
} else if (!current || current.status === "pending" || reactivating && current.status === "revoked") {
|
|
256
|
+
row.status = "connected";
|
|
257
|
+
row.status_detail = null;
|
|
258
|
+
}
|
|
259
|
+
const saved = unwrap(
|
|
260
|
+
await client().from(CONNECTIONS_TABLE).upsert(row, { onConflict: "tenant_id,connector_id,instance_key" }).select("*").single(),
|
|
261
|
+
`write ${CONNECTIONS_TABLE}`
|
|
262
|
+
);
|
|
263
|
+
if (!saved) throw new Error(`[@fayz-ai/core] write ${CONNECTIONS_TABLE}: no row returned.`);
|
|
264
|
+
return toConnection(saved);
|
|
265
|
+
},
|
|
266
|
+
async disconnect() {
|
|
267
|
+
const answer = await scoped(
|
|
268
|
+
client().from(CONNECTIONS_TABLE).update({
|
|
269
|
+
active: false,
|
|
270
|
+
status: "revoked",
|
|
271
|
+
settings: {},
|
|
272
|
+
cursors: {},
|
|
273
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
274
|
+
})
|
|
275
|
+
);
|
|
276
|
+
unwrap(answer, `disconnect ${CONNECTIONS_TABLE}`);
|
|
277
|
+
},
|
|
278
|
+
async listRuns(opts = {}) {
|
|
279
|
+
const connection = await store.get();
|
|
280
|
+
if (!connection?.id) return [];
|
|
281
|
+
const answer = await client().from(SYNC_RUNS_TABLE).select("*").eq("connection_id", connection.id).order("started_at", { ascending: false }).limit(opts.limit ?? 20);
|
|
282
|
+
const rows = unwrap(answer, `read ${SYNC_RUNS_TABLE}`);
|
|
283
|
+
return (rows ?? []).map(toRun);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
return store;
|
|
287
|
+
}
|
|
288
|
+
function connectionKey(connectorId, instanceKey = "") {
|
|
289
|
+
return `${connectorId}\0${instanceKey}`;
|
|
290
|
+
}
|
|
291
|
+
async function listConnections(connectorIds, options = {}) {
|
|
292
|
+
const found = /* @__PURE__ */ new Map();
|
|
293
|
+
const ids = [...new Set(connectorIds)].filter(Boolean);
|
|
294
|
+
if (ids.length === 0) return found;
|
|
295
|
+
const c = options.client ?? getSupabaseClientOptional();
|
|
296
|
+
if (!c) throw new Error("[@fayz-ai/core] Supabase client not available. Call createFayzApp with a provider first.");
|
|
297
|
+
const tenantId = options.tenantId ?? getActiveTenantId();
|
|
298
|
+
if (!tenantId) throw new Error("[@fayz-ai/core] listConnections: no active tenant.");
|
|
299
|
+
const rows = unwrap(
|
|
300
|
+
await c.from(CONNECTIONS_TABLE).select("*").eq("tenant_id", tenantId).in("connector_id", ids),
|
|
301
|
+
`read ${CONNECTIONS_TABLE}`
|
|
302
|
+
);
|
|
303
|
+
for (const row of rows ?? []) {
|
|
304
|
+
const connection = toConnection(row);
|
|
305
|
+
found.set(connectionKey(connection.connectorId, connection.instanceKey), connection);
|
|
306
|
+
}
|
|
307
|
+
return found;
|
|
308
|
+
}
|
|
309
|
+
function connectionSettings(store) {
|
|
310
|
+
return {
|
|
311
|
+
async getSettings() {
|
|
312
|
+
return (await store.get())?.settings ?? {};
|
|
313
|
+
},
|
|
314
|
+
async saveSettings(values) {
|
|
315
|
+
await store.save({ settings: values });
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function connectionRuns(store) {
|
|
320
|
+
return {
|
|
321
|
+
async listRuns(options) {
|
|
322
|
+
try {
|
|
323
|
+
return await (typeof store === "function" ? store() : store).listRuns(options);
|
|
324
|
+
} catch {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function connectionStatus(connectorId, derive, options = {}) {
|
|
331
|
+
const instanceKey = options.instanceKey ?? "";
|
|
332
|
+
return {
|
|
333
|
+
instanceKey,
|
|
334
|
+
statusFromConnection: derive,
|
|
335
|
+
async getStatus() {
|
|
336
|
+
try {
|
|
337
|
+
const store = options.store?.() ?? createConnectionStore(connectorId, { instanceKey });
|
|
338
|
+
return derive(await store.get());
|
|
339
|
+
} catch {
|
|
340
|
+
return { connected: false };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/integrations/request.ts
|
|
347
|
+
var RequestBlockedError = class extends Error {
|
|
348
|
+
constructor(connectorId, reason, elapsedMs, detail) {
|
|
349
|
+
super(`[${connectorId}] request plane blocked: ${reason}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
350
|
+
this.name = "RequestBlockedError";
|
|
351
|
+
this.connectorId = connectorId;
|
|
352
|
+
this.reason = reason;
|
|
353
|
+
this.elapsedMs = elapsedMs;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
var RequestPlaneError = class extends Error {
|
|
357
|
+
constructor(message) {
|
|
358
|
+
super(message);
|
|
359
|
+
this.name = "RequestPlaneError";
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
function requestPlaneSpec(def) {
|
|
363
|
+
const planes = def.planes ?? ["sync"];
|
|
364
|
+
if (!planes.includes("request")) {
|
|
365
|
+
throw new RequestPlaneError(`[${def.id}] is not on the request plane \u2014 declare planes: ['request'].`);
|
|
366
|
+
}
|
|
367
|
+
if (typeof def.latencyBudgetMs !== "number" || !Number.isFinite(def.latencyBudgetMs) || def.latencyBudgetMs <= 0) {
|
|
368
|
+
throw new RequestPlaneError(
|
|
369
|
+
`[${def.id}] request-plane connector must declare latencyBudgetMs \u2014 how long the buyer may wait.`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
if (def.onFailure !== "degrade" && def.onFailure !== "block") {
|
|
373
|
+
throw new RequestPlaneError(
|
|
374
|
+
`[${def.id}] request-plane connector must declare onFailure: 'degrade' | 'block'. Shipping degrades (the sale continues); payment blocks (the order does not confirm).`
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
return { latencyBudgetMs: def.latencyBudgetMs, onFailure: def.onFailure };
|
|
378
|
+
}
|
|
379
|
+
function hasRequestPlane(def) {
|
|
380
|
+
try {
|
|
381
|
+
requestPlaneSpec(def);
|
|
382
|
+
return true;
|
|
383
|
+
} catch {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function runRequest(def, attempt) {
|
|
388
|
+
const spec = requestPlaneSpec(def);
|
|
389
|
+
const now = attempt.now ?? (() => Date.now());
|
|
390
|
+
const started = now();
|
|
391
|
+
const degradeOrBlock = async (reason, detail) => {
|
|
392
|
+
const elapsedMs = now() - started;
|
|
393
|
+
if (spec.onFailure === "block") throw new RequestBlockedError(def.id, reason, elapsedMs, detail);
|
|
394
|
+
if (!attempt.fallback) {
|
|
395
|
+
throw new RequestPlaneError(
|
|
396
|
+
`[${def.id}] declares onFailure: 'degrade' but the call supplied no fallback. Degrading requires an answer to degrade to.`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
value: await attempt.fallback(reason, detail),
|
|
401
|
+
source: "fallback",
|
|
402
|
+
elapsedMs,
|
|
403
|
+
degraded: { reason, detail }
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
if (attempt.skip) return degradeOrBlock("not-connected");
|
|
407
|
+
const controller = new AbortController();
|
|
408
|
+
let timer;
|
|
409
|
+
const budget = new Promise((resolve) => {
|
|
410
|
+
timer = setTimeout(() => resolve("timeout"), spec.latencyBudgetMs);
|
|
411
|
+
});
|
|
412
|
+
try {
|
|
413
|
+
const raced = await Promise.race([
|
|
414
|
+
attempt.call(controller.signal).then((value) => ({ ok: true, value })),
|
|
415
|
+
budget
|
|
416
|
+
]);
|
|
417
|
+
if (raced === "timeout") {
|
|
418
|
+
controller.abort();
|
|
419
|
+
return await degradeOrBlock("timeout", `budget of ${spec.latencyBudgetMs}ms spent`);
|
|
420
|
+
}
|
|
421
|
+
return { value: raced.value, source: "provider", elapsedMs: now() - started };
|
|
422
|
+
} catch (error) {
|
|
423
|
+
if (error instanceof RequestBlockedError || error instanceof RequestPlaneError) throw error;
|
|
424
|
+
return await degradeOrBlock("provider-error", error instanceof Error ? error.message : String(error));
|
|
425
|
+
} finally {
|
|
426
|
+
if (timer) clearTimeout(timer);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
async function runEffect(ledger, input, perform) {
|
|
430
|
+
if (!input.idempotencyKey) {
|
|
431
|
+
throw new RequestPlaneError("runEffect requires an idempotencyKey \u2014 an effect nobody can name twice is an effect that runs twice.");
|
|
432
|
+
}
|
|
433
|
+
const claim = await ledger.claim(input);
|
|
434
|
+
if (!claim.claimed) return { runId: claim.runId, performed: false };
|
|
435
|
+
try {
|
|
436
|
+
const value = await perform(claim.runId);
|
|
437
|
+
await ledger.settle({ runId: claim.runId, status: "success" });
|
|
438
|
+
return { runId: claim.runId, performed: true, value };
|
|
439
|
+
} catch (error) {
|
|
440
|
+
await ledger.settle({
|
|
441
|
+
runId: claim.runId,
|
|
442
|
+
status: "error",
|
|
443
|
+
error: error instanceof Error ? error.message : String(error)
|
|
444
|
+
});
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function createRpcEffectLedger(client) {
|
|
449
|
+
const call = async (fn, args) => {
|
|
450
|
+
const { data, error } = await client.rpc(fn, args);
|
|
451
|
+
if (error) throw new Error(`[@fayz-ai/core] ${fn}: ${error.message}`);
|
|
452
|
+
return data;
|
|
453
|
+
};
|
|
454
|
+
return {
|
|
455
|
+
async claim(input) {
|
|
456
|
+
const rows = await call("plg_claim_effect", {
|
|
457
|
+
p_connection_id: input.connectionId,
|
|
458
|
+
p_idempotency_key: input.idempotencyKey,
|
|
459
|
+
p_direction: input.direction,
|
|
460
|
+
p_trigger_kind: input.triggerKind,
|
|
461
|
+
p_stream: input.stream ?? null
|
|
462
|
+
});
|
|
463
|
+
const row = Array.isArray(rows) ? rows[0] : rows;
|
|
464
|
+
if (!row?.run_id) throw new Error("[@fayz-ai/core] plg_claim_effect returned no run");
|
|
465
|
+
return { runId: row.run_id, claimed: !!row.claimed };
|
|
466
|
+
},
|
|
467
|
+
async settle(input) {
|
|
468
|
+
await call("plg_settle_effect", {
|
|
469
|
+
p_run_id: input.runId,
|
|
470
|
+
p_status: input.status,
|
|
471
|
+
p_stats: input.stats ?? {},
|
|
472
|
+
p_error: input.error ?? null
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function buyerIdempotencyKey(parts) {
|
|
478
|
+
const { effect, subject, amountCents } = parts;
|
|
479
|
+
if (!effect || !subject) {
|
|
480
|
+
throw new RequestPlaneError("buyerIdempotencyKey needs both an effect and a subject (the order, the shipment).");
|
|
481
|
+
}
|
|
482
|
+
return amountCents === void 0 ? `${effect}:${subject}` : `${effect}:${subject}:${amountCents}`;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// src/integrations/index.ts
|
|
486
|
+
var CONNECTOR_CATEGORIES = [
|
|
487
|
+
/** The provider IS a database or backend the app is built on. */
|
|
488
|
+
"database",
|
|
489
|
+
/** Money in: gateways, acquirers, Pix. */
|
|
490
|
+
"payments",
|
|
491
|
+
/** A bank account read (open banking, statements) — not a gateway. */
|
|
492
|
+
"banking",
|
|
493
|
+
/** Stock, invoicing, back office. */
|
|
494
|
+
"erp",
|
|
495
|
+
/** Freight quotes, labels, tracking. */
|
|
496
|
+
"logistics",
|
|
497
|
+
/** Someone else's storefront the business also sells on (iFood, Shopee). */
|
|
498
|
+
"marketplace",
|
|
499
|
+
/** The business's OWN online store. */
|
|
500
|
+
"ecommerce",
|
|
501
|
+
/** Contacts, deals, pipeline. */
|
|
502
|
+
"crm",
|
|
503
|
+
/** Courses sold by a third party (Hotmart, Kiwify). */
|
|
504
|
+
"courses",
|
|
505
|
+
/** Bookings and calendars. */
|
|
506
|
+
"scheduling",
|
|
507
|
+
/** Conversations: WhatsApp, SMS, email campaigns. */
|
|
508
|
+
"messaging",
|
|
509
|
+
/** Measurement, pixels, SEO. */
|
|
510
|
+
"analytics",
|
|
511
|
+
/** Presence on a map or a directory (Google Meu Negócio). */
|
|
512
|
+
"local",
|
|
513
|
+
/** Models and agent frameworks. */
|
|
514
|
+
"ai",
|
|
515
|
+
/** Wiring between other tools (Zapier, n8n). */
|
|
516
|
+
"automation"
|
|
517
|
+
];
|
|
518
|
+
var CATEGORY_SET = new Set(CONNECTOR_CATEGORIES);
|
|
519
|
+
function isConnectorCategory(value) {
|
|
520
|
+
return typeof value === "string" && CATEGORY_SET.has(value);
|
|
521
|
+
}
|
|
522
|
+
function invocationPlane(moment) {
|
|
523
|
+
return moment.plane ?? "request";
|
|
524
|
+
}
|
|
525
|
+
function connectorPlanes(def) {
|
|
526
|
+
return def.planes?.length ? def.planes : ["sync"];
|
|
527
|
+
}
|
|
528
|
+
function preferredPlane(def) {
|
|
529
|
+
const planes = connectorPlanes(def);
|
|
530
|
+
return planes.includes("agentic") ? "agentic" : planes[0];
|
|
531
|
+
}
|
|
532
|
+
function connectorDirection(def) {
|
|
533
|
+
return def.direction ?? "outbound";
|
|
534
|
+
}
|
|
535
|
+
function withAfterHooks(target, methods) {
|
|
536
|
+
return new Proxy(target, {
|
|
537
|
+
get(obj, prop, receiver) {
|
|
538
|
+
const orig = Reflect.get(obj, prop, receiver);
|
|
539
|
+
const hook = methods[prop];
|
|
540
|
+
if (typeof orig !== "function" || !hook) return orig;
|
|
541
|
+
return (...args) => {
|
|
542
|
+
const result = orig.apply(obj, args);
|
|
543
|
+
if (result instanceof Promise) {
|
|
544
|
+
return result.then((value) => {
|
|
545
|
+
try {
|
|
546
|
+
void Promise.resolve(hook(args, value)).catch(() => {
|
|
547
|
+
});
|
|
548
|
+
} catch {
|
|
549
|
+
}
|
|
550
|
+
return value;
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
void Promise.resolve(hook(args, result)).catch(() => {
|
|
555
|
+
});
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
return result;
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export { CONNECTIONS_TABLE, CONNECTOR_CATEGORIES, ConnectionSettingsError, ConnectorRuntimeTokenUnavailableError, RequestBlockedError, RequestPlaneError, SYNC_RUNS_TABLE, assertSettingsAllowed, buyerIdempotencyKey, connectionKey, connectionRuns, connectionSettings, connectionStatus, connectorDirection, connectorEntities, connectorGrantScopes, connectorPermissions, connectorPlanes, connectorRuntimeToken, connectorSettings, connectorSubscribes, connectorSubscribesTo, createConnectionStore, createRpcEffectLedger, eventKeyMatches, getConnectorRuntimeTokenSource, grants, groupedPermissions, hasRequestPlane, hasSensitivePermission, invocationPlane, isConnectorCategory, isDataPermission, isEventPermission, isSettingSet, listConnections, missingRequiredSettings, permissionActions, permissionsWrite, preferredPlane, requestPlaneSpec, runEffect, runRequest, setConnectorRuntimeTokenSource, settingsStatus, setupStatusDetail, sortedPermissions, withAfterHooks };
|
|
565
|
+
//# sourceMappingURL=chunk-7GJTLBG3.js.map
|
|
566
|
+
//# sourceMappingURL=chunk-7GJTLBG3.js.map
|