@proveanything/smartlinks 2.0.56 → 2.0.58

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.
@@ -1,68 +1,69 @@
1
- # Websites: search engines and AI crawlers (SEO + GEO)
2
-
3
- For apps served as websites (an app site at `<name>.smartlinks.host` or a custom domain). Hub sites
4
- get all of this from Hub.
5
-
6
- ## What the platform does for you
7
-
8
- - **`/robots.txt`, `/sitemap.xml`, `/llms.txt`** are generated on the site's own address. Don't ship
9
- `robots.txt` or `sitemap.xml`: one build serves many sites, and sitemap URLs must be absolute on
10
- the site's host, which the build can't know.
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.
15
- - **Canonical address.** A site can answer on its automatic address, a chosen name and a custom
16
- domain. Every page gets `Link: <https://{canonical}{path}>; rel="canonical"`, so search engines
17
- consolidate on one: the custom domain, else the chosen name, else the automatic address. Don't set
18
- your own canonical unless a page has a different canonical page.
19
- - **Previews stay out of search.** A sandbox collection's sites, and any test build (dev, alpha,
20
- beta), are served with `X-Robots-Tag: noindex` and a `robots.txt` that disallows everything,
21
- whatever the build ships.
22
-
23
- ## What you do: `SL.seo` and `SL.site`
24
-
25
- ```ts
26
- import * as SL from '@proveanything/smartlinks'
27
-
28
- // Per route, from its data — on every route change:
29
- SL.seo.head({
30
- title: `${product.name} — ${brand}`,
31
- description: product.description, // ~150 characters, says what the page is
32
- image: product.heroImage?.url, // absolute; used for link previews
33
- type: 'product', // 'website' | 'article' | 'product'
34
- })
35
-
36
- // Structured data (schema.org JSON-LD). The id names the block, so calling again replaces it:
37
- SL.seo.jsonLd('page', SL.seo.schema.product(product, { url: location.href, brand }))
38
- SL.seo.jsonLd('faq', SL.seo.schema.faqPage(faqs.map((f) => ({ question: f.q, answer: f.a }))))
39
- SL.seo.jsonLd('org', SL.seo.schema.organization(collection))
40
-
41
- // When the page's data has rendered:
42
- SL.site.ready()
43
- ```
44
-
45
- | Builder | For |
46
- |---|---|
47
- | `schema.product(product, { url, brand, offer, rating })` | product pages (`offer` only when it's really sold) |
48
- | `schema.faqPage([{ question, answer }])` | FAQs: rich results, and the answer-shaped content AI search cites |
49
- | `schema.organization(collection)` | the brand behind the site (home page) |
50
- | `schema.localBusiness(collection, { type, address, telephone, openingHours })` | a business with a place (`type: 'Florist'`, `'Restaurant'`…) |
51
- | `schema.breadcrumbs([{ name, url }])` | nested pages |
52
- | `schema.article({ headline, datePublished, … })` | posts, guides, news |
53
-
54
- `seo.head` removes tags a previous route set and the next one doesn't. Both are no-ops without a
55
- `document` (SSR, tests).
56
-
57
- `site.ready()` tells the platform's page renderer the page is complete, so it can snapshot the
58
- content for crawlers that don't run JavaScript. Without it, the renderer waits for the network to go
59
- quiet.
60
-
61
- ## Content that gets found
62
-
63
- - **Content in the page, not behind interaction.** FAQ answers in `<details>` are fine; answers
64
- fetched only when a question is clicked aren't seen.
65
- - **One `<h1>` per page**, a logical heading order, `alt` text on content images.
66
- - **Answer-shaped writing.** A short summary near the top, question-style headings where they fit.
67
- This is what AI assistants quote.
68
- - **Real links** (`<a href>`) between pages, never hash routes.
1
+ # Websites: search engines and AI crawlers (SEO + GEO)
2
+
3
+ For apps served as websites (an app site at `<name>.smartlinks.host` or a custom domain). Hub sites
4
+ get all of this from Hub.
5
+
6
+ ## What the platform does for you
7
+
8
+ - **Every page is server-rendered.** A website runs on the SmartLinks site runtime (see
9
+ [websites.md](websites.md)): the first response for any URL is the finished HTML, with its title,
10
+ description, link-preview image, structured data and real status codes (404s are 404s). Search
11
+ engines, AI crawlers and link previews read it without running JavaScript.
12
+ - **`/robots.txt`, `/sitemap.xml`, `/llms.txt`** are generated on the site's own address. Don't ship
13
+ `robots.txt` or `sitemap.xml`: one build serves many sites, and sitemap URLs must be absolute on
14
+ the site's host, which the build can't know.
15
+ - **List every page** in `public/sitemap-paths.txt`, one `<path> [Title]` per line, in menu order
16
+ (`/ Home`, `/menu Menu`, `/book Book a table`). They go into the sitemap and `llms.txt` (titled)
17
+ on the right host, and Forge's preview uses the same list as its page menu. Keep it in step with
18
+ the router.
19
+ - **Canonical address.** A site can answer on its automatic address, a chosen name and a custom
20
+ domain. Every page gets `Link: <https://{canonical}{path}>; rel="canonical"`, so search engines
21
+ consolidate on one: the custom domain, else the chosen name, else the automatic address. Don't set
22
+ your own canonical unless a page has a different canonical page.
23
+ - **Previews stay out of search.** A sandbox collection's sites, and any test build (dev, alpha,
24
+ beta), are served with `X-Robots-Tag: noindex` and a `robots.txt` that disallows everything,
25
+ whatever the build ships.
26
+
27
+ ## What you do: `SL.seo` and `SL.site`
28
+
29
+ ```ts
30
+ import * as SL from '@proveanything/smartlinks'
31
+
32
+ // Per route, from its data — on every route change:
33
+ SL.seo.head({
34
+ title: `${product.name} — ${brand}`,
35
+ description: product.description, // ~150 characters, says what the page is
36
+ image: product.heroImage?.url, // absolute; used for link previews
37
+ type: 'product', // 'website' | 'article' | 'product'
38
+ })
39
+
40
+ // Structured data (schema.org JSON-LD). The id names the block, so calling again replaces it:
41
+ SL.seo.jsonLd('page', SL.seo.schema.product(product, { url: location.href, brand }))
42
+ SL.seo.jsonLd('faq', SL.seo.schema.faqPage(faqs.map((f) => ({ question: f.q, answer: f.a }))))
43
+ SL.seo.jsonLd('org', SL.seo.schema.organization(collection))
44
+
45
+ ```
46
+
47
+ | Builder | For |
48
+ |---|---|
49
+ | `schema.product(product, { url, brand, offer, rating })` | product pages (`offer` only when it's really sold) |
50
+ | `schema.faqPage([{ question, answer }])` | FAQs: rich results, and the answer-shaped content AI search cites |
51
+ | `schema.organization(collection)` | the brand behind the site (home page) |
52
+ | `schema.localBusiness(collection, { type, address, telephone, openingHours })` | a business with a place (`type: 'Florist'`, `'Restaurant'`…) |
53
+ | `schema.breadcrumbs([{ name, url }])` | nested pages |
54
+ | `schema.article({ headline, datePublished, … })` | posts, guides, news |
55
+
56
+ `seo.head` removes tags a previous route set and the next one doesn't. Both are no-ops without a
57
+ `document`. In a server-rendered site, prefer the router's own head support (TanStack Start's route
58
+ `head()`), so the tags are in the server's HTML; use `SL.seo.schema.*` to build the JSON-LD for it.
59
+
60
+ `SL.site.ready()` is no longer needed: pages arrive finished from the server.
61
+
62
+ ## Content that gets found
63
+
64
+ - **Content in the page, not behind interaction.** FAQ answers in `<details>` are fine; answers
65
+ fetched only when a question is clicked aren't seen.
66
+ - **One `<h1>` per page**, a logical heading order, `alt` text on content images.
67
+ - **Answer-shaped writing.** A short summary near the top, question-style headings where they fit.
68
+ This is what AI assistants quote.
69
+ - **Real links** (`<a href>`) between pages, never hash routes.
@@ -0,0 +1,91 @@
1
+ # Websites: server-rendered on the SmartLinks site runtime
2
+
3
+ A website built on SmartLinks is an app whose public side is a **server-rendered site** at its own
4
+ address: an automatic `*.smartlinks.host` name, a chosen name, or the business's own domain. Every
5
+ page arrives as finished HTML, so search engines, AI crawlers and link previews see the real
6
+ content, and visitors see it before any JavaScript runs.
7
+
8
+ ## How a request is served
9
+
10
+ ```
11
+ visitor / crawler / agent
12
+ └─► SmartLinks site router (edge)
13
+ ├─ /robots.txt, /sitemap.xml, /llms.txt made by the platform (site-seo.md)
14
+ ├─ a file in your build (has an extension) served from the build, cached at the edge
15
+ └─ anything else your site's server renders it
16
+ • runs in an isolated worker, close to the SmartLinks API
17
+ • may call the SmartLinks API and the hosts you declare, nothing else
18
+ • request header x-sl-site says which site it is (SL.site.fromRequest)
19
+ ```
20
+
21
+ The router also adds the canonical or noindex header, the security headers, and
22
+ `window.__SMARTLINKS_SITE__` to every HTML page (read in the browser with `SL.site.current()` or
23
+ `SL.readContext()`).
24
+
25
+ ## The build
26
+
27
+ The website template (TanStack Start) produces this; you don't write it by hand.
28
+
29
+ ```
30
+ dist/
31
+ ├── index.html? / assets/… the client build (hashed files: cached for a year)
32
+ ├── sitemap-paths.txt your pages, "<path> [Title]" per line (site-seo.md)
33
+ ├── _server/ the server bundle: one ES module entry + its chunks
34
+ │ └── index.mjs exports default { fetch(request, env, ctx) } (WinterTC)
35
+ └── app.manifest.json
36
+ ```
37
+
38
+ Manifest:
39
+
40
+ ```jsonc
41
+ "site": {
42
+ "server": {
43
+ "main": "_server/index.mjs", // the server entry, inside the build
44
+ "compatibilityFlags": ["nodejs_compat"] // allowed: nodejs_compat, nodejs_compat_v2, nodejs_als
45
+ },
46
+ "network": ["api.example-weather.com"] // other hosts the server may call (optional)
47
+ }
48
+ ```
49
+
50
+ On publish, the server folder becomes the site's server for that channel. Promote copies the same
51
+ bytes. `_server/` is never served to visitors.
52
+
53
+ ## The server's rules
54
+
55
+ - **Which site:** `const site = SL.site.fromRequest(request)` gives `{ host, collectionId, appId,
56
+ channel, canonicalHost }`. One build serves every site it's installed on, so always take the
57
+ collection from here, never from a constant.
58
+ - **Data:** call the SmartLinks API with the SDK as you would in the browser. The server reads what
59
+ an anonymous visitor could read on that site, and only that collection. It holds no keys and no
60
+ signed-in user.
61
+ - **Writes** (contact forms, entries, sign-ups) go to your app's **server functions**
62
+ (server-functions.md), called from the browser as today. Their capabilities, secrets and limits
63
+ apply.
64
+ - **Other hosts:** only those in `site.network`. Anything else is refused (`403 Blocked`).
65
+ - **Limits per request:** a CPU-time budget and a cap on outgoing calls. A page that needs more is
66
+ doing work that belongs in a server function or a scheduled job.
67
+ - **No secrets** in the server bundle and no Node built-ins beyond `nodejs_compat`.
68
+
69
+ ## Caching
70
+
71
+ The router keeps a rendered page at the edge when your response says it may:
72
+
73
+ | Your response | At the edge |
74
+ |---|---|
75
+ | `Cache-Control: public, s-maxage=300` (the template's default for pages) | kept up to 5 minutes |
76
+ | `private`, `no-store`, `no-cache`, or a `Set-Cookie` | never kept |
77
+ | no `Cache-Control` | never kept |
78
+
79
+ Cached pages are keyed by the release and the collection's **content generation**: when products,
80
+ public app records, app settings or the collection change, the generation moves and every page of
81
+ the collection's sites re-renders on its next visit (within about 30 seconds). Publishing a release
82
+ does the same. Tracking parameters (`utm_*`, `gclid`, `fbclid`…) don't create separate entries.
83
+
84
+ Keep pages the same for every visitor. Anything personal (a signed-in owner's products, a basket)
85
+ renders in the browser after load.
86
+
87
+ ## Locally
88
+
89
+ `npm run dev` runs the site with your dev collection (`.env`: `VITE_SL_COLLECTION_ID`), because
90
+ there's no router to say which site it is: `SL.site.fromRequest()` is null, so fall back to it.
91
+ Forge's preview runs the same server code the site runtime does.
package/dist/openapi.yaml CHANGED
@@ -19652,6 +19652,8 @@ components:
19652
19652
  $ref: "#/components/schemas/AppDataDeclaration"
19653
19653
  headless:
19654
19654
  $ref: "#/components/schemas/AppHeadlessDeclaration"
19655
+ connector:
19656
+ $ref: "#/components/schemas/AppConnectorDeclaration"
19655
19657
  required:
19656
19658
  - name
19657
19659
  - version
@@ -19659,6 +19661,295 @@ components:
19659
19661
  - function
19660
19662
  - files
19661
19663
  - components
19664
+ ConnectorConnectionField:
19665
+ type: object
19666
+ properties:
19667
+ name:
19668
+ type: string
19669
+ label:
19670
+ type: string
19671
+ description:
19672
+ type: string
19673
+ secret:
19674
+ type: string
19675
+ required:
19676
+ type: boolean
19677
+ type:
19678
+ type: string
19679
+ enum:
19680
+ - text
19681
+ - url
19682
+ - select
19683
+ - number
19684
+ - boolean
19685
+ options:
19686
+ type: array
19687
+ items:
19688
+ type: object
19689
+ additionalProperties: true
19690
+ placeholder:
19691
+ type: string
19692
+ required:
19693
+ - name
19694
+ - label
19695
+ ConnectorPreset:
19696
+ type: object
19697
+ properties:
19698
+ id:
19699
+ type: string
19700
+ title:
19701
+ type: string
19702
+ description:
19703
+ type: string
19704
+ direction:
19705
+ type: string
19706
+ enum:
19707
+ - inbound
19708
+ - outbound
19709
+ entity:
19710
+ type: string
19711
+ flow:
19712
+ type: object
19713
+ additionalProperties: true
19714
+ required:
19715
+ - id
19716
+ - title
19717
+ - direction
19718
+ - entity
19719
+ ConnectorPanel:
19720
+ type: object
19721
+ properties:
19722
+ id:
19723
+ type: string
19724
+ title:
19725
+ type: string
19726
+ component:
19727
+ type: string
19728
+ required:
19729
+ - id
19730
+ - title
19731
+ - component
19732
+ AppConnectorDeclaration:
19733
+ type: object
19734
+ properties:
19735
+ provider:
19736
+ type: string
19737
+ title:
19738
+ type: string
19739
+ directions:
19740
+ type: array
19741
+ items:
19742
+ type: string
19743
+ enum:
19744
+ - inbound
19745
+ - outbound
19746
+ entities:
19747
+ type: array
19748
+ items:
19749
+ type: string
19750
+ connection:
19751
+ type: object
19752
+ additionalProperties: true
19753
+ settings:
19754
+ type: object
19755
+ additionalProperties: true
19756
+ presets:
19757
+ type: array
19758
+ items:
19759
+ $ref: "#/components/schemas/ConnectorPreset"
19760
+ functions:
19761
+ type: object
19762
+ additionalProperties: true
19763
+ testConnection:
19764
+ type: string
19765
+ describeTarget:
19766
+ type: string
19767
+ sample:
19768
+ type: string
19769
+ shape:
19770
+ type: string
19771
+ serialize:
19772
+ type: string
19773
+ send:
19774
+ type: string
19775
+ fetch:
19776
+ type: string
19777
+ panels:
19778
+ type: array
19779
+ items:
19780
+ $ref: "#/components/schemas/ConnectorPanel"
19781
+ aiGuide:
19782
+ type: string
19783
+ required:
19784
+ - provider
19785
+ - directions
19786
+ - entities
19787
+ - functions
19788
+ ConnectorInstallation:
19789
+ type: object
19790
+ properties:
19791
+ collectionId:
19792
+ type: string
19793
+ appId:
19794
+ type: string
19795
+ flowId:
19796
+ type: string
19797
+ connection:
19798
+ type: object
19799
+ additionalProperties: true
19800
+ settings:
19801
+ type: object
19802
+ additionalProperties: true
19803
+ required:
19804
+ - collectionId
19805
+ - appId
19806
+ - connection
19807
+ - settings
19808
+ ConnectorTestResult:
19809
+ type: object
19810
+ properties:
19811
+ ok:
19812
+ type: boolean
19813
+ message:
19814
+ type: string
19815
+ required:
19816
+ - ok
19817
+ - message
19818
+ ConnectorTargetField:
19819
+ type: object
19820
+ properties:
19821
+ path:
19822
+ type: string
19823
+ label:
19824
+ type: string
19825
+ type:
19826
+ type: string
19827
+ enum:
19828
+ - string
19829
+ - number
19830
+ - boolean
19831
+ - date
19832
+ - object
19833
+ - array
19834
+ required:
19835
+ type: boolean
19836
+ description:
19837
+ type: string
19838
+ enum:
19839
+ type: array
19840
+ items:
19841
+ type: string
19842
+ required:
19843
+ - path
19844
+ ConnectorTargetSchema:
19845
+ type: object
19846
+ properties:
19847
+ fields:
19848
+ type: array
19849
+ items:
19850
+ $ref: "#/components/schemas/ConnectorTargetField"
19851
+ required:
19852
+ - fields
19853
+ ConnectorRecords:
19854
+ type: object
19855
+ properties:
19856
+ records:
19857
+ type: array
19858
+ items:
19859
+ type: object
19860
+ additionalProperties: true
19861
+ nextCursor:
19862
+ type: string
19863
+ required:
19864
+ - records
19865
+ ConnectorShapeInput:
19866
+ type: object
19867
+ properties:
19868
+ installation:
19869
+ $ref: "#/components/schemas/ConnectorInstallation"
19870
+ records:
19871
+ type: array
19872
+ items:
19873
+ type: object
19874
+ additionalProperties: true
19875
+ required:
19876
+ - installation
19877
+ - records
19878
+ ConnectorShapeResult:
19879
+ type: object
19880
+ properties:
19881
+ documents:
19882
+ type: array
19883
+ items:
19884
+ type: object
19885
+ additionalProperties: true
19886
+ skipped:
19887
+ type: array
19888
+ items:
19889
+ type: object
19890
+ additionalProperties: true
19891
+ required:
19892
+ - documents
19893
+ ConnectorSerializeInput:
19894
+ type: object
19895
+ properties:
19896
+ installation:
19897
+ $ref: "#/components/schemas/ConnectorInstallation"
19898
+ documents:
19899
+ type: array
19900
+ items:
19901
+ type: object
19902
+ additionalProperties: true
19903
+ eventType:
19904
+ type: string
19905
+ required:
19906
+ - installation
19907
+ - documents
19908
+ ConnectorSerializeResult:
19909
+ type: object
19910
+ properties:
19911
+ payloads:
19912
+ type: array
19913
+ items: {}
19914
+ skipped:
19915
+ type: array
19916
+ items:
19917
+ type: object
19918
+ additionalProperties: true
19919
+ required:
19920
+ - payloads
19921
+ ConnectorSendInput:
19922
+ type: object
19923
+ properties:
19924
+ installation:
19925
+ $ref: "#/components/schemas/ConnectorInstallation"
19926
+ payloads:
19927
+ type: array
19928
+ items: {}
19929
+ required:
19930
+ - installation
19931
+ - payloads
19932
+ ConnectorSendResult:
19933
+ type: object
19934
+ properties:
19935
+ results:
19936
+ type: array
19937
+ items:
19938
+ type: object
19939
+ additionalProperties: true
19940
+ required:
19941
+ - results
19942
+ ConnectorFetchInput:
19943
+ type: object
19944
+ properties:
19945
+ installation:
19946
+ $ref: "#/components/schemas/ConnectorInstallation"
19947
+ cursor:
19948
+ type: string
19949
+ limit:
19950
+ type: number
19951
+ required:
19952
+ - installation
19662
19953
  CollectionAppWidget:
19663
19954
  type: object
19664
19955
  properties:
@@ -25913,6 +26204,18 @@ components:
25913
26204
  type: string
25914
26205
  auth:
25915
26206
  $ref: "#/components/schemas/FlowConnectionAuth"
26207
+ FlowConnectorRef:
26208
+ type: object
26209
+ properties:
26210
+ appId:
26211
+ type: string
26212
+ preset:
26213
+ type: string
26214
+ settings:
26215
+ type: object
26216
+ additionalProperties: true
26217
+ required:
26218
+ - appId
25916
26219
  IntegrationFlowConfig:
25917
26220
  type: object
25918
26221
  properties:
@@ -25922,6 +26225,8 @@ components:
25922
26225
  type: array
25923
26226
  items:
25924
26227
  $ref: "#/components/schemas/FieldMapping"
26228
+ connector:
26229
+ $ref: "#/components/schemas/FlowConnectorRef"
25925
26230
  IntegrationFlow:
25926
26231
  type: object
25927
26232
  properties:
@@ -26054,6 +26359,12 @@ components:
26054
26359
  type: number
26055
26360
  failed:
26056
26361
  type: number
26362
+ created:
26363
+ type: number
26364
+ updated:
26365
+ type: number
26366
+ skipped:
26367
+ type: number
26057
26368
  status:
26058
26369
  $ref: "#/components/schemas/RunStatus"
26059
26370
  runId:
@@ -26062,9 +26373,47 @@ components:
26062
26373
  - flowId
26063
26374
  - direction
26064
26375
  - records
26065
- - sent
26066
26376
  - failed
26067
26377
  - status
26378
+ InstalledConnector:
26379
+ type: object
26380
+ properties:
26381
+ appId:
26382
+ type: string
26383
+ name:
26384
+ type: string
26385
+ channel:
26386
+ type: string
26387
+ version:
26388
+ type: string
26389
+ connector:
26390
+ type: object
26391
+ additionalProperties: true
26392
+ installation:
26393
+ type: object
26394
+ additionalProperties: true
26395
+ secrets:
26396
+ type: object
26397
+ additionalProperties:
26398
+ type: object
26399
+ additionalProperties: true
26400
+ required:
26401
+ - appId
26402
+ - name
26403
+ - channel
26404
+ - version
26405
+ - connector
26406
+ - installation
26407
+ - secrets
26408
+ SaveConnectorInstallationInput:
26409
+ type: object
26410
+ properties:
26411
+ connection:
26412
+ type: object
26413
+ additionalProperties: true
26414
+ settings:
26415
+ type: object
26416
+ additionalProperties: true
26068
26417
  CapturedRequest:
26069
26418
  type: object
26070
26419
  properties:
@@ -26406,6 +26755,14 @@ components:
26406
26755
  - none
26407
26756
  UpdateFlowInput:
26408
26757
  $ref: "#/components/schemas/CreateFlowInput"
26758
+ ConnectorConsoleStep:
26759
+ type: string
26760
+ enum:
26761
+ - testConnection
26762
+ - describeTarget
26763
+ - sample
26764
+ - shape
26765
+ - serialize
26409
26766
  RunTrigger:
26410
26767
  type: string
26411
26768
  enum: