@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 ADDED
@@ -0,0 +1,134 @@
1
+ # @lessly/sdk-app
2
+
3
+ The Lessly SDK for App is a TypeScript SDK that provides runtime bindings and type-safe generated code for interacting with Lessly Platform APIs and services. This package generates type definitions and client bindings from API catalogs, enabling developers to build applications that consume Lessly platform services with full type safety and autocompletion.
4
+
5
+ **Note:** The `src/gen` directory contains generated SDK code and should not be edited by hand. Generated files are produced by running `npm run generate` with an API catalog.
6
+
7
+ ## Usage
8
+
9
+ Create a client with `createLesslyApp`, then call operations through the Proxy namespace tree:
10
+
11
+ ```ts
12
+ import { createLesslyApp } from '@lessly/sdk-app';
13
+
14
+ const sdk = createLesslyApp({
15
+ baseUrl: 'https://api.lessly.dev',
16
+ productId: 'prod_123',
17
+ // getCsrfToken defaults to reading the `lessly_csrf` cookie; override for SSR/tests.
18
+ });
19
+
20
+ const { domains } = await sdk.tracking.domains.list({ page: 1 });
21
+ await sdk.tracking.domains.create({ host: 'links.example.com' });
22
+ ```
23
+
24
+ Errors are surfaced as a typed `LesslyApiError` (`status`, `code`, `body`).
25
+
26
+ ### TanStack Query
27
+
28
+ Each namespace subpath (`@lessly/sdk-app/<namespace>`) also ships framework-agnostic
29
+ query/mutation **option factories** — plain objects, not hooks — usable with any
30
+ TanStack Query adapter (React, Solid, Vue, Svelte). You pass the `sdk` instance
31
+ explicitly:
32
+
33
+ ```ts
34
+ // React example — works the same with any @tanstack/*-query adapter.
35
+ import { useQuery, useMutation } from '@tanstack/react-query';
36
+ import {
37
+ trackingDomainsListQueryOptions,
38
+ trackingDomainsCreateMutationOptions,
39
+ } from '@lessly/sdk-app/tracking';
40
+
41
+ function Domains() {
42
+ const { data } = useQuery(trackingDomainsListQueryOptions(sdk, { page: 1 }));
43
+ const create = useMutation(trackingDomainsCreateMutationOptions(sdk));
44
+ // create.mutate({ host: 'links.example.com' })
45
+ }
46
+ ```
47
+
48
+ The factories return `{ queryKey, queryFn }` (and `{ mutationKey, mutationFn }`),
49
+ so they also work directly with `queryClient.ensureQueryData(...)` and friends.
50
+ This package has **no runtime dependencies** — bring your own TanStack Query adapter.
51
+
52
+ ## Publishing
53
+
54
+ This repo holds source only — **humans push code to GitHub, builds run in dev-console
55
+ via Cloud Build, and the published package lives on npmjs.** Nothing commits or pushes back to
56
+ this repo automatically; the catalog snapshot and changelog are stored in GCS, not in git.
57
+
58
+ Cloud Build clones the repo read-only and drives the runner-agnostic pipeline CLI:
59
+
60
+ ```sh
61
+ npm run ci -- --channel <next|latest> --catalog-url <URL> [--baseline <path>]
62
+ ```
63
+
64
+ - `--channel` — the npm dist-tag to publish under (`next` for staging, `latest` for production).
65
+ - `--catalog-url` — the public catalog endpoint, fetched with an anonymous GET
66
+ (staging `https://api.lessly.dev/catalog/tools`, production `https://api.lessly.com/catalog/tools`).
67
+ - `--baseline` — path to the previous catalog snapshot (downloaded from GCS) used for the
68
+ content-hash gate and the semver diff-classifier. **Omit it for the first publish.**
69
+
70
+ The very first publish on the (empty) npmjs registry is floored at **`0.2.0`**, not `0.1.0`,
71
+ so it lands strictly above the legacy GAR `0.1.x` line that existing consumers pinned (e.g.
72
+ `^0.1.1`). This mirrors the `@lessly/ui` migration (GAR `0.1.0` → npmjs `0.2.0`). Once a version
73
+ exists on a channel's dist-tag, normal semver bumping (and the recovery patch-bump) takes over.
74
+ The floor is keyed off the **registry**, not the flag: if the channel's dist-tag resolves to
75
+ nothing (empty npmjs), the run takes the first-publish path even when Cloud Build passes a stored
76
+ (GAR-era) baseline — the baseline is ignored and the freshly-fetched catalog ships as the `0.2.0`
77
+ initial release. So Cloud Build may keep passing the stored baseline through the migration without
78
+ erroring.
79
+
80
+ Because npm versions are immutable **package-wide** (not per-dist-tag), a missing channel tag does
81
+ not by itself mean an empty registry. The pipeline therefore also checks the package-wide max
82
+ version: if a tag is absent but the package already carries versions under another tag (e.g. `next`
83
+ published `0.2.0` and a first production run finds `latest` absent), it force-publishes a **fresh
84
+ patch bumped past that max** so `npm publish --tag <channel>` seeds the tag without a 409 on the
85
+ already-published version. The genuine `0.2.0` first-publish floor applies only when the package
86
+ has **no versions anywhere**.
87
+
88
+ The CLI fetches the catalog, regenerates `src/gen`, and compares against the baseline. When
89
+ the generated output changed it bumps the version in `package.json` (base version resolved
90
+ from `npm view @lessly/sdk-app dist-tags` against the configured registry, per channel —
91
+ see "Registry: public npmjs" below), appends `CHANGELOG.md`, and writes:
92
+
93
+ - `out/result.json` — `{ publish_needed, level, version }` (always written).
94
+ - `out/new-snapshot.json` — the new snapshot (written only when a publish is needed; see
95
+ "Snapshot shape" below).
96
+
97
+ The CLI performs **no git, gcloud, or npm-publish operations** — it is pure Node. Cloud Build
98
+ reads `out/result.json`, then runs `npm publish` and uploads the new snapshot + changelog to
99
+ GCS itself.
100
+
101
+ ### Registry: public npmjs
102
+
103
+ The package now publishes to **public npmjs**, not GAR: `package.json` sets
104
+ `publishConfig.registry = https://registry.npmjs.org/` and `publishConfig.access = public`.
105
+ Cloud Build publishes each channel under its own dist-tag — `next` for staging, `latest` for
106
+ production.
107
+
108
+ **Scope-override gotcha:** this repo's `.npmrc` still carries `@lessly:registry=<GAR URL>` for
109
+ dependency-resolution stability (nothing in this package's runtime deps is scoped
110
+ `@lessly`, so that entry is safe to keep). A scoped `.npmrc` registry entry **outranks**
111
+ `publishConfig.registry` for npm's own resolution, so if the `rebuild_sdk` Cloud Build step
112
+ were to run a bare `npm publish`, it would silently target GAR instead of npmjs. The step
113
+ MUST publish with an explicit override:
114
+
115
+ ```sh
116
+ npm publish --registry https://registry.npmjs.org/
117
+ ```
118
+
119
+ Base-version resolution (the `npm view <pkg>@<channel> dist-tags` lookup inside the pipeline
120
+ CLI) follows the same rule: it reads the registry from `package.json`'s `publishConfig.registry`
121
+ first, falling back to the `.npmrc` `@lessly:registry` value if `publishConfig.registry` is
122
+ absent. This requires no change to the CLI's `--channel`/`--catalog-url`/`--baseline` contract.
123
+
124
+ ### Snapshot shape
125
+
126
+ `out/new-snapshot.json` is now a wrapper, `{ catalog, docsHash }`, rather than a bare catalog —
127
+ `docsHash` is the content hash of the **shipped** guide (the `docs/*.md` + `docs/recipes` files,
128
+ matching the `files` globs; internal `docs/superpowers/` is excluded), added so a docs-only change
129
+ (no catalog diff) still triggers a PATCH publish. The file remains an **opaque blob** to Cloud
130
+ Build: store it in GCS exactly as written and pass it back verbatim as `--baseline` on the next
131
+ run. Older, bare-catalog baselines from before this change are still accepted: because such a
132
+ baseline predates in-package docs, the first run against it publishes a **one-time migration
133
+ PATCH** (when shipped docs are present) that ships the guide and seeds the wrapper snapshot —
134
+ after which docs-change detection is fully hash-based.