@proveanything/smartlinks 2.0.38 → 2.0.39

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.
@@ -62,7 +62,7 @@ export declare namespace functions {
62
62
  }): string;
63
63
  /**
64
64
  * Call a PUBLIC app server function inline (surface `'public'`).
65
- * App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`.
65
+ * App-scoped: `POST /fn/public/collection/:c/app/:appId/functions/:name`.
66
66
  *
67
67
  * @example
68
68
  * // App calling its own function (appId from initializeApi({ appId })):
@@ -73,7 +73,7 @@ export declare namespace functions {
73
73
  function call<T = FunctionCallResult>(collectionId: string, name: string, body?: Record<string, any>, opts?: FunctionCallOptions): Promise<T>;
74
74
  /**
75
75
  * Call an ADMIN app server function (surface `'admin'`; requires an admin session).
76
- * App-scoped: `POST /admin/collection/:c/app/:appId/functions/:name`.
76
+ * App-scoped: `POST /fn/admin/collection/:c/app/:appId/functions/:name`.
77
77
  */
78
78
  function callAdmin<T = FunctionCallResult>(collectionId: string, name: string, body?: Record<string, any>, opts?: FunctionCallOptions): Promise<T>;
79
79
  /**
@@ -14,6 +14,10 @@
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
16
  //
17
+ // PREFIX. App-scoped calls go to /fn/{public|admin}/collection/…/functions/… (under the API base,
18
+ // so /api/v1/fn/…): one prefix for all function traffic, which the platform can route to its own
19
+ // service. The same call without /fn still works (older SDKs). The flat alias has no /fn form.
20
+ //
17
21
  // RELEASE CHANNEL. The channel is part of the URL — /collection/:c/app/:appId/<channel>/functions/:name
18
22
  // — never a query param (the function owns its query string, and a configured URL such as a webhook
19
23
  // can only ever hit the channel it names). With NO channel the server runs the release the collection
@@ -55,7 +59,7 @@ function appBase(surface, collectionId, opts) {
55
59
  if (!app)
56
60
  return `/${surface}/collection/${c}`; // deprecated flat alias — resolves installed apps only
57
61
  const ch = resolveFunctionChannel(opts, app);
58
- return `/${surface}/collection/${c}/app/${encodeURIComponent(app)}${ch ? `/${ch}` : ''}`;
62
+ return `/fn/${surface}/collection/${c}/app/${encodeURIComponent(app)}${ch ? `/${ch}` : ''}`;
59
63
  }
60
64
  /** The API path a function call goes to (exported for hosts/tests that need the exact URL). */
