@loopwise/sdk 0.1.0-beta.2 → 0.1.0-beta.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@loopwise/sdk` are documented here.
4
+
5
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ Versioning: [SemVer](https://semver.org/) with pre-1.0 semantics — minor bumps
7
+ (`0.x → 0.x+1`) may include breaking changes; beta tags (`0.1.0-beta.N`)
8
+ follow the same rules as a single 0.x line.
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [0.1.0-beta.3] — 2026-05-20
13
+
14
+ ### Added
15
+
16
+ - `LoopwiseConfig.site` — preferred name for the site this client reads
17
+ from. Accepts a subdomain (`'acme'`) or full domain
18
+ (`'acme.myteachify.com'`, `'nii.school'`). Replaces the deprecated
19
+ `tenant` field; both fields are accepted, `site` wins when both are set.
20
+ - `LoopwiseError.site` — mirror of the deprecated `tenant` property.
21
+ Catch-and-read consumers can migrate at their own pace.
22
+
23
+ ### Deprecated
24
+
25
+ - `LoopwiseConfig.tenant` — renamed to `site`. Both fields still work;
26
+ `site` wins when both are set. **Removed in `0.2.0`.**
27
+ - `LoopwiseError.tenant` — renamed to `site`. **Removed in `0.2.0`.**
28
+
29
+ ### Removed
30
+
31
+ - `LoopwiseErrorCode = '…' | 'TENANT_NOT_FOUND'` — the literal was never
32
+ emitted by any SDK code path (it was a reserved/aspirational shape
33
+ that never got runtime mapping). Removed instead of renaming to
34
+ `'SITE_NOT_FOUND'` — no point preserving phantom API surface. If a
35
+ future SDK release adds actual runtime mapping (e.g. translating
36
+ Rails' `School-000` GraphQL error into a typed code), the canonical
37
+ `'SITE_NOT_FOUND'` literal will be added then. Removal is type-only
38
+ (narrowing checks like `case 'TENANT_NOT_FOUND':` were unreachable
39
+ branches in any consumer code).
40
+
41
+ ### Changed (non-breaking)
42
+
43
+ - README + every user-visible JSDoc reframed to use "site" language
44
+ instead of "tenant" / "multi-tenant" / "BFF derives tenant from host"
45
+ — SDK consumers don't need to learn the platform's multi-tenancy
46
+ model, just the SDK's behaviour and limits (`createClient({})` reads
47
+ from the page's own site; `createClient({ site: '…' })` reads from a
48
+ different site at the cost of authenticated calls).
49
+
50
+ ## [0.1.0-beta.2] — 2026-05-19
51
+
52
+ ### Fixed
53
+
54
+ - `orders.get` returns `null` from a null GraphQL payload instead of
55
+ throwing.
56
+ - `orders.get` passes the `id` as an inline GraphQL argument rather than
57
+ a `$variable`, avoiding collision when composed via `batch()`.
58
+
59
+ ### Documentation
60
+
61
+ - `orders.get` JSDoc clarifies `OrderItem.amount` is per-unit price
62
+ (not line total), and null-guards the `tradeNo` URL-param lookup
63
+ example.
64
+
65
+ ## [0.1.0-beta.1]
66
+
67
+ Initial public beta. `courses.list/get`, `posts.list/get`, `orders.get`,
68
+ `batch()`, raw `query()`. Schema-lock pipeline. Same-origin BFF and
69
+ explicit cross-origin endpoint resolution.
package/README.md CHANGED
@@ -15,15 +15,15 @@ Same package powers all three; the difference is the loader.
15
15
  ```ts
16
16
  import { createClient } from '@loopwise/sdk';
17
17
 
18
- // Browser, same-origin (the page is served from the tenant origin):
18
+ // Reads from the site this page is on. No config needed.
19
19
  const loopwise = createClient({});
20
20
  const { items } = await loopwise.courses.list({ limit: 20 });
