@proveanything/smartlinks 2.0.34 → 2.0.36

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.
@@ -32,6 +32,9 @@ export declare const AI_TOOL_NAMES: {
32
32
  readonly PDF_EXTRACT: "pdf.extract";
33
33
  readonly PDF_DECODE_BARCODES: "pdf.decodeBarcodes";
34
34
  readonly PDF_INSPECT_GRAPHICS: "pdf.inspectGraphics";
35
+ readonly PDF_EDIT: "pdf.edit";
36
+ readonly PDF_PREFLIGHT: "pdf.preflight";
37
+ readonly PDF_PRINT_READY: "pdf.printReady";
35
38
  readonly HTTP_REQUEST: "http.request";
36
39
  readonly TRANSLATE: "translate";
37
40
  };
package/dist/ai-tools.js CHANGED
@@ -29,6 +29,9 @@ export const AI_TOOL_NAMES = {
29
29
  PDF_EXTRACT: 'pdf.extract',
30
30
  PDF_DECODE_BARCODES: 'pdf.decodeBarcodes',
31
31
  PDF_INSPECT_GRAPHICS: 'pdf.inspectGraphics',
32
+ PDF_EDIT: 'pdf.edit',
33
+ PDF_PREFLIGHT: 'pdf.preflight',
34
+ PDF_PRINT_READY: 'pdf.printReady',
32
35
  HTTP_REQUEST: 'http.request',
33
36
  TRANSLATE: 'translate',
34
37
  };
@@ -76,6 +79,12 @@ export const BUILTIN_AI_TOOLS = [
76
79
  description: 'Deterministically decode 1D/2D barcodes (EAN/UPC/QR/Code128/DataMatrix/…) on a PDF page by rasterizing and running a WASM decoder — reliable barcode/QR values where vision hallucinates. Returns value, symbology, page, bbox, confidence. No AI.' },
77
80
  { name: 'pdf.inspectGraphics', group: 'media', capabilities: ['web:read'],
78
81
  description: 'Prepress structural inspection: per-page vector-path/image/outlined-text counts + colour spaces used, plus document-level named SPOT colours (e.g. "PANTONE 871 C"). Answers whether spot/foil plates survived. Deterministic, no AI.' },
82
+ { name: 'pdf.edit', group: 'media', capabilities: ['web:read', 'media:pdf'],
83
+ description: 'Edit an existing PDF: replace LIVE text (glyph-exact removal so neighbouring text never moves; redrawn in the original font when it has the glyphs, else an embedded fallback; CMYK/spot colour kept; matches may span lines), add text and images in boxes (cmyk converts images for print). Returns url + per-item results with verified flags. Outlined text cannot be edited.' },
84
+ { name: 'pdf.preflight', group: 'media', capabilities: ['web:read'],
85
+ description: 'Prepress preflight without changing the PDF: non-embedded fonts, RGB/Lab content, missing TrimBox/BleedBox and short bleed, low effective image resolution, transparency, white overprint, missing PDF/X output intent. Severity follows the target profile. No AI.' },
86
+ { name: 'pdf.printReady', group: 'media', capabilities: ['web:read', 'media:pdf'],
87
+ description: 'Prepare a PDF for print: RGB/Lab -> CMYK with a real press profile (spot colours kept), embed fonts, optional transparency flattening, mark as PDF/X-1a or PDF/X-4 with the output intent embedded, then preflight the RESULT. Returns url + report { compliant, findings, changes }.' },
79
88
  { name: 'http.request', group: 'net', capabilities: ['net:http'],
80
89
  description: 'SSRF-guarded outbound HTTP(S) request to a public URL; returns status/headers/body.' },
81
90
  { name: 'translate', group: 'text', capabilities: ['ai:text'],
@@ -27,7 +27,7 @@ export declare namespace collection {
27
27
  * The server derives the requesting domain from the request headers
28
28
  * (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is
29
29
  * passed — this is the call a Hub frontend makes on load to find out which
30
- * collection it is serving, whether it's reached via `{brand}.mysmartlinks.app`
30
+ * collection it is serving, whether it's reached via `{brand}.smartlinks.host`
31
31
  * or a bring-your-own custom domain (e.g. `hub.acme.com`).
32
32
  *
33
33
  * @returns Promise resolving to the CollectionResponse mapped to the domain
@@ -39,9 +39,9 @@ export declare namespace collection {
39
39
  *
40
40
  * Unlike {@link getByHub}, the domain is passed explicitly rather than derived
41
41
  * from request headers — use this for raw/cross-origin calls where the Hub
42
- * frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
42
+ * frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
43
43
  *
44
- * @param domain – The Hub domain to resolve (custom domain or {brand}.mysmartlinks.app)
44
+ * @param domain – The Hub domain to resolve (custom domain or {brand}.smartlinks.host)
45
45
  * @returns Promise resolving to the CollectionResponse mapped to the domain
46
46
  * @throws ErrorResponse (404) if no collection is mapped to the domain
47
47
  */
@@ -57,7 +57,7 @@ export declare namespace collection {
57
57
  /**
58
58
  * Claim or rename the Hub subdomain for a collection (admin only).
59
59
  *
60
- * Maps `{hubName}.mysmartlinks.app` to the collection. If the collection
60
+ * Maps `{hubName}.smartlinks.host` to the collection. If the collection
61
61
  * already had a different hub name, the previous subdomain is released
62
62
  * automatically.
63
63
  *
@@ -43,7 +43,7 @@ export var collection;
43
43
  * The server derives the requesting domain from the request headers
44
44
  * (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is
45
45
  * passed — this is the call a Hub frontend makes on load to find out which
46
- * collection it is serving, whether it's reached via `{brand}.mysmartlinks.app`
46
+ * collection it is serving, whether it's reached via `{brand}.smartlinks.host`
47
47
  * or a bring-your-own custom domain (e.g. `hub.acme.com`).
48
48
  *
49
49
  * @returns Promise resolving to the CollectionResponse mapped to the domain
@@ -59,9 +59,9 @@ export var collection;
59
59
  *
60
60
  * Unlike {@link getByHub}, the domain is passed explicitly rather than derived
61
61
  * from request headers — use this for raw/cross-origin calls where the Hub
62
- * frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
62
+ * frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
63
63
  *
64
- * @param domain – The Hub domain to resolve (custom domain or {brand}.mysmartlinks.app)
64
+ * @param domain – The Hub domain to resolve (custom domain or {brand}.smartlinks.host)
65
65
  * @returns Promise resolving to the CollectionResponse mapped to the domain
66
66
  * @throws ErrorResponse (404) if no collection is mapped to the domain
67
67
  */
@@ -86,7 +86,7 @@ export var collection;
86
86
  /**
87
87
  * Claim or rename the Hub subdomain for a collection (admin only).
88
88
  *
89
- * Maps `{hubName}.mysmartlinks.app` to the collection. If the collection
89
+ * Maps `{hubName}.smartlinks.host` to the collection. If the collection
90
90
  * already had a different hub name, the previous subdomain is released
91
91
  * automatically.
92
92
  *
@@ -11,15 +11,55 @@ export interface FunctionListResponse {
11
11
  /**
12
12
  * Options for a function call.
13
13
  * - `appId` scopes resolution to one app (recommended; falls back to the SDK app context).
14
- * - `channel` selects which release to run when addressing an app that is NOT enabled on the
15
- * collection (enablement isn't required — the app is resolved directly by id). Defaults to
16
- * `stable` server-side, so pass `channel: 'dev'` to test a dev build before installing it.
14
+ * - `channel` runs a specific release ('dev' | 'alpha' | 'beta' | 'stable') instead of the one the
15
+ * collection has installed — normally left unset (see RELEASE CHANNEL above); `null` forces the
16
+ * installed release even when the app/host set a channel. The app must be installed on the
17
+ * collection, restricted to it (e.g. the developer's sandbox), or a platform-approved public app.
17
18
  */
18
19
  export interface FunctionCallOptions {
19
20
  appId?: string;
21
+ channel?: string | null;
22
+ }
23
+ /**
24
+ * The release channel a call targets, or undefined for "the collection's installed release".
25
+ * A host context `appChannel` can be scoped to ONE app with `appChannelApp` — needed where several
26
+ * apps share a page (Forge's portal preview of a dev component), so only the app under development
27
+ * calls its dev build.
28
+ */
29
+ export declare function resolveFunctionChannel(opts?: FunctionCallOptions, appId?: string): string | undefined;
30
+ /** The API path a function call goes to (exported for hosts/tests that need the exact URL). */
31
+ export declare function functionPath(surface: 'public' | 'admin', collectionId: string, name: string, opts?: FunctionCallOptions): string;
32
+ /** Options for {@link functions.siteUrl}. */
33
+ export interface FunctionSiteUrlOptions {
34
+ /** Release channel ('dev' | 'alpha' | 'beta' | 'stable'); omit for the collection's installed release. */
20
35
  channel?: string;
36
+ /** Sub-path after the function name, e.g. "/orders/123" (the function must declare trigger.path). */
37
+ path?: string;
38
+ /** Query parameters to append. */
39
+ query?: Record<string, string>;
40
+ /** Use this host instead of the collection's siteHost (e.g. its connected custom domain). */
41
+ host?: string;
21
42
  }
22
43
  export declare namespace functions {
44
+ /**
45
+ * The PUBLIC address of an app function on the collection's own site — what you give a third party
46
+ * as a webhook URL, or call from the collection's public pages:
47
+ * `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`.
48
+ * Every HTTP method the function declares works there, with the raw body for signature checks.
49
+ * Pass the collection (or its siteHost). This address is for public/integration calls; signed-in
50
+ * calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session
51
+ * never goes to a tenant hostname.
52
+ *
53
+ * @example
54
+ * const col = await SL.collection.get(collectionId)
55
+ * const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' })
56
+ * // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
57
+ */
58
+ function siteUrl(collection: {
59
+ siteHost?: string | null;
60
+ } | string, name: string, opts?: FunctionSiteUrlOptions & {
61
+ appId?: string;
62
+ }): string;
23
63
  /**
24
64
  * Call a PUBLIC app server function inline (surface `'public'`).
25
65
  * App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`.
@@ -13,19 +13,85 @@
13
13
  // available, the call falls back to the DEPRECATED flat path `/collection/:c/functions/:name`,
14
14
  // which the server resolves by bare name and REJECTS with 409 AMBIGUOUS_FUNCTION when more than one
15
15
  // installed app defines that name. Always prefer an appId.
16
- import { post, request, getAppContext } from "../http.js";
17
- function fnPath(surface, collectionId, name, opts = {}) {
16
+ //
17
+ // RELEASE CHANNEL. The channel is part of the URL — /collection/:c/app/:appId/<channel>/functions/:name
18
+ // — never a query param (the function owns its query string, and a configured URL such as a webhook
19
+ // can only ever hit the channel it names). With NO channel the server runs the release the collection
20
+ // has installed, which is what production wants. A channel comes from, in order: `opts.channel`, the
21
+ // app's channel (initializeApi({ appChannel }) / setAppChannel), or the host's `appChannel` context
22
+ // param (Forge's preview of a Test build passes appChannel=dev). Pass `channel: null` to force the
23
+ // installed release regardless.
24
+ import { post, request, getAppContext, getAppChannel } from "../http.js";
25
+ import { readContext } from "../context.js";
26
+ const CHANNELS = ['dev', 'alpha', 'beta', 'stable'];
27
+ /**
28
+ * The release channel a call targets, or undefined for "the collection's installed release".
29
+ * A host context `appChannel` can be scoped to ONE app with `appChannelApp` — needed where several
30
+ * apps share a page (Forge's portal preview of a dev component), so only the app under development
31
+ * calls its dev build.
32
+ */
33
+ export function resolveFunctionChannel(opts = {}, appId) {
34
+ var _a, _b;
35
+ if (opts.channel === null)
36
+ return undefined;
37
+ let raw = (_a = opts.channel) !== null && _a !== void 0 ? _a : getAppChannel();
38
+ if (raw == null) {
39
+ const ctx = readContext();
40
+ const scopedTo = ctx.appChannelApp;
41
+ if (ctx.appChannel && (!scopedTo || scopedTo === ((_b = appId !== null && appId !== void 0 ? appId : opts.appId) !== null && _b !== void 0 ? _b : getAppContext())))
42
+ raw = ctx.appChannel;
43
+ }
44
+ if (!raw)
45
+ return undefined;
46
+ const ch = String(raw).trim().toLowerCase();
47
+ if (!CHANNELS.includes(ch))
48
+ throw new Error(`Unknown release channel "${raw}" (expected ${CHANNELS.join(' | ')})`);
49
+ return ch;
50
+ }
51
+ function appBase(surface, collectionId, opts) {
18
52
  var _a;
19
53
  const c = encodeURIComponent(collectionId);
20
- const n = encodeURIComponent(name);
21
54
  const app = (_a = opts.appId) !== null && _a !== void 0 ? _a : getAppContext();
22
- const q = opts.channel ? `?channel=${encodeURIComponent(opts.channel)}` : '';
23
- return app
24
- ? `/${surface}/collection/${c}/app/${encodeURIComponent(app)}/functions/${n}${q}`
25
- : `/${surface}/collection/${c}/functions/${n}${q}`; // deprecated flat alias
55
+ if (!app)
56
+ return `/${surface}/collection/${c}`; // deprecated flat alias — resolves installed apps only
57
+ const ch = resolveFunctionChannel(opts, app);
58
+ return `/${surface}/collection/${c}/app/${encodeURIComponent(app)}${ch ? `/${ch}` : ''}`;
26
59
  }
60
+ /** The API path a function call goes to (exported for hosts/tests that need the exact URL). */
61
+ export function functionPath(surface, collectionId, name, opts = {}) {
62
+ return `${appBase(surface, collectionId, opts)}/functions/${encodeURIComponent(name)}`;
63
+ }
64
+ const fnPath = functionPath;
27
65
  export var functions;
28
66
  (function (functions) {
67
+ /**
68
+ * The PUBLIC address of an app function on the collection's own site — what you give a third party
69
+ * as a webhook URL, or call from the collection's public pages:
70
+ * `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`.
71
+ * Every HTTP method the function declares works there, with the raw body for signature checks.
72
+ * Pass the collection (or its siteHost). This address is for public/integration calls; signed-in
73
+ * calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session
74
+ * never goes to a tenant hostname.
75
+ *
76
+ * @example
77
+ * const col = await SL.collection.get(collectionId)
78
+ * const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' })
79
+ * // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
80
+ */
81
+ function siteUrl(collection, name, opts = {}) {
82
+ var _a, _b;
83
+ const host = opts.host || (typeof collection === 'string' ? collection : collection && collection.siteHost);
84
+ if (!host)
85
+ throw new Error('functions.siteUrl: the collection has no siteHost (fetch it with SL.collection.get)');
86
+ const app = (_a = opts.appId) !== null && _a !== void 0 ? _a : getAppContext();
87
+ if (!app)
88
+ throw new Error('functions.siteUrl: appId required (pass it, or initializeApi({ appId }))');
89
+ const ch = resolveFunctionChannel({ channel: (_b = opts.channel) !== null && _b !== void 0 ? _b : null }, app);
90
+ const sub = opts.path ? '/' + String(opts.path).replace(/^\/+/, '') : '';
91
+ const qs = opts.query && Object.keys(opts.query).length ? '?' + new URLSearchParams(opts.query).toString() : '';
92
+ return `https://${String(host).replace(/^https?:\/\//, '').replace(/\/+$/, '')}/_fn/${encodeURIComponent(app)}${ch ? `/${ch}` : ''}/${encodeURIComponent(name)}${sub}${qs}`;
93
+ }
94
+ functions.siteUrl = siteUrl;
29
95
  /**
30
96
  * Call a PUBLIC app server function inline (surface `'public'`).
31
97
  * App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`.
@@ -53,14 +119,7 @@ export var functions;
53
119
  * appId is given (or set as the SDK app context): `GET /public/collection/:c[/app/:appId]/functions`.
54
120
  */
55
121
  async function list(collectionId, opts = {}) {
56
- var _a;
57
- const c = encodeURIComponent(collectionId);
58
- const app = (_a = opts.appId) !== null && _a !== void 0 ? _a : getAppContext();
59
- const q = opts.channel ? `?channel=${encodeURIComponent(opts.channel)}` : '';
60
- const path = app
61
- ? `/public/collection/${c}/app/${encodeURIComponent(app)}/functions${q}`
62
- : `/public/collection/${c}/functions${q}`;
63
- return request(path);
122
+ return request(`${appBase('public', collectionId, opts)}/functions`);
64
123
  }
65
124
  functions.list = list;
66
125
  })(functions || (functions = {}));
package/dist/context.d.ts CHANGED
@@ -3,6 +3,13 @@ export interface SmartLinksContext {
3
3
  appId?: string;
4
4
  productId?: string;
5
5
  proofId?: string;
6
+ /**
7
+ * The release channel the host is running this build as (e.g. 'dev' in Forge's preview of a Test
8
+ * build). SL.functions uses it to call that channel's server functions. Absent in production.
9
+ */
10
+ appChannel?: string;
11
+ /** Limits `appChannel` to one app (where several apps share a page, e.g. a portal preview). */
12
+ appChannelApp?: string;
6
13
  /** Any other declared view params (e.g. pageId, voteId, orientation, tvMode). */
7
14
  [key: string]: string | undefined;
8
15
  }
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.34 | Generated: 2026-10-02T16:06:32.154Z
3
+ Version: 2.0.36 | Generated: 2026-10-03T15:04:37.396Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -172,17 +172,23 @@ The current app context (appId), if the SDK was initialized with one.
172
172
  **setAppContext**(id: string | undefined) → `void`
173
173
  Set (or clear) the current app context — the appId used to scope SL.functions calls.
174
174
 
175
- **initializeApi**(options: {
176
- baseURL: string
177
- apiKey?: string
178
- bearerToken?: string
179
- proxyMode?: boolean
180
- ngrokSkipBrowserWarning?: boolean
181
- extraHeaders?: Record<string, string>
182
- /**
183
- * Declares the host platform. Set to `'native'` on native/Capacitor hosts to opt into
184
- * AuthKit refresh tokens — the SDK then sends `X-Client-Platform: native` on every
185
- * request, so login endpoints return `refreshToken`/`refreshTokenExpiresAt` and a
175
+ **getAppChannel**() → `string | undefined`
176
+ The release channel explicitly set for this app build (initializeApi({ appChannel }) / setAppChannel).
177
+
178
+ **setAppChannel**(channel: string | undefined) → `void`
179
+ Set (or clear) the release channel SL.functions calls target.
180
+
181
+ **initializeApi**(options: {
182
+ baseURL: string
183
+ apiKey?: string
184
+ bearerToken?: string
185
+ proxyMode?: boolean
186
+ ngrokSkipBrowserWarning?: boolean
187
+ extraHeaders?: Record<string, string>
188
+ /**
189
+ * Declares the host platform. Set to `'native'` on native/Capacitor hosts to opt into
190
+ * AuthKit refresh tokens — the SDK then sends `X-Client-Platform: native` on every
191
+ * request, so login endpoints return `refreshToken`/`refreshTokenExpiresAt` and a
186
192
  * short-lived access token. Omit (or `'web'`) → `void`
187
193
  Call this once (e.g. at app startup) to configure baseURL/auth.
188
194
 
@@ -213,56 +219,56 @@ Returns true if initializeApi() has been called at least once. Useful for guards
213
219
  **hasAuthCredentials**() → `boolean`
214
220
  Returns true if the SDK currently has any auth credential set (bearer token or API key). Use this as a cheap pre-flight check before calling endpoints that require authentication, to avoid issuing a network request that you already know will return a 401. ```ts if (hasAuthCredentials()) { const account = await auth.getAccount() } ```
215
221
 
216
- **configureSdkCache**(options: {
217
- enabled?: boolean
218
- ttlMs?: number
219
- maxEntries?: number
220
- persistence?: 'none' | 'indexeddb'
221
- persistenceTtlMs?: number
222
- serveStaleOnOffline?: boolean
223
- clearOnPageLoad?: boolean
222
+ **configureSdkCache**(options: {
223
+ enabled?: boolean
224
+ ttlMs?: number
225
+ maxEntries?: number
226
+ persistence?: 'none' | 'indexeddb'
227
+ persistenceTtlMs?: number
228
+ serveStaleOnOffline?: boolean
229
+ clearOnPageLoad?: boolean
224
230
  }) → `void`
225
231
  Configure the SDK's built-in in-memory GET cache. The cache is transparent — it sits inside the HTTP layer and requires no changes to your existing API calls. All GET requests benefit automatically. Per-resource rules (collections/products → 1 h, proofs → 30 s, etc.) override this value. in-memory only (`'none'`, default). Ignored in Node.js. fallback, from the original fetch time (default: 7 days). `SmartlinksOfflineError` with stale data instead of propagating the network error. caches on page load/refresh. IndexedDB persists for offline. ```ts // Enable IndexedDB persistence for offline support configureSdkCache({ persistence: 'indexeddb' }) // Disable cache entirely in test environments configureSdkCache({ enabled: false }) // Keep caches across page refreshes (not recommended for production) configureSdkCache({ clearOnPageLoad: false }) ```
226
232
 
227
233
  **invalidateCache**(urlPattern?: string, options?: InvalidateCacheOptions) → `void`
228
234
  Manually invalidate entries in the SDK's GET cache. Note: the GET cache is **in-memory, per page load** (with an optional L2 IndexedDB layer when persistence is enabled) — it does not persist across reloads unless you opt into persistence, so it rarely needs disabling "for correctness". *contains* this string is removed). With `{ exact: true }`, matches the path precisely. Omit to wipe the entire cache. ```ts invalidateCache() // clear everything invalidateCache('/collection/abc123') // that collection AND everything under it invalidateCache('/collection/abc123', { exact: true }) // ONLY that collection entry invalidateCache('/products/') // all canonical plural product responses ```
229
235
 
230
- **proxyUploadFormData**(path: string,
231
- formData: FormData,
236
+ **proxyUploadFormData**(path: string,
237
+ formData: FormData,
232
238
  onProgress?: (percent: number) → `void`
233
239
  Upload a FormData payload via proxy with progress events using chunked postMessage. Parent is expected to implement the counterpart protocol.
234
240
 
235
241
  **request**(path: string) → `Promise<T>`
236
242
  Internal helper that performs a GET request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. Cache pipeline (when caching is not skipped): L1 hit → return from memory (no I/O) L2 hit → return from IndexedDB, promote to L1 (no network) Miss → fetch from network, store in L1 + L2 Offline → serve stale L2 entry via SmartlinksOfflineError (if persistence enabled) Concurrent identical GETs share one in-flight promise (deduplication). Node-safe: IndexedDB calls are no-ops when IDB is unavailable.
237
243
 
238
- **post**(path: string,
239
- body: any,
244
+ **post**(path: string,
245
+ body: any,
240
246
  extraHeaders?: Record<string, string>) → `Promise<T>`
241
247
  Internal helper that performs a POST request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. If body is FormData, Content-Type is not set. Returns the parsed JSON as T, or throws an Error.
242
248
 
243
- **put**(path: string,
244
- body: any,
249
+ **put**(path: string,
250
+ body: any,
245
251
  extraHeaders?: Record<string, string>) → `Promise<T>`
246
252
  Internal helper that performs a PUT request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. If body is FormData, Content-Type is not set. Returns the parsed JSON as T, or throws an Error.
247
253
 
248
- **patch**(path: string,
249
- body: any,
254
+ **patch**(path: string,
255
+ body: any,
250
256
  extraHeaders?: Record<string, string>) → `Promise<T>`
251
257
  Internal helper that performs a PATCH request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. If body is FormData, Content-Type is not set. Returns the parsed JSON as T, or throws an Error.
252
258
 
253
- **requestWithOptions**(path: string,
259
+ **requestWithOptions**(path: string,
254
260
  options: RequestInit) → `Promise<T>`
255
261
  Internal helper that performs a request to `${baseURL}${path}` with custom options, injecting headers for apiKey or bearerToken if present. Returns the parsed JSON as T, or throws an Error.
256
262
 
257
- **requestStream**(path: string,
258
- options?: {
259
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
260
- body?: any
261
- headers?: Record<string, string>
263
+ **requestStream**(path: string,
264
+ options?: {
265
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
266
+ body?: any
267
+ headers?: Record<string, string>
262
268
  }) → `Promise<AsyncIterable<T>>`
263
269
  Internal helper that performs a streaming request using the shared auth and proxy transport. The response is expected to be `text/event-stream` with JSON payloads in `data:` frames.
264
270
 
265
- **del**(path: string,
271
+ **del**(path: string,
266
272
  extraHeaders?: Record<string, string>) → `Promise<T>`
267
273
  Internal helper that performs a DELETE request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. Returns the parsed JSON as T, or throws an Error.
268
274
 
@@ -1528,6 +1534,45 @@ interface PdfInspectGraphicsArgs {
1528
1534
  }
1529
1535
  ```
1530
1536
 
1537
+ **PdfBox** (interface)
1538
+ ```typescript
1539
+ interface PdfBox {
1540
+ x: number; y: number; w: number; h: number
1541
+ }
1542
+ ```
1543
+
1544
+ **PdfEditArgs** (interface)
1545
+ ```typescript
1546
+ interface PdfEditArgs {
1547
+ url: string
1548
+ replaceText?: { id?: string; page?: number; find: string; replace: string; bold?: boolean; align?: 'left' | 'center' | 'right' }[]
1549
+ addText?: { id?: string; page: number; box: PdfBox; text: string; size?: number; bold?: boolean; colour?: PdfColour; align?: 'left' | 'center' | 'right' }[]
1550
+ addImages?: { id?: string; page: number; box: PdfBox; imageUrl: string; colorSpace?: 'rgb' | 'cmyk' }[]
1551
+ preserveColour?: boolean
1552
+ }
1553
+ ```
1554
+
1555
+ **PdfPreflightArgs** (interface)
1556
+ ```typescript
1557
+ interface PdfPreflightArgs {
1558
+ url: string; profile?: PdfXProfile; bleed?: { mm: number }; minDpi?: number
1559
+ }
1560
+ ```
1561
+
1562
+ **PdfPrintReadyArgs** (interface)
1563
+ ```typescript
1564
+ interface PdfPrintReadyArgs {
1565
+ url: string
1566
+ profile?: PdfXProfile
1567
+ outputIntent?: string
1568
+ convertToCmyk?: boolean
1569
+ embedFonts?: boolean
1570
+ bleed?: { mm: number }
1571
+ flattenTransparency?: boolean
1572
+ minDpi?: number
1573
+ }
1574
+ ```
1575
+
1531
1576
  **HttpRequestArgs** (interface)
1532
1577
  ```typescript
1533
1578
  interface HttpRequestArgs {
@@ -1566,6 +1611,9 @@ interface AiToolArgsMap {
1566
1611
  'pdf.extract': PdfExtractArgs
1567
1612
  'pdf.decodeBarcodes': PdfDecodeBarcodesArgs
1568
1613
  'pdf.inspectGraphics': PdfInspectGraphicsArgs
1614
+ 'pdf.edit': PdfEditArgs
1615
+ 'pdf.preflight': PdfPreflightArgs
1616
+ 'pdf.printReady': PdfPrintReadyArgs
1569
1617
  'http.request': HttpRequestArgs
1570
1618
  'translate': TranslateArgs
1571
1619
  }
@@ -1741,6 +1789,68 @@ interface PdfInspectGraphicsResult {
1741
1789
  }
1742
1790
  ```
1743
1791
 
1792
+ **PdfEditItemResult** (interface)
1793
+ ```typescript
1794
+ interface PdfEditItemResult {
1795
+ id: string
1796
+ status: 'done' | 'failed'
1797
+ note?: string
1798
+ verified?: boolean
1799
+ usedFallbackFont?: boolean
1800
+ }
1801
+ ```
1802
+
1803
+ **PdfEditResult** (interface)
1804
+ ```typescript
1805
+ interface PdfEditResult {
1806
+ url: string; results: PdfEditItemResult[]; warnings: string[]
1807
+ }
1808
+ ```
1809
+
1810
+ **PdfPreflightFinding** (interface)
1811
+ ```typescript
1812
+ interface PdfPreflightFinding {
1813
+ severity: 'error' | 'warning'; code: string; page?: number; message: string
1814
+ }
1815
+ ```
1816
+
1817
+ **PdfPreflightResult** (interface)
1818
+ ```typescript
1819
+ interface PdfPreflightResult {
1820
+ url: string
1821
+ compliant: boolean
1822
+ profile: PdfXProfile | null
1823
+ version: string | null
1824
+ findings: PdfPreflightFinding[]
1825
+ stats: {
1826
+ pageCount: number
1827
+ fonts: { name: string; embedded: boolean; subset: boolean; type: string; pages: number[] }[]
1828
+ spotColours: string[]
1829
+ images: { page: number; dpi: number; width: number; height: number; space: string; placedMm: { w: number; h: number } }[]
1830
+ transparency: boolean
1831
+ outputIntent: { identifier: string | null; hasProfile: boolean } | null
1832
+ [key: string]: any
1833
+ }
1834
+ }
1835
+ ```
1836
+
1837
+ **PdfPrintReadyResult** (interface)
1838
+ ```typescript
1839
+ interface PdfPrintReadyResult {
1840
+ url: string
1841
+ report: {
1842
+ compliant: boolean
1843
+ profile: PdfXProfile
1844
+ outputIntent: string
1845
+ findings: PdfPreflightFinding[]
1846
+ changes: { code: string; message: string }[]
1847
+ before: { compliant: boolean; errors: number; findings: PdfPreflightFinding[] }
1848
+ stats: Record<string, any>
1849
+ note: string
1850
+ }
1851
+ }
1852
+ ```
1853
+
1744
1854
  **ResponsesAgentTrace** (interface)
1745
1855
  ```typescript
1746
1856
  interface ResponsesAgentTrace {
@@ -1778,6 +1888,10 @@ interface AgentResponseCompletedEvent {
1778
1888
 
1779
1889
  **AiToolName** = ``
1780
1890
 
1891
+ **PdfColour** = `string | [number, number, number, number]`
1892
+
1893
+ **PdfXProfile** = `'pdfx-1a' | 'pdfx-4'`
1894
+
1781
1895
  **AgentStreamEvent** = ``
1782
1896
 
1783
1897
  ### analytics
@@ -4725,6 +4839,7 @@ interface Collection {
4725
4839
  redirectUrl?: string // Whether the collection has a custom domain
4726
4840
  hubName?: string
4727
4841
  hubCustomDomain?: string
4842
+ siteHost?: string | null
4728
4843
  shortId: string, // The shortId of this collection
4729
4844
  dark?: boolean // if dark mode is enabled for this collection
4730
4845
  primaryColor?: string
@@ -9476,7 +9591,17 @@ interface FunctionListResponse {
9476
9591
  **FunctionCallOptions** (interface)
9477
9592
  ```typescript
9478
9593
  interface FunctionCallOptions {
9479
- appId?: string; channel?: string
9594
+ appId?: string; channel?: string | null
9595
+ }
9596
+ ```
9597
+
9598
+ **FunctionSiteUrlOptions** (interface)
9599
+ ```typescript
9600
+ interface FunctionSiteUrlOptions {
9601
+ channel?: string
9602
+ path?: string
9603
+ query?: Record<string, string>
9604
+ host?: string
9480
9605
  }
9481
9606
  ```
9482
9607
 
@@ -10608,16 +10733,16 @@ Retrieves all Collections.
10608
10733
  Retrieve a collection by its shortId (public endpoint).
10609
10734
 
10610
10735
  **getByHub**() → `Promise<CollectionResponse>`
10611
- Resolve the collection for the current Hub domain (public endpoint). The server derives the requesting domain from the request headers (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is passed — this is the call a Hub frontend makes on load to find out which collection it is serving, whether it's reached via `{brand}.mysmartlinks.app` or a bring-your-own custom domain (e.g. `hub.acme.com`).
10736
+ Resolve the collection for the current Hub domain (public endpoint). The server derives the requesting domain from the request headers (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is passed — this is the call a Hub frontend makes on load to find out which collection it is serving, whether it's reached via `{brand}.smartlinks.host` or a bring-your-own custom domain (e.g. `hub.acme.com`).
10612
10737
 
10613
10738
  **getByDomain**(domain: string) → `Promise<CollectionResponse>`
10614
- Resolve the collection for an explicit Hub domain (public endpoint). Unlike {@link getByHub}, the domain is passed explicitly rather than derived from request headers — use this for raw/cross-origin calls where the Hub frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
10739
+ Resolve the collection for an explicit Hub domain (public endpoint). Unlike {@link getByHub}, the domain is passed explicitly rather than derived from request headers — use this for raw/cross-origin calls where the Hub frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
10615
10740
 
10616
10741
  **checkHubAvailability**(collectionId: string, name: string) → `Promise<HubAvailabilityResponse>`
10617
10742
  Check whether a Hub subdomain name is available to claim (admin only).
10618
10743
 
10619
10744
  **claimHub**(collectionId: string, hubName: string) → `Promise<CollectionResponse>`
10620
- Claim or rename the Hub subdomain for a collection (admin only). Maps `{hubName}.mysmartlinks.app` to the collection. If the collection already had a different hub name, the previous subdomain is released automatically.
10745
+ Claim or rename the Hub subdomain for a collection (admin only). Maps `{hubName}.smartlinks.host` to the collection. If the collection already had a different hub name, the previous subdomain is released automatically.
10621
10746
 
10622
10747
  **registerDomain**(collectionId: string, domain: string, target: DomainTarget = "smartlinks") → `Promise<any>`
10623
10748
  Register a custom domain for a collection and provision its managed certificate (admin only). `"smartlinks"` (the id.smartlinks.app load balancer). Pass `"hub"` to register a bring-your-own Hub domain.
@@ -10988,6 +11113,17 @@ Delete a form for a collection (admin only).
10988
11113
 
10989
11114
  ### functions
10990
11115
 
11116
+ **resolveFunctionChannel**(opts: FunctionCallOptions = {}, appId?: string) → `string | undefined`
11117
+ The release channel a call targets, or undefined for "the collection's installed release". A host context `appChannel` can be scoped to ONE app with `appChannelApp` — needed where several apps share a page (Forge's portal preview of a dev component), so only the app under development calls its dev build.
11118
+
11119
+ **functionPath**(surface: 'public' | 'admin', collectionId: string, name: string, opts: FunctionCallOptions = {}) → `string`
11120
+ The API path a function call goes to (exported for hosts/tests that need the exact URL).
11121
+
11122
+ **siteUrl**(collection: { siteHost?: string | null } | string,
11123
+ name: string,
11124
+ opts: FunctionSiteUrlOptions & { appId?: string } = {}) → `string`
11125
+ The PUBLIC address of an app function on the collection's own site — what you give a third party as a webhook URL, or call from the collection's public pages: `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`. Every HTTP method the function declares works there, with the raw body for signature checks. Pass the collection (or its siteHost). This address is for public/integration calls; signed-in calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session never goes to a tenant hostname. const col = await SL.collection.get(collectionId) const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' }) // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
11126
+
10991
11127
  **call**(collectionId: string,
10992
11128
  name: string,
10993
11129
  body: Record<string, any> = {},