61
65
  export function functionPath(surface, collectionId, name, opts = {}) {
@@ -94,7 +98,7 @@ export var functions;
94
98
  functions.siteUrl = siteUrl;
95
99
  /**
96
100
  * Call a PUBLIC app server function inline (surface `'public'`).
97
- * App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`.
101
+ * App-scoped: `POST /fn/public/collection/:c/app/:appId/functions/:name`.
98
102
  *
99
103
  * @example
100
104
  * // App calling its own function (appId from initializeApi({ appId })):
@@ -108,7 +112,7 @@ export var functions;
108
112
  functions.call = call;
109
113
  /**
110
114
  * Call an ADMIN app server function (surface `'admin'`; requires an admin session).
111
- * App-scoped: `POST /admin/collection/:c/app/:appId/functions/:name`.
115
+ * App-scoped: `POST /fn/admin/collection/:c/app/:appId/functions/:name`.
112
116
  */
113
117
  async function callAdmin(collectionId, name, body = {}, opts = {}) {
114
118
  return post(fnPath('admin', collectionId, name, opts), body);
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.38 | Generated: 2026-10-05T12:10:36.598Z
3
+ Version: 2.0.39 | Generated: 2026-10-05T14:14:46.558Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -26,6 +26,12 @@ For detailed guides on specific features:
26
26
  - **[iframe Responder](iframe-responder.md)** - iframe integration and cross-origin communication (incl. hand-rolled streaming protocol)
27
27
  - **[Utilities](utils.md)** - Helper functions for building portal paths, URLs, and common tasks
28
28
  - **[UI Utils](ui-utils.md)** - Reusable, themeable admin UI React component library for microapps
29
+ - **[Headless Providers](headless-providers.md)** - Declaring an app's content for other sites: the manifest `data` + `headless` blocks (types, storage, public fields, examples, read recipes, categories), `SL.headless.validate`, `smartlinks-headless`, and the procedure for adding a headless mode
30
+ - **[Websites: SEO + GEO](site-seo.md)** - For apps served as websites: platform-generated robots/sitemap/llms.txt, canonical addresses, `SL.seo.head` / `SL.seo.jsonLd` / `SL.seo.schema.*`, `SL.site.ready()`, routes in `sitemap-paths.txt`
31
+ - **[Agent Tools](agent-tools.md)** - Exposing app functions as AI agent tools
32
+ - **[Host Dependency Contract](host-dependency-contract.md)** - The shared dependencies (React, the SDK…) the host provides, and what an app must externalise
33
+ - **[Theme Tokens](theme-tokens.md)** - The `--sl-*` semantic token contract hosts set and apps bind to (`theme.css`)
34
+ - **[CSS Baseline](css-baseline.md)** - The frozen `sl-*` structural helper classes hosts guarantee
29
35
  - **[Caching](caching.md)** - Multi-tier caching strategy (in-memory, SessionStorage, IndexedDB) used by the SDK
30
36
  - **[Native Facade](native-facade.md)** - Contract layer for accessing device capabilities (share, NFC, haptics) across host shells
31
37
  - **[i18n](i18n.md)** - Internationalization and localization
@@ -11192,22 +11198,22 @@ The release channel a call targets, or undefined for "the collection's installed
11192
11198
  **functionPath**(surface: 'public' | 'admin', collectionId: string, name: string, opts: FunctionCallOptions = {}) → `string`
11193
11199
  The API path a function call goes to (exported for hosts/tests that need the exact URL).
11194
11200
 
11195
- **siteUrl**(collection: { siteHost?: string | null } | string,
11196
- name: string,
11201
+ **siteUrl**(collection: { siteHost?: string | null } | string,
11202
+ name: string,
11197
11203
  opts: FunctionSiteUrlOptions & { appId?: string } = {}) → `string`
11198
11204
  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
11199
11205
 
11200
- **call**(collectionId: string,
11201
- name: string,
11202
- body: Record<string, any> = {},
11206
+ **call**(collectionId: string,
11207
+ name: string,
11208
+ body: Record<string, any> = {},
11203
11209
  opts: FunctionCallOptions = {}) → `Promise<T>`
11204
- Call a PUBLIC app server function inline (surface `'public'`). App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`. // App calling its own function (appId from initializeApi({ appId })): const { value } = await SL.functions.call<{ value: number }>(collectionId, 'pressCounter') // Or address another app explicitly: await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'my-counter-app' })
11210
+ Call a PUBLIC app server function inline (surface `'public'`). App-scoped: `POST /fn/public/collection/:c/app/:appId/functions/:name`. // App calling its own function (appId from initializeApi({ appId })): const { value } = await SL.functions.call<{ value: number }>(collectionId, 'pressCounter') // Or address another app explicitly: await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'my-counter-app' })
11205
11211
 
11206
- **callAdmin**(collectionId: string,
11207
- name: string,
11208
- body: Record<string, any> = {},
11212
+ **callAdmin**(collectionId: string,
11213
+ name: string,
11214
+ body: Record<string, any> = {},
11209
11215
  opts: FunctionCallOptions = {}) → `Promise<T>`
11210
- Call an ADMIN app server function (surface `'admin'`; requires an admin session). App-scoped: `POST /admin/collection/:c/app/:appId/functions/:name`.
11216
+ Call an ADMIN app server function (surface `'admin'`; requires an admin session). App-scoped: `POST /fn/admin/collection/:c/app/:appId/functions/:name`.
11211
11217
 
11212
11218
  **list**(collectionId: string, opts: FunctionCallOptions = {}) → `Promise<FunctionListResponse>`
11213
11219
  List the public functions available for a collection (discovery). Scoped to one app when an appId is given (or set as the SDK app context): `GET /public/collection/:c[/app/:appId]/functions`.
@@ -50,6 +50,14 @@ Site builders (the Forge agent included) read these declarations to decide "inst
50
50
  | `thread` | App threads: discussions, Q&A, reviews | `SL.app.threads.list / get` |
51
51
  | `config` (+ optional `key`) | App configuration: settings, small fixed lists | `SL.appConfiguration.getConfig({ collectionId, appId })` |
52
52
 
53
+ **What the read calls return.** This is fixed by the storage kind, so a provider never needs to describe it, and site builders get it from the checker and `forge-cms describe`:
54
+
55
+ - **`record`, `case`, `thread`:** `list` returns `{ data: Item[], pagination: { total, limit, offset, hasMore } }`.
56
+ - The items are in **`response.data`**, never `response.items` or `response.records`.
57
+ - Each item's declared fields are in **`item.data`** (e.g. `record.data.question`). `id`, `status`, `productId` and the dates are top-level.
58
+ - Page with `offset` / `limit` while `pagination.hasMore`. `get` returns one item.
59
+ - **`config`:** the configuration object. Items are in `config.<key>` when the type names a `key`.
60
+
53
61
  Products, contacts and proofs are platform data. Reference them with `ref` fields (`"to": "product"`); never redeclare them.
54
62
 
55
63
  **Field types:** `string`, `text`, `richtext` (HTML), `markdown`, `number`, `boolean`, `date` (YYYY-MM-DD), `datetime`, `enum` (+ `options`), `url`, `image` and `file` (a URL or `{ url, ... }`), `ref` and `ref[]` (+ `to`: a declared type id, or `product` / `contact` / `proof`), `string[]`, `json` (avoid where a typed field fits).
@@ -8,9 +8,10 @@ get all of this from Hub.
8
8
  - **`/robots.txt`, `/sitemap.xml`, `/llms.txt`** are generated on the site's own address. Don't ship
9
9
  `robots.txt` or `sitemap.xml`: one build serves many sites, and sitemap URLs must be absolute on
10
10
  the site's host, which the build can't know.
11
- - **Declare your routes** in `public/sitemap-paths.txt`, one path per line (`/`, `/menu`,
12
- `/book`). They go into the sitemap and `llms.txt` on the right host. Pre-rendered pages
13
- (`about/index.html`) are found automatically.
11
+ - **List every page** in `public/sitemap-paths.txt`, one `<path> [Title]` per line, in menu order
12
+ (`/ Home`, `/menu Menu`, `/book Book a table`). They go into the sitemap and `llms.txt` (titled)
13
+ on the right host, and Forge's preview uses the same list as its page menu. Keep it in step with
14
+ the router. Pre-rendered pages (`about/index.html`) are found automatically.
14
15
  - **Canonical address.** A site can answer on its automatic address, a chosen name and a custom
15
16
  domain. Every page gets `Link: <https://{canonical}{path}>; rel="canonical"`, so search engines
16
17
  consolidate on one: the custom domain, else the chosen name, else the automatic address. Don't set
@@ -8,12 +8,26 @@ export interface HeadlessValidation {
8
8
  ok: boolean;
9
9
  errors: HeadlessIssue[];
10
10
  warnings: HeadlessIssue[];
11
- /** The read recipe for each type: the declared one, else the standard one for its storage. */
11
+ /**
12
+ * The read recipe for each type: the declared one, else the standard one for its storage — plus what
13
+ * the call returns and where an item's fields are. Those two always come from the storage kind (the
14
+ * platform's response shape), never from the app, so a custom recipe can't leave them out.
15
+ */
12
16
  recipes: Record<string, {
13
17
  list: string;
14
18
  get?: string;
19
+ returns: string;
20
+ item: string;
15
21
  }>;
16
22
  }
23
+ /**
24
+ * What a type's read calls return, and where one item's declared fields live — fixed by the storage
25
+ * kind (see app-objects.md "Paginated List Responses"). Sites must read items from `response.data`.
26
+ */
27
+ export declare function responseShape(type: AppDataType): {
28
+ returns: string;
29
+ item: string;
30
+ };
17
31
  /** The standard SDK read calls for a type, from how it's stored. `appId` / `collectionId` are the caller's variables. */
18
32
  export declare function standardRecipe(type: AppDataType): {
19
33
  list: string;
package/dist/headless.js CHANGED
@@ -18,13 +18,36 @@ const PLATFORM_REFS = new Set(['product', 'contact', 'proof']);
18
18
  const SEO_HELPERS = new Set(['faqPage', 'product', 'article', 'breadcrumbs', 'organization', 'localBusiness']);
19
19
  const STORAGE_KINDS = new Set(['record', 'case', 'thread', 'config']);
20
20
  const SEMVER = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
21
+ /**
22
+ * What a type's read calls return, and where one item's declared fields live — fixed by the storage
23
+ * kind (see app-objects.md "Paginated List Responses"). Sites must read items from `response.data`.
24
+ */
25
+ export function responseShape(type) {
26
+ const s = type.storage;
27
+ const first = Object.keys(type.fields || {})[0] || 'field';
28
+ const paged = (name) => `list → { data: ${name}[], pagination: { total, limit, offset, hasMore } }. The items are in response.data (not response.items / response.records); page with offset/limit while pagination.hasMore. get → one ${name}.`;
29
+ switch (s && s.kind) {
30
+ case 'record':
31
+ return { returns: paged('AppRecord'), item: `Each record's declared fields are in record.data (e.g. record.data.${first}); record.id, record.productId, record.status, record.createdAt are top-level.` };
32
+ case 'case':
33
+ return { returns: paged('AppCase'), item: `Each case's declared fields are in case.data (e.g. case.data.${first}); id, status, category and dates are top-level.` };
34
+ case 'thread':
35
+ return { returns: paged('AppThread'), item: `Each thread's declared fields are in thread.data (e.g. thread.data.${first}); replies are in thread.replies.` };
36
+ case 'config': {
37
+ const key = s.key;
38
+ return { returns: "The app's configuration object for the collection.", item: key ? `The items are in config.${key}; each item's fields are its declared fields.` : 'The declared fields are top-level properties of the config object.' };
39
+ }
40
+ default:
41
+ return { returns: '(unknown storage)', item: '' };
42
+ }
43
+ }
21
44
  /** The standard SDK read calls for a type, from how it's stored. `appId` / `collectionId` are the caller's variables. */
22
45
  export function standardRecipe(type) {
23
46
  const s = type.storage;
24
47
  switch (s && s.kind) {
25
48
  case 'record':
26
49
  return {
27
- list: `SL.app.records.list(collectionId, appId, { recordType: '${s.recordType}', limit: 50 }) // → { data: AppRecord[] }; fields in record.data`,
50
+ list: `SL.app.records.list(collectionId, appId, { recordType: '${s.recordType}', limit: 50 })`,
28
51
  get: 'SL.app.records.get(collectionId, appId, recordId)',
29
52
  };
30
53
  case 'case':
@@ -179,7 +202,8 @@ export function validate(manifest, opts = {}) {
179
202
  if (samples && samples.length)
180
203
  checkItems('real item', typeId, type, samples, { errors, warnings });
181
204
  const std = standardRecipe(type);
182
- recipes[typeId] = Object.assign({ list: (type.read && type.read.list) || std.list }, ((type.read && type.read.get) || std.get ? { get: (type.read && type.read.get) || std.get } : {}));
205
+ const get = (type.read && type.read.get) || std.get;
206
+ recipes[typeId] = Object.assign(Object.assign({ list: (type.read && type.read.list) || std.list }, (get ? { get } : {})), responseShape(type));
183
207
  }
184
208
  }
185
209
  // ---- headless
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.38 | Generated: 2026-10-05T12:10:36.598Z
3
+ Version: 2.0.39 | Generated: 2026-10-05T14:14:46.558Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -26,6 +26,12 @@ For detailed guides on specific features:
26
26
  - **[iframe Responder](iframe-responder.md)** - iframe integration and cross-origin communication (incl. hand-rolled streaming protocol)
27
27
  - **[Utilities](utils.md)** - Helper functions for building portal paths, URLs, and common tasks
28
28
  - **[UI Utils](ui-utils.md)** - Reusable, themeable admin UI React component library for microapps
29
+ - **[Headless Providers](headless-providers.md)** - Declaring an app's content for other sites: the manifest `data` + `headless` blocks (types, storage, public fields, examples, read recipes, categories), `SL.headless.validate`, `smartlinks-headless`, and the procedure for adding a headless mode
30
+ - **[Websites: SEO + GEO](site-seo.md)** - For apps served as websites: platform-generated robots/sitemap/llms.txt, canonical addresses, `SL.seo.head` / `SL.seo.jsonLd` / `SL.seo.schema.*`, `SL.site.ready()`, routes in `sitemap-paths.txt`
31
+ - **[Agent Tools](agent-tools.md)** - Exposing app functions as AI agent tools
32
+ - **[Host Dependency Contract](host-dependency-contract.md)** - The shared dependencies (React, the SDK…) the host provides, and what an app must externalise
33
+ - **[Theme Tokens](theme-tokens.md)** - The `--sl-*` semantic token contract hosts set and apps bind to (`theme.css`)
34
+ - **[CSS Baseline](css-baseline.md)** - The frozen `sl-*` structural helper classes hosts guarantee
29
35
  - **[Caching](caching.md)** - Multi-tier caching strategy (in-memory, SessionStorage, IndexedDB) used by the SDK
30
36
  - **[Native Facade](native-facade.md)** - Contract layer for accessing device capabilities (share, NFC, haptics) across host shells
31
37
  - **[i18n](i18n.md)** - Internationalization and localization
@@ -11192,22 +11198,22 @@ The release channel a call targets, or undefined for "the collection's installed
11192
11198
  **functionPath**(surface: 'public' | 'admin', collectionId: string, name: string, opts: FunctionCallOptions = {}) → `string`
11193
11199
  The API path a function call goes to (exported for hosts/tests that need the exact URL).
11194
11200
 
11195
- **siteUrl**(collection: { siteHost?: string | null } | string,
11196
- name: string,
11201
+ **siteUrl**(collection: { siteHost?: string | null } | string,
11202
+ name: string,
11197
11203
  opts: FunctionSiteUrlOptions & { appId?: string } = {}) → `string`
11198
11204
  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
11199
11205
 
11200
- **call**(collectionId: string,
11201
- name: string,
11202
- body: Record<string, any> = {},
11206
+ **call**(collectionId: string,
11207
+ name: string,
11208
+ body: Record<string, any> = {},
11203
11209
  opts: FunctionCallOptions = {}) → `Promise<T>`
11204
- Call a PUBLIC app server function inline (surface `'public'`). App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`. // App calling its own function (appId from initializeApi({ appId })): const { value } = await SL.functions.call<{ value: number }>(collectionId, 'pressCounter') // Or address another app explicitly: await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'my-counter-app' })
11210
+ Call a PUBLIC app server function inline (surface `'public'`). App-scoped: `POST /fn/public/collection/:c/app/:appId/functions/:name`. // App calling its own function (appId from initializeApi({ appId })): const { value } = await SL.functions.call<{ value: number }>(collectionId, 'pressCounter') // Or address another app explicitly: await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'my-counter-app' })
11205
11211
 
11206
- **callAdmin**(collectionId: string,
11207
- name: string,
11208
- body: Record<string, any> = {},
11212
+ **callAdmin**(collectionId: string,
11213
+ name: string,
11214
+ body: Record<string, any> = {},
11209
11215
  opts: FunctionCallOptions = {}) → `Promise<T>`
11210
- Call an ADMIN app server function (surface `'admin'`; requires an admin session). App-scoped: `POST /admin/collection/:c/app/:appId/functions/:name`.
11216
+ Call an ADMIN app server function (surface `'admin'`; requires an admin session). App-scoped: `POST /fn/admin/collection/:c/app/:appId/functions/:name`.
11211
11217
 
11212
11218
  **list**(collectionId: string, opts: FunctionCallOptions = {}) → `Promise<FunctionListResponse>`
11213
11219
  List the public functions available for a collection (discovery). Scoped to one app when an appId is given (or set as the SDK app context): `GET /public/collection/:c[/app/:appId]/functions`.
@@ -50,6 +50,14 @@ Site builders (the Forge agent included) read these declarations to decide "inst
50
50
  | `thread` | App threads: discussions, Q&A, reviews | `SL.app.threads.list / get` |
51
51
  | `config` (+ optional `key`) | App configuration: settings, small fixed lists | `SL.appConfiguration.getConfig({ collectionId, appId })` |
52
52
 
53
+ **What the read calls return.** This is fixed by the storage kind, so a provider never needs to describe it, and site builders get it from the checker and `forge-cms describe`:
54
+
55
+ - **`record`, `case`, `thread`:** `list` returns `{ data: Item[], pagination: { total, limit, offset, hasMore } }`.
56
+ - The items are in **`response.data`**, never `response.items` or `response.records`.
57
+ - Each item's declared fields are in **`item.data`** (e.g. `record.data.question`). `id`, `status`, `productId` and the dates are top-level.
58
+ - Page with `offset` / `limit` while `pagination.hasMore`. `get` returns one item.
59
+ - **`config`:** the configuration object. Items are in `config.<key>` when the type names a `key`.
60
+
53
61
  Products, contacts and proofs are platform data. Reference them with `ref` fields (`"to": "product"`); never redeclare them.
54
62
 
55
63
  **Field types:** `string`, `text`, `richtext` (HTML), `markdown`, `number`, `boolean`, `date` (YYYY-MM-DD), `datetime`, `enum` (+ `options`), `url`, `image` and `file` (a URL or `{ url, ... }`), `ref` and `ref[]` (+ `to`: a declared type id, or `product` / `contact` / `proof`), `string[]`, `json` (avoid where a typed field fits).
package/docs/site-seo.md CHANGED
@@ -8,9 +8,10 @@ get all of this from Hub.
8
8
  - **`/robots.txt`, `/sitemap.xml`, `/llms.txt`** are generated on the site's own address. Don't ship
9
9
  `robots.txt` or `sitemap.xml`: one build serves many sites, and sitemap URLs must be absolute on
10
10
  the site's host, which the build can't know.
11
- - **Declare your routes** in `public/sitemap-paths.txt`, one path per line (`/`, `/menu`,
12
- `/book`). They go into the sitemap and `llms.txt` on the right host. Pre-rendered pages
13
- (`about/index.html`) are found automatically.
11
+ - **List every page** in `public/sitemap-paths.txt`, one `<path> [Title]` per line, in menu order
12
+ (`/ Home`, `/menu Menu`, `/book Book a table`). They go into the sitemap and `llms.txt` (titled)
13
+ on the right host, and Forge's preview uses the same list as its page menu. Keep it in step with
14
+ the router. Pre-rendered pages (`about/index.html`) are found automatically.
14
15
  - **Canonical address.** A site can answer on its automatic address, a chosen name and a custom
15
16
  domain. Every page gets `Link: <https://{canonical}{path}>; rel="canonical"`, so search engines
16
17
  consolidate on one: the custom domain, else the chosen name, else the automatic address. Don't set
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "2.0.38",
3
+ "version": "2.0.39",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",