21
21
  ```
22
22
 
23
- That's the entire happy path **when the page is served from the tenant
24
- origin** (`acme.kaik.io`, `acme.myteachify.com`, `nii.school`, etc.). The BFF
25
- on that origin derives the tenant from the request host and binds it to the
26
- response on the trusted server side.
23
+ That's it. The SDK calls the site that's serving the page
24
+ (`acme.kaik.io`, `acme.myteachify.com`, `nii.school`, etc.) and the
25
+ site's API binds the response to itself — you never identify the site
26
+ yourself. To call a different site, see [Calling a different site](#calling-a-different-site).
27
27
 
28
28
  ## Quick start — single-file HTML (no build)
29
29
 
@@ -32,44 +32,47 @@ response on the trusted server side.
32
32
  <script type="module">
33
33
  import { createClient } from 'https://esm.sh/@loopwise/sdk@beta';
34
34
 
35
- // Same-origin variant — works when the HTML is served from the tenant origin
36
- // (e.g. acme.kaik.io). Empty config; BFF derives the tenant from host.
35
+ // Reads from the site this HTML is loaded on.
37
36
  const loopwise = createClient({});
38
37
  const { items } = await loopwise.courses.list({ limit: 20 });
39
38
  document.body.textContent = items.map(c => c.title).join(', ');
40
39
  </script>
41
40
  ```
42
41
 
43
- ## Cross-origin (Loopwise Pages preview, SSR, build pipelines)
42
+ ## Calling a different site
44
43
 
45
- When the SDK runs **outside** a tenant origin, pass `tenant` explicitly so
46
- the SDK can pick the right Rails origin to call.
44
+ When your code runs **somewhere other than the site itself** — a Loopwise
45
+ Pages preview at `<scriptName>.loopwise.page`, a build / SSR pipeline,
46
+ server-to-server tooling — name the site you want to read from:
47
47
 
48
48
  ```ts
49
49
  const loopwise = createClient({
50
- tenant: 'acme', // required cross-origin
51
- apiKey: process.env.LOOPWISE_API_KEY, // required in SSR / build (browser ignores)
50
+ site: 'acme', // 'acme' or 'acme.myteachify.com'
51
+ apiKey: process.env.LOOPWISE_API_KEY, // build/SSR only; browser ignores
52
52
  });
53
53
  ```
54
54
 
55
- ### Where this matters most: `deploy_static_page`
55
+ `site` accepts a subdomain (`'acme'`) or a full domain
56
+ (`'acme.myteachify.com'`, `'nii.school'`). The SDK uses it to pick the
57
+ right API origin.
56
58
 
57
- Pages deployed via `deploy_static_page` live at `https://<script>.loopwise.page/`
58
- — that is **not** a tenant origin, so:
59
+ ### Limits when not on the site itself
59
60
 
60
- - The BFF same-origin trick does not apply. You **must** pass `tenant`.
61
- - Browser cross-origin requests carry no first-party cookies, so anything
62
- that requires a logged-in viewer (`cart`, `viewer.profile`,
63
- authenticated mutations) **will not work** at preview time.
64
- - Public, anonymous reads (`courses.list`, `posts.list`, etc.) are fine.
61
+ Cross-origin requests **do not carry first-party cookies**. So calls
62
+ that require a signed-in visitor — `cart`, `viewer.profile`,
63
+ authenticated mutations — **will not work** from off-site code.
64
+ Public, anonymous reads (`courses.list`, `posts.list`, etc.) work fine.
65
+
66
+ This is the dominant case for `deploy_static_page` bundles (which
67
+ serve at `<scriptName>.loopwise.page`):
65
68
 
66
69
  ```ts
67
70
  // Inside an HTML bundle deployed to https://lw-acme-home.loopwise.page/
68
- const loopwise = createClient({ tenant: 'acme' });
71
+ const loopwise = createClient({ site: 'acme' });
69
72
  const { items } = await loopwise.courses.list({ limit: 20 }, { cache: 60 });
70
73
  ```
71
74
 
