@lessly/sdk-app 0.1.11
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 +134 -0
- package/dist/client.gen-DtgaSZYB.d.cts +909 -0
- package/dist/client.gen-DtgaSZYB.d.ts +909 -0
- package/dist/index.cjs +932 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +929 -0
- package/dist/index.js.map +1 -0
- package/dist/organization/index.cjs +251 -0
- package/dist/organization/index.cjs.map +1 -0
- package/dist/organization/index.d.cts +200 -0
- package/dist/organization/index.d.ts +200 -0
- package/dist/organization/index.js +201 -0
- package/dist/organization/index.js.map +1 -0
- package/dist/playground/index.cjs +66 -0
- package/dist/playground/index.cjs.map +1 -0
- package/dist/playground/index.d.cts +52 -0
- package/dist/playground/index.d.ts +52 -0
- package/dist/playground/index.js +53 -0
- package/dist/playground/index.js.map +1 -0
- package/docs/README.md +82 -0
- package/docs/recipes/federation.md +111 -0
- package/docs/recipes/local-dev.md +107 -0
- package/docs/recipes/sdk-usage.md +99 -0
- package/docs/rules.md +89 -0
- package/package.json +54 -0
- package/src/gen/bindings.gen.ts +782 -0
- package/src/gen/client.gen.ts +237 -0
- package/src/gen/manifest.gen.ts +2 -0
- package/src/gen/organization/index.ts +3 -0
- package/src/gen/organization/queryOptions.gen.ts +318 -0
- package/src/gen/playground/index.ts +3 -0
- package/src/gen/playground/queryOptions.gen.ts +86 -0
- package/src/gen/types.gen.ts +936 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Recipe: calling the platform API
|
|
2
|
+
|
|
3
|
+
How to reach Lessly platform data from an App. The normative rule is APP-001
|
|
4
|
+
(all platform data access goes through `@lessly/sdk-app`) and APP-006 (prefer
|
|
5
|
+
the generated option factories) — this page shows the *how*.
|
|
6
|
+
|
|
7
|
+
## Create the client
|
|
8
|
+
|
|
9
|
+
Create one client with `createLesslyApp` and call operations through the
|
|
10
|
+
Proxy namespace tree:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { createLesslyApp } from '@lessly/sdk-app';
|
|
14
|
+
|
|
15
|
+
const sdk = createLesslyApp({
|
|
16
|
+
baseUrl: 'https://api.lessly.dev',
|
|
17
|
+
productId: 'prod_123',
|
|
18
|
+
// getCsrfToken defaults to reading the `lessly_csrf` cookie; override for SSR/tests.
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const { domains } = await sdk.tracking.domains.list({ page: 1 });
|
|
22
|
+
await sdk.tracking.domains.create({ host: 'links.example.com' });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`productId` is the active product — read it from your `./App` props or the
|
|
26
|
+
`X-Product-Id` header (see APP-005), never decoded from a token.
|
|
27
|
+
|
|
28
|
+
## Handle errors with `LesslyApiError`
|
|
29
|
+
|
|
30
|
+
Every failed call throws a typed `LesslyApiError` with `status`, `code`, and
|
|
31
|
+
`body`. Branch on `code` (stable) rather than `status` alone when you need to
|
|
32
|
+
distinguish platform-defined failure reasons:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { LesslyApiError } from '@lessly/sdk-app';
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await sdk.tracking.domains.create({ host: 'links.example.com' });
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err instanceof LesslyApiError) {
|
|
41
|
+
if (err.status === 409) {
|
|
42
|
+
// err.code / err.body carry the platform's structured reason
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Prefer the generated query/mutation option factories
|
|
50
|
+
|
|
51
|
+
Each namespace subpath (`@lessly/sdk-app/<namespace>`) ships framework-agnostic
|
|
52
|
+
query/mutation **option factories** — plain objects, not hooks — usable with
|
|
53
|
+
any TanStack Query adapter (React, Solid, Vue, Svelte). You pass the `sdk`
|
|
54
|
+
instance explicitly:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// React example — works the same with any @tanstack/*-query adapter.
|
|
58
|
+
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
59
|
+
import {
|
|
60
|
+
trackingDomainsListQueryOptions,
|
|
61
|
+
trackingDomainsCreateMutationOptions,
|
|
62
|
+
} from '@lessly/sdk-app/tracking';
|
|
63
|
+
|
|
64
|
+
function Domains() {
|
|
65
|
+
const { data } = useQuery(trackingDomainsListQueryOptions(sdk, { page: 1 }));
|
|
66
|
+
const create = useMutation(trackingDomainsCreateMutationOptions(sdk));
|
|
67
|
+
// create.mutate({ host: 'links.example.com' })
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The factories return `{ queryKey, queryFn }` (and `{ mutationKey, mutationFn }`),
|
|
72
|
+
so they also work directly with `queryClient.ensureQueryData(...)` and friends
|
|
73
|
+
— useful for prefetching outside a component, e.g. in a route loader:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
await queryClient.ensureQueryData(trackingDomainsListQueryOptions(sdk, { page: 1 }));
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Prefer these factories over hand-rolling calls against `sdk.<namespace>...`
|
|
80
|
+
directly (APP-006): they keep query keys, request shapes, and cache
|
|
81
|
+
invalidation consistent with the published API surface, and stay in sync with
|
|
82
|
+
it automatically when you run `npm i @lessly/sdk-app@latest`.
|
|
83
|
+
|
|
84
|
+
## Namespaces and subpath imports
|
|
85
|
+
|
|
86
|
+
The SDK is organized by namespace (`tracking`, `mail`, `playground`, ...). Call
|
|
87
|
+
operations off `sdk.<namespace>.<resource>.<action>(...)`, and import that
|
|
88
|
+
namespace's option factories from the matching subpath,
|
|
89
|
+
`@lessly/sdk-app/<namespace>` — not the package root. This keeps each App's
|
|
90
|
+
bundle limited to the namespaces it actually uses.
|
|
91
|
+
|
|
92
|
+
## What not to do
|
|
93
|
+
|
|
94
|
+
- Don't `fetch` a platform host directly, and don't use any other HTTP client
|
|
95
|
+
for platform data (APP-001) — always go through `sdk` or the generated
|
|
96
|
+
factories.
|
|
97
|
+
- Don't hand-roll request paths against the client when a
|
|
98
|
+
`*QueryOptions`/`*MutationOptions` factory already exists for that operation
|
|
99
|
+
(APP-006).
|
package/docs/rules.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# App Rules
|
|
2
|
+
|
|
3
|
+
Normative rules for building a Lessly App on `@lessly/sdk-app`. Read this
|
|
4
|
+
before writing code; see `README.md` for the map of the rest of the guide.
|
|
5
|
+
|
|
6
|
+
Severity (RFC 2119): **MUST** blocks merge, **SHOULD** is a strong
|
|
7
|
+
recommendation, **MAY** is an allowed option.
|
|
8
|
+
|
|
9
|
+
Rule IDs (`APP-NNN`) are immutable once merged: a rule's ID and meaning never
|
|
10
|
+
change, even if its wording is later clarified. Some of these rules cover
|
|
11
|
+
ground shared with extensions and the platform's Module Federation host; where
|
|
12
|
+
that's true, this file references the existing `FED-*` rule ID from the
|
|
13
|
+
extensions-guide instead of restating it — go there for the full rule text,
|
|
14
|
+
rationale, and examples.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
### APP-001 (MUST) — All platform data access goes through `@lessly/sdk-app`
|
|
19
|
+
|
|
20
|
+
Every read or write to a Lessly platform API MUST go through a
|
|
21
|
+
`@lessly/sdk-app` client created with `createLesslyApp`, or through the
|
|
22
|
+
generated `*QueryOptions`/`*MutationOptions` factories built on top of it. An
|
|
23
|
+
App MUST NOT `fetch` a platform host directly, and MUST NOT use any other HTTP
|
|
24
|
+
client to reach platform data.
|
|
25
|
+
|
|
26
|
+
### APP-002 (MUST) — Frontend-only
|
|
27
|
+
|
|
28
|
+
An App MUST NOT ship a backend service, a database, or an MCP endpoint. All
|
|
29
|
+
of its logic runs in the browser, built on `@lessly/sdk-app` and the Module
|
|
30
|
+
Federation contract below. If a feature needs server-side state or a
|
|
31
|
+
service-to-service call, it belongs in an extension, not an App.
|
|
32
|
+
|
|
33
|
+
### APP-003 (MUST) — Manifest is `lessly.app.yaml`, schema v1
|
|
34
|
+
|
|
35
|
+
The manifest file is `lessly.app.yaml`, and its top-level surface is exactly:
|
|
36
|
+
`app_schema_version: 1`, `id`, `title`, `version`, `nav` (with `nav.icon`). No
|
|
37
|
+
other top-level field is valid — in particular, no extension/backend manifest
|
|
38
|
+
field (`s2s`, `webhooks`, `billing`, `analytics`, `routing`, `public`, ...)
|
|
39
|
+
belongs in an App manifest.
|
|
40
|
+
|
|
41
|
+
### APP-004 (MUST) — Module Federation contract
|
|
42
|
+
|
|
43
|
+
An App exposes `./App` as its Module Federation remote entry, implementing
|
|
44
|
+
the shell's remote contract (FED-001). The Module Federation `name` MUST equal
|
|
45
|
+
the manifest `id` (FED-004). React, React DOM, React Router, and `@lessly/ui`
|
|
46
|
+
MUST be declared as shared singletons, never bundled (FED-005), using
|
|
47
|
+
`@module-federation/vite` pinned at exactly `1.16.12` (FED-006). The asset
|
|
48
|
+
base in federation mode is the absolute path `/~/<id>/`. The build MUST
|
|
49
|
+
produce one merged dual-mode `dist/` covering both standalone and federation
|
|
50
|
+
artifacts (FED-007). An App MAY additionally expose a data-only `./navigation`
|
|
51
|
+
module (FED-011). This rule states the contract; it does not restate FED-001,
|
|
52
|
+
FED-004, FED-005, FED-006, FED-007, or FED-011 — see those rules for exact
|
|
53
|
+
wording, rationale, and examples.
|
|
54
|
+
|
|
55
|
+
### APP-005 (MUST) — Auth is ambient; an App does not own it
|
|
56
|
+
|
|
57
|
+
Authentication is ambient: the hosting shell owns cookies in production and a
|
|
58
|
+
bearer proxy in local dev. An App MUST NOT implement login, token handling,
|
|
59
|
+
refresh, or product switching — those belong to the shell. An App MUST read
|
|
60
|
+
the active product from its props or the `X-Product-Id` header, never by
|
|
61
|
+
decoding a token.
|
|
62
|
+
|
|
63
|
+
An App MUST NOT rely on the `user` prop for anything beyond display. In
|
|
64
|
+
dev-standalone mode, `user` is a stub (`{ id }` sourced from the local dev
|
|
65
|
+
JWT, `displayName: 'Dev User'`) — it is not a real session. The real,
|
|
66
|
+
shell-authenticated user only arrives once the app is composed via
|
|
67
|
+
federation, through the `./App` props.
|
|
68
|
+
|
|
69
|
+
### APP-006 (SHOULD) — Use the generated query/mutation option factories
|
|
70
|
+
|
|
71
|
+
Prefer the generated `*QueryOptions`/`*MutationOptions` factories
|
|
72
|
+
(`@lessly/sdk-app/<namespace>`) over hand-rolling request paths against the
|
|
73
|
+
SDK client directly. They keep query keys, request shapes, and cache
|
|
74
|
+
invalidation consistent with the published API surface, and stay in sync with
|
|
75
|
+
it automatically on upgrade.
|
|
76
|
+
|
|
77
|
+
### APP-007 (MUST) — Versioning hygiene
|
|
78
|
+
|
|
79
|
+
Bump the manifest `version` on every release. Keep the SDK and this guide
|
|
80
|
+
current with:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npm i @lessly/sdk-app@latest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use this exact `@latest` form — not `npm update`. `@lessly/sdk-app` is
|
|
87
|
+
pre-1.0 and pinned with a caret range, so `npm update` never crosses a minor
|
|
88
|
+
version; since this SDK also bumps its minor version whenever the platform
|
|
89
|
+
ships a new tool, `npm update` would never actually deliver those updates.
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lessly/sdk-app",
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"docs/*.md",
|
|
12
|
+
"docs/recipes",
|
|
13
|
+
"src/gen"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/",
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"generate": "tsx scripts/generate.ts fixtures/catalog.sample.json",
|
|
21
|
+
"ci": "tsx scripts/ci-run.ts",
|
|
22
|
+
"sync-exports": "tsx scripts/sync-exports.ts",
|
|
23
|
+
"build": "npm run sync-exports && tsup",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest run"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"json-schema-to-typescript": "^15.0.3",
|
|
31
|
+
"tsup": "^8.3.0",
|
|
32
|
+
"tsx": "^4.19.0",
|
|
33
|
+
"typescript": "^5.6.0",
|
|
34
|
+
"vitest": "^2.1.0"
|
|
35
|
+
},
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.js",
|
|
40
|
+
"require": "./dist/index.cjs"
|
|
41
|
+
},
|
|
42
|
+
"./organization": {
|
|
43
|
+
"types": "./dist/organization/index.d.ts",
|
|
44
|
+
"import": "./dist/organization/index.js",
|
|
45
|
+
"require": "./dist/organization/index.cjs"
|
|
46
|
+
},
|
|
47
|
+
"./playground": {
|
|
48
|
+
"types": "./dist/playground/index.d.ts",
|
|
49
|
+
"import": "./dist/playground/index.js",
|
|
50
|
+
"require": "./dist/playground/index.cjs"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"sdkContentHash": "sha256:c6888491b9e25a037a38993fa524e1a5b646053465b66a88c5f7dfe3ba69ce70"
|
|
54
|
+
}
|