@sprigr/apps-app-sdk 0.1.1 → 0.2.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/README.md +27 -6
- package/dist/index.d.ts +156 -3
- package/dist/index.js +118 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
# @sprigr/apps-app-sdk
|
|
2
2
|
|
|
3
|
-
Shared handler types + helpers for
|
|
3
|
+
Shared handler types + helpers for Sprigr marketplace apps.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npm install @sprigr/apps-app-sdk
|
|
7
|
+
```
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
## Talking to the platform
|
|
10
|
+
|
|
11
|
+
The runtime injects the `env.SPRIGR` host object **only on `/__sprigr/*` dispatch paths** (tool, schedule, event and platform-webhook handlers). Inline Next.js route handlers never get it, so an app that receives its provider webhook on an inline route has no working `env.SPRIGR` on the path that matters most.
|
|
12
|
+
|
|
13
|
+
- `emitMarketplaceEvent(env, event, payload, opts?)` — emit from either context. Uses the injected binding when present, the install-token bridge (`POST ${SPRIGR_PLATFORM_BASE}/internal/wfp/emit`) otherwise. Never throws, so a webhook ack is never at risk; times out after 5s. Returns `{ emitted, via: 'binding' | 'http' | 'none', eventId?, error? }` — record `via` in your audit row.
|
|
14
|
+
- `withSprigrEmitFallback(env)` — repair `env.SPRIGR.emit` once and leave existing call sites untouched. Matches the host object's contract (resolves `{ ok, eventId, queued }`, throws on non-2xx).
|
|
15
|
+
- `resolveInstallBridge(env)` / `installTokenPost(bridge, path, body, opts?)` — build your own `/internal/wfp/*` fallback (collections, files, inbox) with the auth and error extraction handled.
|
|
16
|
+
- `overlaySprigr(env, sprigr)` — overlay a patched `SPRIGR` via `Object.create`. Never rebuild a dispatch-path env by spread: the real bindings live on the prototype and `SPRIGR` is non-enumerable, so `{ ...env }` yields an env whose `DB` is `undefined`.
|
|
17
|
+
|
|
18
|
+
## Webhook callback URLs
|
|
19
|
+
|
|
20
|
+
- `resolvePlatformWebhookBase(env)` / `buildMarketplaceWebhookUrl(env, installId, topicPath)` — env-correct platform host, so a staging install never registers prod-pointing subscriptions.
|
|
21
|
+
|
|
22
|
+
## Misc
|
|
23
|
+
|
|
24
|
+
- `fetchWithRetry` — rate-limit-header-aware, jittered retry
|
|
25
|
+
- `constantTimeEqual(a, b)` — bearer-secret verification
|
|
26
|
+
- `encodeState` / `decodeState` — OAuth state base64url
|
|
27
|
+
- `parseActor` / `actorKey` — per-actor token scoping
|
|
28
|
+
- `putAppFile` / `putAppFileStream` / `appFileUrl` / `getAppFile` / `listAppFiles` / `deleteAppFile` — durable app-scoped file storage from outside the injected bridge
|
|
29
|
+
- `fetchFileBytes` / `fetchFileAsBase64` / `bytesToBase64` / `base64ToBytes` — file byte helpers
|
|
30
|
+
|
|
31
|
+
Full platform semantics: [`docs/platform-reference.md`](../../docs/platform-reference.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -235,6 +235,150 @@ declare function resolvePlatformWebhookBase(env: PlatformHostEnv, override?: str
|
|
|
235
235
|
*/
|
|
236
236
|
declare function buildMarketplaceWebhookUrl(env: PlatformHostEnv, installId: string, topicPath: string, override?: string | null): string;
|
|
237
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Talking to the platform from BOTH execution contexts.
|
|
240
|
+
*
|
|
241
|
+
* The marketplace runtime injects the `SPRIGR` host object **only on
|
|
242
|
+
* `/__sprigr/*` dispatch paths** (tool, schedule, event and platform-webhook
|
|
243
|
+
* handlers). Inline Next.js route handlers (`app/api/.../route.ts`) never
|
|
244
|
+
* receive it. Any app whose provider webhook lands on an inline route (because
|
|
245
|
+
* the provider doesn't HMAC bodies, so the marketplace dispatcher can't verify
|
|
246
|
+
* the delivery) therefore has NO working `env.SPRIGR` on the one path that
|
|
247
|
+
* matters most.
|
|
248
|
+
*
|
|
249
|
+
* An app that only tries `env.SPRIGR.emit` from there emits nothing at all,
|
|
250
|
+
* silently, while still acking the provider 200. That has now shipped four
|
|
251
|
+
* separate times (shopify #478, procore, starshipit, cin7-core), so the fix
|
|
252
|
+
* lives here instead of being re-derived per app.
|
|
253
|
+
*
|
|
254
|
+
* The escape hatch: `SPRIGR_PLATFORM_BASE` and `SPRIGR_INSTALL_TOKEN` are plain
|
|
255
|
+
* script vars stamped on every per-install WFP upload, so unlike `SPRIGR` they
|
|
256
|
+
* ARE readable from an inline route. With them you can call the same
|
|
257
|
+
* `/internal/wfp/*` endpoints the host object calls under the covers:
|
|
258
|
+
*
|
|
259
|
+
* POST ${SPRIGR_PLATFORM_BASE}/internal/wfp/emit
|
|
260
|
+
* Authorization: Bearer ${SPRIGR_INSTALL_TOKEN}
|
|
261
|
+
* { event, payload, sourceIntegration?, targetAppInstallationId?, dedupId? }
|
|
262
|
+
*
|
|
263
|
+
* Use `emitMarketplaceEvent` for a single call site, or `withSprigrEmitFallback`
|
|
264
|
+
* to repair `env.SPRIGR.emit` once and leave existing call sites untouched.
|
|
265
|
+
*/
|
|
266
|
+
|
|
267
|
+
/** Resolved per-install bridge credentials. */
|
|
268
|
+
interface InstallBridge {
|
|
269
|
+
/** Platform base, trailing slash stripped. */
|
|
270
|
+
base: string;
|
|
271
|
+
/** Per-install bearer, `<installId>.<base64url-hmac>`. */
|
|
272
|
+
token: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Minimal env shape these helpers read. Every app's own env interface
|
|
276
|
+
* (ProcoreEnv, ShopifyEnv, ...) already satisfies it structurally, so passing
|
|
277
|
+
* the full env is fine.
|
|
278
|
+
*/
|
|
279
|
+
interface WfpBridgeEnv {
|
|
280
|
+
SPRIGR?: unknown;
|
|
281
|
+
SPRIGR_PLATFORM_BASE?: string;
|
|
282
|
+
SPRIGR_INSTALL_TOKEN?: string;
|
|
283
|
+
INSTALL_ID?: string;
|
|
284
|
+
[key: string]: unknown;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Pull the install-token bridge off env, or null when either half is missing.
|
|
288
|
+
*
|
|
289
|
+
* Deliberately has NO production default for the base: an app that guessed
|
|
290
|
+
* `https://webhooks.sprigr.com` would have a *staging* install firing events
|
|
291
|
+
* into *prod*, where the install id is unknown and the call 404s into a tail
|
|
292
|
+
* nobody watching staging ever reads. Failing closed is louder.
|
|
293
|
+
*/
|
|
294
|
+
declare function resolveInstallBridge(env: WfpBridgeEnv): InstallBridge | null;
|
|
295
|
+
/**
|
|
296
|
+
* Human-readable note about which half of the bridge is missing. Which binding
|
|
297
|
+
* is absent is the entire diagnosis, so never collapse this to a bare
|
|
298
|
+
* "not configured".
|
|
299
|
+
*/
|
|
300
|
+
declare function describeMissingBridge(env: WfpBridgeEnv): string;
|
|
301
|
+
/**
|
|
302
|
+
* Overlay a replacement `SPRIGR` onto env WITHOUT rebuilding it by spread.
|
|
303
|
+
*
|
|
304
|
+
* On the `/__sprigr/*` dispatch path the platform hands handlers
|
|
305
|
+
* `Object.create(bindings)` with `SPRIGR` as a NON-ENUMERABLE own property: the
|
|
306
|
+
* real bindings (`DB`, secrets, the install token itself) live on the
|
|
307
|
+
* prototype. `{ ...env }` copies none of that — it yields an env whose `DB` is
|
|
308
|
+
* undefined. That is exactly how every scheduled `ms_index_files` run died for
|
|
309
|
+
* 24h on staging (microsoft-365 v0.12.0, fixed in v0.14.2, #758). Chaining off
|
|
310
|
+
* env via the prototype preserves whatever shape the caller had, plain or
|
|
311
|
+
* wrapped.
|
|
312
|
+
*/
|
|
313
|
+
declare function overlaySprigr<E extends object>(env: E, sprigr: unknown): E;
|
|
314
|
+
/** Reply shape of `/internal/wfp/emit`. */
|
|
315
|
+
interface WfpEmitReply {
|
|
316
|
+
ok?: boolean;
|
|
317
|
+
eventId?: string;
|
|
318
|
+
/** False when the platform accepted the call but the enqueue itself failed. */
|
|
319
|
+
queued?: boolean;
|
|
320
|
+
error?: string;
|
|
321
|
+
}
|
|
322
|
+
/** How the emit reached the platform. `none` means it never left. */
|
|
323
|
+
type EmitTransport = 'binding' | 'http' | 'none';
|
|
324
|
+
interface EmitResult {
|
|
325
|
+
emitted: boolean;
|
|
326
|
+
eventId?: string;
|
|
327
|
+
/** Which transport carried it. Worth recording in an audit row: it makes the
|
|
328
|
+
* binding silently disappearing again visible without a redeploy. */
|
|
329
|
+
via: EmitTransport;
|
|
330
|
+
error?: string;
|
|
331
|
+
}
|
|
332
|
+
/** Default ceiling on the platform call. A webhook ack matters more than the
|
|
333
|
+
* event: providers retry a failed delivery, and a slow emit must never turn
|
|
334
|
+
* the ack into a non-2xx. */
|
|
335
|
+
declare const DEFAULT_EMIT_TIMEOUT_MS = 5000;
|
|
336
|
+
interface EmitMarketplaceEventOptions extends MarketplaceEventEmitOpts {
|
|
337
|
+
/** Override the 5s ceiling on the HTTP fallback. */
|
|
338
|
+
timeoutMs?: number;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* POST a JSON body to an `/internal/wfp/*` endpoint with the install-token
|
|
342
|
+
* bearer. Returns the parsed reply and throws on a non-2xx, matching what the
|
|
343
|
+
* injected host object does, so callers can treat both paths identically.
|
|
344
|
+
*
|
|
345
|
+
* Shared by every install-token fallback (emit, inbox, collections, files) so
|
|
346
|
+
* the auth header and error-detail extraction live in exactly one place.
|
|
347
|
+
*/
|
|
348
|
+
declare function installTokenPost(bridge: InstallBridge, path: string, body: unknown, opts?: {
|
|
349
|
+
label?: string;
|
|
350
|
+
timeoutMs?: number;
|
|
351
|
+
signal?: AbortSignal;
|
|
352
|
+
}): Promise<Record<string, unknown>>;
|
|
353
|
+
/**
|
|
354
|
+
* Emit a marketplace event from any execution context, never throwing.
|
|
355
|
+
*
|
|
356
|
+
* Prefers the injected `env.SPRIGR.emit`; falls back to the install-token
|
|
357
|
+
* bridge when it is absent (i.e. on an inline Next route). Safe to call from a
|
|
358
|
+
* webhook receiver: every failure is reported in the result rather than raised,
|
|
359
|
+
* so the provider ack is never at risk.
|
|
360
|
+
*
|
|
361
|
+
* const r = await emitMarketplaceEvent(env, 'procore.rfi.updated', payload, {
|
|
362
|
+
* sourceIntegration: { integrationId: env.INSTALL_ID, integrationType: 'procore' },
|
|
363
|
+
* });
|
|
364
|
+
* await audit(env, 'emit', JSON.stringify(r)); // records `via`
|
|
365
|
+
*/
|
|
366
|
+
declare function emitMarketplaceEvent(env: WfpBridgeEnv, event: string, payload: unknown, opts?: EmitMarketplaceEventOptions): Promise<EmitResult>;
|
|
367
|
+
/**
|
|
368
|
+
* Return an env whose `SPRIGR.emit` works even on an inline route, leaving
|
|
369
|
+
* existing `env.SPRIGR.emit(...)` call sites untouched. Use this when an app
|
|
370
|
+
* emits from many places; use `emitMarketplaceEvent` for a single call site.
|
|
371
|
+
*
|
|
372
|
+
* The installed `emit` matches the injected host object's contract: it resolves
|
|
373
|
+
* `{ ok, eventId, queued }` and THROWS on a non-2xx, so existing
|
|
374
|
+
* `queued === false` checks and try/catch blocks keep working unchanged.
|
|
375
|
+
*
|
|
376
|
+
* When the bindings are absent the env is returned as-is (`SPRIGR` stays
|
|
377
|
+
* undefined) so callers fail the same way they already do rather than
|
|
378
|
+
* discovering a new shape.
|
|
379
|
+
*/
|
|
380
|
+
declare function withSprigrEmitFallback<E extends WfpBridgeEnv & object>(env: E): E;
|
|
381
|
+
|
|
238
382
|
/**
|
|
239
383
|
* Caller identity stamped by the platform on agent-initiated WFP dispatches.
|
|
240
384
|
*
|
|
@@ -601,12 +745,17 @@ interface SprigrFilesEditInput {
|
|
|
601
745
|
/** App-relative key of the source file already in app storage. */
|
|
602
746
|
file_key: string;
|
|
603
747
|
/** pdf runs the PyMuPDF ops engine (list_fields / fill_form / replace_text);
|
|
604
|
-
* docx/xlsx run python-docx / openpyxl
|
|
605
|
-
|
|
748
|
+
* docx/xlsx run python-docx / openpyxl; xer/pmxml/mspdi run the platform's
|
|
749
|
+
* pure-TS schedule engine in-worker (Primavera P6 / MS Project XML, no
|
|
750
|
+
* sandbox cold-start exposure). */
|
|
751
|
+
format: 'docx' | 'xlsx' | 'pdf' | 'xer' | 'pmxml' | 'mspdi';
|
|
606
752
|
operations: Array<Record<string, unknown>>;
|
|
607
753
|
output_filename?: string;
|
|
608
754
|
output_key?: string;
|
|
609
755
|
allow_lossy?: boolean;
|
|
756
|
+
/** Schedule formats only: write even when the FK-integrity gate finds
|
|
757
|
+
* errors (the validation result is still returned). */
|
|
758
|
+
skip_validation?: boolean;
|
|
610
759
|
/** Deterministic idempotency token: the platform persists the pipeline's
|
|
611
760
|
* terminal result under it, so a retry of the same logical edit after a
|
|
612
761
|
* dispatch timeout replays the finished result instead of re-running the
|
|
@@ -622,6 +771,10 @@ interface SprigrFilesEditResult {
|
|
|
622
771
|
failed_operations?: Array<Record<string, unknown>>;
|
|
623
772
|
warning?: string;
|
|
624
773
|
error?: string;
|
|
774
|
+
/** Schedule formats only: the FK-integrity result ({errorCount, warningCount, issues}). */
|
|
775
|
+
validation?: Record<string, unknown>;
|
|
776
|
+
/** Schedule formats only: one-line engine summary incl. matched counts. */
|
|
777
|
+
summary?: string;
|
|
625
778
|
}
|
|
626
779
|
/** Input to `env.SPRIGR.files.create` (build a NEW docx/xlsx from a spec). */
|
|
627
780
|
interface SprigrFilesCreateInput {
|
|
@@ -700,4 +853,4 @@ interface ExecutionContextLike {
|
|
|
700
853
|
}
|
|
701
854
|
type HandlerFn<TArgs, TResult> = (args: TArgs, env: Record<string, unknown>, ctx?: ExecutionContextLike) => Promise<TResult>;
|
|
702
855
|
|
|
703
|
-
export { type Actor, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type D1Like, type D1PreparedStatementLike, DEFAULT_MAX_FILE_BYTES, type EventArgs, type ExecutionContextLike, type FetchFileResult, type FetchWithRetryOptions, type GetAppFileResult, type HandlerFn, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, type OAuthState, type PlatformHostEnv, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type ScheduleArgs, type SprigrDataApi, type SprigrDataSearchOpts, type SprigrDataSearchResult, type SprigrFilesApi, type SprigrFilesApiWithEdit, type SprigrFilesCreateInput, type SprigrFilesCreateResult, type SprigrFilesEditInput, type SprigrFilesEditResult, type SprigrFilesJobResult, type WebhookArgs, actorKey, appFileUrl, base64ToBytes, buildMarketplaceWebhookUrl, bytesToBase64, constantTimeEqual, decodeState, deleteAppFile, encodeState, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, getAppFile, hmacSha256Hex, listAppFiles, parseActor, putAppFile, putAppFileStream, randomHex, resolvePlatformWebhookBase };
|
|
856
|
+
export { type Actor, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type D1Like, type D1PreparedStatementLike, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EventArgs, type ExecutionContextLike, type FetchFileResult, type FetchWithRetryOptions, type GetAppFileResult, type HandlerFn, type InstallBridge, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, type OAuthState, type PlatformHostEnv, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type ScheduleArgs, type SprigrDataApi, type SprigrDataSearchOpts, type SprigrDataSearchResult, type SprigrFilesApi, type SprigrFilesApiWithEdit, type SprigrFilesCreateInput, type SprigrFilesCreateResult, type SprigrFilesEditInput, type SprigrFilesEditResult, type SprigrFilesJobResult, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, appFileUrl, base64ToBytes, buildMarketplaceWebhookUrl, bytesToBase64, constantTimeEqual, decodeState, deleteAppFile, describeMissingBridge, emitMarketplaceEvent, encodeState, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, getAppFile, hmacSha256Hex, installTokenPost, listAppFiles, overlaySprigr, parseActor, putAppFile, putAppFileStream, randomHex, resolveInstallBridge, resolvePlatformWebhookBase, withSprigrEmitFallback };
|
package/dist/index.js
CHANGED
|
@@ -166,6 +166,116 @@ function buildMarketplaceWebhookUrl(env, installId, topicPath, override) {
|
|
|
166
166
|
return `${base}/webhook/marketplace/${installId}/${topicPath}`;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
// src/wfp-bridge.ts
|
|
170
|
+
function resolveInstallBridge(env) {
|
|
171
|
+
const token = typeof env.SPRIGR_INSTALL_TOKEN === "string" ? env.SPRIGR_INSTALL_TOKEN : "";
|
|
172
|
+
const base = typeof env.SPRIGR_PLATFORM_BASE === "string" ? env.SPRIGR_PLATFORM_BASE.replace(/\/+$/, "") : "";
|
|
173
|
+
if (!token || !base) return null;
|
|
174
|
+
return { base, token };
|
|
175
|
+
}
|
|
176
|
+
function describeMissingBridge(env) {
|
|
177
|
+
const base = typeof env.SPRIGR_PLATFORM_BASE === "string" && env.SPRIGR_PLATFORM_BASE ? "set" : "unset";
|
|
178
|
+
const token = typeof env.SPRIGR_INSTALL_TOKEN === "string" && env.SPRIGR_INSTALL_TOKEN ? "set" : "unset";
|
|
179
|
+
return `no_emit_path (SPRIGR unbound, SPRIGR_PLATFORM_BASE=${base}, SPRIGR_INSTALL_TOKEN=${token})`;
|
|
180
|
+
}
|
|
181
|
+
function overlaySprigr(env, sprigr) {
|
|
182
|
+
const out = Object.create(env);
|
|
183
|
+
Object.defineProperty(out, "SPRIGR", { value: sprigr, enumerable: false, configurable: false });
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
var DEFAULT_EMIT_TIMEOUT_MS = 5e3;
|
|
187
|
+
function compactOpts(opts) {
|
|
188
|
+
const out = {};
|
|
189
|
+
if (!opts) return out;
|
|
190
|
+
if (opts.sourceIntegration !== void 0) out.sourceIntegration = opts.sourceIntegration;
|
|
191
|
+
if (opts.targetAppInstallationId !== void 0) out.targetAppInstallationId = opts.targetAppInstallationId;
|
|
192
|
+
if (opts.dedupId !== void 0) out.dedupId = opts.dedupId;
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
function bindingEmit(env) {
|
|
196
|
+
const sprigr = env.SPRIGR;
|
|
197
|
+
return typeof sprigr?.emit === "function" ? sprigr.emit.bind(
|
|
198
|
+
sprigr
|
|
199
|
+
) : null;
|
|
200
|
+
}
|
|
201
|
+
async function installTokenPost(bridge, path, body, opts) {
|
|
202
|
+
const label = opts?.label ?? `POST ${path}`;
|
|
203
|
+
const controller = new AbortController();
|
|
204
|
+
const timer = opts?.timeoutMs != null ? setTimeout(() => controller.abort(), opts.timeoutMs) : void 0;
|
|
205
|
+
if (opts?.signal) {
|
|
206
|
+
if (opts.signal.aborted) controller.abort();
|
|
207
|
+
else opts.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const resp = await fetch(`${bridge.base}${path}`, {
|
|
211
|
+
method: "POST",
|
|
212
|
+
headers: { authorization: `Bearer ${bridge.token}`, "content-type": "application/json" },
|
|
213
|
+
body: JSON.stringify(body),
|
|
214
|
+
signal: controller.signal
|
|
215
|
+
});
|
|
216
|
+
const text = await resp.text();
|
|
217
|
+
let parsed = null;
|
|
218
|
+
try {
|
|
219
|
+
parsed = text ? JSON.parse(text) : null;
|
|
220
|
+
} catch {
|
|
221
|
+
parsed = null;
|
|
222
|
+
}
|
|
223
|
+
if (!resp.ok) {
|
|
224
|
+
const detail = parsed && typeof parsed === "object" && "error" in parsed ? String(parsed.error) : text.slice(0, 200);
|
|
225
|
+
throw new Error(`${label} failed: ${resp.status} ${detail}`);
|
|
226
|
+
}
|
|
227
|
+
return parsed ?? {};
|
|
228
|
+
} finally {
|
|
229
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function emitMarketplaceEvent(env, event, payload, opts) {
|
|
233
|
+
const { timeoutMs, ...emitOpts } = opts ?? {};
|
|
234
|
+
const compact = compactOpts(emitOpts);
|
|
235
|
+
const passThrough = Object.keys(compact).length > 0 ? compact : void 0;
|
|
236
|
+
const injected = bindingEmit(env);
|
|
237
|
+
if (injected) {
|
|
238
|
+
try {
|
|
239
|
+
const r = await injected(event, payload, passThrough);
|
|
240
|
+
if (r && r.queued === false) {
|
|
241
|
+
return { emitted: false, via: "binding", eventId: r.eventId, error: r.error ?? "not_queued" };
|
|
242
|
+
}
|
|
243
|
+
return { emitted: r?.ok !== false, via: "binding", eventId: r?.eventId };
|
|
244
|
+
} catch (err) {
|
|
245
|
+
return { emitted: false, via: "binding", error: err instanceof Error ? err.message : String(err) };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const bridge = resolveInstallBridge(env);
|
|
249
|
+
if (!bridge) return { emitted: false, via: "none", error: describeMissingBridge(env) };
|
|
250
|
+
try {
|
|
251
|
+
const reply = await installTokenPost(
|
|
252
|
+
bridge,
|
|
253
|
+
"/internal/wfp/emit",
|
|
254
|
+
{ event, payload: payload === void 0 ? null : payload, ...compact },
|
|
255
|
+
{ label: "emit", timeoutMs: timeoutMs ?? DEFAULT_EMIT_TIMEOUT_MS }
|
|
256
|
+
);
|
|
257
|
+
if (reply.ok === false || reply.queued === false) {
|
|
258
|
+
return { emitted: false, via: "http", eventId: reply.eventId, error: reply.error ?? "not_queued" };
|
|
259
|
+
}
|
|
260
|
+
return { emitted: true, via: "http", eventId: reply.eventId };
|
|
261
|
+
} catch (err) {
|
|
262
|
+
return { emitted: false, via: "http", error: err instanceof Error ? err.message : String(err) };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function withSprigrEmitFallback(env) {
|
|
266
|
+
if (bindingEmit(env)) return env;
|
|
267
|
+
const bridge = resolveInstallBridge(env);
|
|
268
|
+
if (!bridge) return env;
|
|
269
|
+
const emit = async (event, payload, opts) => await installTokenPost(
|
|
270
|
+
bridge,
|
|
271
|
+
"/internal/wfp/emit",
|
|
272
|
+
{ event, payload: payload === void 0 ? null : payload, ...compactOpts(opts) },
|
|
273
|
+
{ label: "emit", timeoutMs: DEFAULT_EMIT_TIMEOUT_MS }
|
|
274
|
+
);
|
|
275
|
+
const existing = env.SPRIGR ?? {};
|
|
276
|
+
return overlaySprigr(env, { ...existing, emit });
|
|
277
|
+
}
|
|
278
|
+
|
|
169
279
|
// src/actor.ts
|
|
170
280
|
function parseActor(args) {
|
|
171
281
|
if (!args || typeof args !== "object") return void 0;
|
|
@@ -274,6 +384,7 @@ function deleteAppFile(env, key) {
|
|
|
274
384
|
return call(env, "delete", { key });
|
|
275
385
|
}
|
|
276
386
|
export {
|
|
387
|
+
DEFAULT_EMIT_TIMEOUT_MS,
|
|
277
388
|
DEFAULT_MAX_FILE_BYTES,
|
|
278
389
|
actorKey,
|
|
279
390
|
appFileUrl,
|
|
@@ -283,16 +394,22 @@ export {
|
|
|
283
394
|
constantTimeEqual,
|
|
284
395
|
decodeState,
|
|
285
396
|
deleteAppFile,
|
|
397
|
+
describeMissingBridge,
|
|
398
|
+
emitMarketplaceEvent,
|
|
286
399
|
encodeState,
|
|
287
400
|
fetchFileAsBase64,
|
|
288
401
|
fetchFileBytes,
|
|
289
402
|
fetchWithRetry,
|
|
290
403
|
getAppFile,
|
|
291
404
|
hmacSha256Hex,
|
|
405
|
+
installTokenPost,
|
|
292
406
|
listAppFiles,
|
|
407
|
+
overlaySprigr,
|
|
293
408
|
parseActor,
|
|
294
409
|
putAppFile,
|
|
295
410
|
putAppFileStream,
|
|
296
411
|
randomHex,
|
|
297
|
-
|
|
412
|
+
resolveInstallBridge,
|
|
413
|
+
resolvePlatformWebhookBase,
|
|
414
|
+
withSprigrEmitFallback
|
|
298
415
|
};
|