72
- For preview UX that needs authenticated state, see
75
+ For preview UX that needs an authenticated viewer, see
73
76
  [Preview modes](#preview-modes) below.
74
77
 
75
78
  ## `cache` option
@@ -124,22 +127,21 @@ content; use `cache: 'no-store'` for admin previews.
124
127
 
125
128
  Two preview surfaces exist, each with different capability:
126
129
 
127
- | Surface | Origin | Same-origin BFF? | Authenticated viewer? | Use for |
130
+ | Surface | URL | Hosted on the site? | Authenticated viewer? | Use for |
128
131
  |---|---|---|---|---|
129
- | **Loopwise Pages preview** | `<scriptName>.loopwise.page` | ❌ cross-origin | ❌ no first-party cookies | Anonymous storefront, marketing pages, public listings |
130
- | **Tenant-origin preview** | `<tenant-origin>/_preview/<scriptName>/` | ✅ same-origin | ✅ admin can preview as a logged-in viewer | Cart, checkout, member-gated content, full storefront |
132
+ | **Loopwise Pages preview** | `<scriptName>.loopwise.page` | ❌ separate origin | ❌ no first-party cookies | Anonymous storefront, marketing pages, public listings |
133
+ | **Site-origin preview** | `<site>/_preview/<scriptName>/` | ✅ same origin as the site | ✅ admins can preview signed in | Cart, checkout, member-gated content, full storefront |
131
134
 
132
- The tenant origin can be `<sub>.kaik.io`, `<sub>.myteachify.com`, or a
133
- custom FQDN like `nii.school`. `deploy_static_page` returns both
134
- surfaces (`preview_url` + `tenant_preview_url_template` with a
135
- `<TENANT_ORIGIN>` placeholder).
135
+ The site URL can be `<sub>.kaik.io`, `<sub>.myteachify.com`, or a custom
136
+ domain like `nii.school`. `deploy_static_page` returns both URLs in its
137
+ response.
136
138
 
137
- Pick the surface that matches what you're testing — anonymous storefront
138
- reads on Surface A (faster, edge-cacheable), authenticated flows on
139
- Surface B (logged-in only, never edge-cached). Surface B requires
140
- relative asset paths in your bundle (the proxy injects `<base>` so
141
- relative paths resolve under `/_preview/<scriptName>/`; root-absolute
142
- paths like `/style.css` still go to the tenant root and 404).
139
+ Pick the surface that matches what you're testing — anonymous reads on
140
+ Surface A (faster, edge-cacheable), authenticated flows on Surface B
141
+ (signed-in only, never edge-cached). Surface B requires relative asset
142
+ paths in your bundle (the proxy injects `<base>` so relative paths
143
+ resolve under `/_preview/<scriptName>/`; root-absolute paths like
144
+ `/style.css` still go to the site root and 404).
143
145
 
144
146
  ## React — coming soon
145
147
 
@@ -155,10 +157,12 @@ your own state (`useEffect` + `useState`, TanStack Query, etc.).
155
157
 
156
158
  ## Public API
157
159
 
158
- - `createClient(config)` — storefront client. `config.tenant` is optional in
159
- the browser when the page is on a tenant origin (BFF derives it from
160
- host); required cross-origin and in SSR / build.
160
+ - `createClient(config)` — storefront client. Pass `config.site` only
161
+ when calling a different site than the one your code is running on
162
+ (see [Calling a different site](#calling-a-different-site)).
161
163
  - `loopwise.courses.list(args, options?)` / `.get(args, options?)`.
164
+ - `loopwise.posts.list(args, options?)` / `.get(args, options?)`.
165
+ - `loopwise.orders.get(args, options?)` — post-checkout pixel-tracking pattern; see the method's JSDoc for the `?tradeNo=…` → `fbq('track', 'Purchase', …)` recipe.
162
166
  - `loopwise.batch(builders, options?)` — composes N queries into one HTTP request.
163
167
  - `loopwise.query<T>(document, variables?, operationName?, options?)` — raw GraphQL escape hatch.
164
168
 
package/dist/config.d.mts CHANGED
@@ -1,10 +1,22 @@
1
1
  //#region src/config.d.ts
2
2
  interface LoopwiseConfig {
3
3
  /**
4
- * Tenant identifier (subdomain "acme" or full FQDN "nii.school").
4
+ * The site this client reads from. Accepts a subdomain (`'acme'`) or a
5
+ * full domain (`'acme.myteachify.com'`, `'nii.school'`).
5
6
  *
6
- * Optional in browser runtime — SDK auto-resolves from `window.location.hostname`
7
- * (e.g. `acme.kaik.io` -> `"acme"`). Required in build/SSR runtime.
7
+ * Omit when your code runs on the site itself — the SDK calls the
8
+ * page's own origin. Required when running off-origin: cross-domain
9
+ * embeds (Loopwise Pages previews at `*.loopwise.page`), SSR / build
10
+ * pipelines, server-to-server tooling.
11
+ *
12
+ * Cross-origin requests carry no first-party cookies, so authenticated
13
+ * calls (`cart`, `viewer.profile`, mutations that need a signed-in
14
+ * visitor) won't work in that mode — public reads do.
15
+ */
16
+ site?: string;
17
+ /**
18
+ * @deprecated Renamed to `site`. Both fields work for now; if both are
19
+ * set, `site` wins. **Will be removed in `0.2.0`.**
8
20
  */
9
21
  tenant?: string;
10
22
  /**
@@ -15,9 +27,9 @@ interface LoopwiseConfig {
15
27
  */
16
28
  apiKey?: string;
17
29
  /**
18
- * Override the GraphQL endpoint. Defaults to:
19
- * - Browser: same-origin `/api/graphql`
20
- * - Build/SSR: `https://{tenant}/api/graphql` (or kaik.io fallback)
30
+ * Override the GraphQL endpoint. You normally never set this — the SDK
31
+ * picks the right URL from `site` (or the page's own origin when `site`
32
+ * is omitted).
21
33
  */
22
34
  endpoint?: string;
23
35
  }
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { LoopwiseConfig } from "./config.mjs";
2
2
 
3
3
  //#region src/error.d.ts
4
- type LoopwiseErrorCode = 'NETWORK' | 'AUTH' | 'GRAPHQL' | 'RATE_LIMIT' | 'TENANT_NOT_FOUND' | 'CONFIG';
4
+ type LoopwiseErrorCode = 'NETWORK' | 'AUTH' | 'GRAPHQL' | 'RATE_LIMIT' | 'CONFIG';
5
5
  interface GraphQLError {
6
6
  message: string;
7
7
  path?: (string | number)[];
@@ -9,11 +9,15 @@ interface GraphQLError {
9
9
  }
10
10
  declare class LoopwiseError extends Error {
11
11
  readonly code: LoopwiseErrorCode;
12
+ /** The site this client was configured for. */
13
+ readonly site?: string;
14
+ /** @deprecated Renamed to `site`. Holds the same resolved value. Removed in 0.2.0. */
12
15
  readonly tenant?: string;
13
16
  readonly endpoint?: string;
14
17
  readonly graphqlErrors?: GraphQLError[];
15
18
  readonly requestId?: string;
16
19
  constructor(code: LoopwiseErrorCode, message: string, detail?: {
20
+ site?: string; /** @deprecated Renamed to `site`. Removed in 0.2.0. */
17
21
  tenant?: string;
18
22
  endpoint?: string;
19
23
  graphqlErrors?: GraphQLError[];
@@ -79,9 +83,7 @@ declare class QueryBuilder<T> implements PromiseLike<T> {
79
83
  //#endregion
80
84
  //#region src/client.d.ts
81
85
  declare class LoopwiseClient implements BatchedFetcher {
82
- readonly config: {
83
- tenant?: string;
84
- } & LoopwiseConfig;
86
+ readonly config: LoopwiseConfig;
85
87
  private endpoint;
86
88
  constructor(config: LoopwiseConfig);
87
89
  private defaultEndpoint;
@@ -201,7 +203,7 @@ declare class CoursesResource {
201
203
  private client;
202
204
  constructor(client: LoopwiseClient);
203
205
  /**
204
- * List courses for the current tenant.
206
+ * List published courses.
205
207
  * @example
206
208
  * const { items, pageInfo } = await loopwise.courses.list({ limit: 20 })
207
209
  * @example
@@ -248,9 +250,8 @@ declare class OrdersResource {
248
250
  * Look up a PII-safe order projection by id (the value the storefront
249
251
  * appends to a merchant's `checkoutSuccessUrl` as `?tradeNo=...`).
250
252
  *
251
- * Returns `null`-shaped result via the GraphQL `null` payload when the
252
- * id doesn't resolve in the current tenant — does NOT throw. Use the
253
- * returned `status` to gate Meta Pixel `Purchase` events:
253
+ * Returns `null` when the id doesn't resolve on this site — does NOT
254
+ * throw. Use the returned `status` to gate Meta Pixel `Purchase` events:
254
255
  *
255
256
  * @example
256
257
  * const id = new URLSearchParams(location.search).get('tradeNo');
@@ -289,7 +290,7 @@ declare class PostsResource {
289
290
  private client;
290
291
  constructor(client: LoopwiseClient);
291
292
  /**
292
- * List published posts for the current tenant, ordered by publishedAt desc.
293
+ * List published posts, ordered by publishedAt desc.
293
294
  * @example
294
295
  * const { items, pageInfo } = await loopwise.posts.list({ limit: 10 })
295
296
  * @example
@@ -334,9 +335,18 @@ interface Loopwise {
334
335
  /**
335
336
  * Raw GraphQL escape hatch. Use when a typed resource method doesn't cover
336
337
  * what you need (new schema fields, custom queries, ad-hoc inspection).
338
+ * Variables are sent as-is; errors are still mapped to LoopwiseError.
339
+ *
340
+ * Most storefront fields (`courses`, `posts`, `order`, ...) take a
341
+ * required `subdomain: String!` argument. The BFF rewrites that variable
342
+ * to the trusted server-derived value on every request, so consumers
343
+ * declaring `$subdomain` see whatever value the BFF binds — not what
344
+ * they pass.
345
+ *
337
346
  * @example
338
- * const data = await loopwise.query<{ school: { name: string } }>(`
339
- * query { school(subdomain: "acme") { name } }
347
+ * // Minimal shape demo (always valid, no scoping required):
348
+ * const data = await loopwise.query<{ __typename: string }>(`
349
+ * query { __typename }
340
350
  * `)
341
351
  */
342
352
  query<T = unknown>(document: string, variables?: Record<string, unknown>, operationName?: string, options?: FetchOptions): Promise<T>;
package/dist/index.mjs CHANGED
@@ -1,6 +1,9 @@
1
1
  //#region src/error.ts
2
2
  var LoopwiseError = class extends Error {
3
3
  code;
4
+ /** The site this client was configured for. */
5
+ site;
6
+ /** @deprecated Renamed to `site`. Holds the same resolved value. Removed in 0.2.0. */
4
7
  tenant;
5
8
  endpoint;
6
9
  graphqlErrors;
@@ -9,7 +12,9 @@ var LoopwiseError = class extends Error {
9
12
  super(message);
10
13
  this.name = "LoopwiseError";
11
14
  this.code = code;
12
- this.tenant = detail.tenant;
15
+ const site = detail.site ?? detail.tenant;
16
+ this.site = site;
17
+ this.tenant = site;
13
18
  this.endpoint = detail.endpoint;
14
19
  this.graphqlErrors = detail.graphqlErrors;
15
20
  this.requestId = detail.requestId;
@@ -29,20 +34,21 @@ var LoopwiseClient = class {
29
34
  config;
30
35
  endpoint;
31
36
  constructor(config) {
32
- const tenant = config.tenant ?? void 0;
33
- if (detectRuntime() !== "browser" && !tenant) throw new LoopwiseError("CONFIG", "tenant is required outside browser runtime. Pass it to createClient({ tenant }).");
37
+ const site = config.site ?? config.tenant;
38
+ if (detectRuntime() !== "browser" && !site) throw new LoopwiseError("CONFIG", "site is required outside browser runtime. Pass it to createClient({ site }).");
34
39
  this.config = {
35
40
  ...config,
36
- tenant
41
+ site,
42
+ tenant: site
37
43
  };
38
44
  this.endpoint = config.endpoint ?? this.defaultEndpoint();
39
45
  }
40
46
  defaultEndpoint() {
41
- const tenant = this.config.tenant;
42
- if (detectRuntime() === "browser" && !tenant) return "/api/graphql";
43
- const t = tenant;
44
- if (t.includes(".")) return `https://${t}/api/graphql`;
45
- return `https://${t}.kaik.io/api/graphql`;
47
+ const site = this.config.site;
48
+ if (detectRuntime() === "browser" && !site) return "/api/graphql";
49
+ const s = site;
50
+ if (s.includes(".")) return `https://${s}/api/graphql`;
51
+ return `https://${s}.kaik.io/api/graphql`;
46
52
  }
47
53
  async execute(descriptor, options = {}) {
48
54
  const document = renderSingleDocument(descriptor);
@@ -115,26 +121,26 @@ var LoopwiseClient = class {
115
121
  });
116
122
  } catch (err) {
117
123
  throw new LoopwiseError("NETWORK", `Network error: ${err.message}`, {
118
- tenant: this.config.tenant,
124
+ site: this.config.site,
119
125
  endpoint: this.endpoint
120
126
  });
121
127
  }
122
128
  if (response.status === 401 || response.status === 403) throw new LoopwiseError("AUTH", `Auth failed (${response.status})`, {
123
- tenant: this.config.tenant,
129
+ site: this.config.site,
124
130
  endpoint: this.endpoint,
125
131
  requestId: response.headers.get("x-request-id") ?? void 0
126
132
  });
127
133
  if (response.status === 429) throw new LoopwiseError("RATE_LIMIT", "Rate limited", {
128
- tenant: this.config.tenant,
134
+ site: this.config.site,
129
135
  endpoint: this.endpoint
130
136
  });
131
137
  if (!response.ok) throw new LoopwiseError("NETWORK", `Upstream ${response.status}`, {
132
- tenant: this.config.tenant,
138
+ site: this.config.site,
133
139
  endpoint: this.endpoint
134
140
  });
135
141
  const body = await response.json();
136
142
  if (body.errors?.length) throw new LoopwiseError("GRAPHQL", body.errors[0]?.message ?? "GraphQL error", {
137
- tenant: this.config.tenant,
143
+ site: this.config.site,
138
144
  endpoint: this.endpoint,
139
145
  graphqlErrors: body.errors
140
146
  });
@@ -319,7 +325,7 @@ const DEFAULT_GET_FIELDS$2 = {
319
325
  function tenantVars$2(client) {
320
326
  return { subdomain: {
321
327
  type: "String!",
322
- value: client.config.tenant ?? ""
328
+ value: client.config.site ?? ""
323
329
  } };
324
330
  }
325
331
  var CoursesResource = class {
@@ -328,7 +334,7 @@ var CoursesResource = class {
328
334
  this.client = client;
329
335
  }
330
336
  /**
331
- * List courses for the current tenant.
337
+ * List published courses.
332
338
  * @example
333
339
  * const { items, pageInfo } = await loopwise.courses.list({ limit: 20 })
334
340
  * @example
@@ -433,7 +439,7 @@ const DEFAULT_GET_FIELDS$1 = {
433
439
  function tenantVars$1(client) {
434
440
  return { subdomain: {
435
441
  type: "String!",
436
- value: client.config.tenant ?? ""
442
+ value: client.config.site ?? ""
437
443
  } };
438
444
  }
439
445
  var OrdersResource = class {
@@ -445,9 +451,8 @@ var OrdersResource = class {
445
451
  * Look up a PII-safe order projection by id (the value the storefront
446
452
  * appends to a merchant's `checkoutSuccessUrl` as `?tradeNo=...`).
447
453
  *
448
- * Returns `null`-shaped result via the GraphQL `null` payload when the
449
- * id doesn't resolve in the current tenant — does NOT throw. Use the
450
- * returned `status` to gate Meta Pixel `Purchase` events:
454
+ * Returns `null` when the id doesn't resolve on this site — does NOT
455
+ * throw. Use the returned `status` to gate Meta Pixel `Purchase` events:
451
456
  *
452
457
  * @example
453
458
  * const id = new URLSearchParams(location.search).get('tradeNo');
@@ -563,7 +568,7 @@ const DEFAULT_GET_FIELDS = {
563
568
  function tenantVars(client) {
564
569
  return { subdomain: {
565
570
  type: "String!",
566
- value: client.config.tenant ?? ""
571
+ value: client.config.site ?? ""
567
572
  } };
568
573
  }
569
574
  var PostsResource = class {
@@ -572,7 +577,7 @@ var PostsResource = class {
572
577
  this.client = client;
573
578
  }
574
579
  /**
575
- * List published posts for the current tenant, ordered by publishedAt desc.
580
+ * List published posts, ordered by publishedAt desc.
576
581
  * @example
577
582
  * const { items, pageInfo } = await loopwise.posts.list({ limit: 10 })
578
583
  * @example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopwise/sdk",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "Loopwise platform SDK — agent-friendly headless API client",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,7 +37,8 @@
37
37
  "files": [
38
38
  "dist/*.mjs",
39
39
  "dist/*.d.mts",
40
- "README.md"
40
+ "README.md",
41
+ "CHANGELOG.md"
41
42
  ],
42
43
  "scripts": {
43
44
  "build": "tsdown",