@narrative.io/data-collaboration-sdk-ts 4.2.0 → 4.3.1

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 CHANGED
@@ -13,6 +13,7 @@ The official TypeScript SDK for the Narrative.io Data Collaboration Platform. Th
13
13
  - [Configuration](#configuration)
14
14
  - [TypeScript Support](#typescript-support)
15
15
  - [Error Handling](#error-handling)
16
+ - [Testing with Mock Handlers](#testing-with-mock-handlers)
16
17
  - [API Reference](#api-reference)
17
18
  - [Contributing](#contributing)
18
19
  - [License](#license)
@@ -385,6 +386,75 @@ const narrative = new NarrativeApi({
385
386
  });
386
387
  ```
387
388
 
389
+ ## Testing with Mock Handlers
390
+
391
+ The `testing` subpath ships [MSW](https://mswjs.io) request handlers generated against the
392
+ same OpenAPI spec this SDK is checked against, so your mocks can't drift from what the client
393
+ actually puts on the wire.
394
+
395
+ `msw` and `openapi-msw` are optional peer dependencies — install both to use this entry point:
396
+
397
+ ```bash
398
+ bun add -d msw openapi-msw
399
+ ```
400
+
401
+ ```typescript
402
+ import { setupServer } from 'msw/node';
403
+ import { createHandlers, createDataPlane } from '@narrative.io/data-collaboration-sdk-ts/testing';
404
+ import { DataPlaneApi } from '@narrative.io/data-collaboration-sdk-ts';
405
+
406
+ const server = setupServer(
407
+ ...createHandlers({
408
+ dataPlanes: [createDataPlane({ display_name: 'Primary' })],
409
+ }),
410
+ );
411
+
412
+ beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
413
+ afterAll(() => server.close());
414
+
415
+ it('lists data planes', async () => {
416
+ const { records } = await new DataPlaneApi({ apiKey: 'test' }).getDataPlanes();
417
+ expect(records[0].display_name).toBe('Primary');
418
+ });
419
+ ```
420
+
421
+ `createHandlers` takes three options, all optional:
422
+
423
+ | Option | Default | Purpose |
424
+ | --- | --- | --- |
425
+ | `baseUrl` | `https://api.narrative.io` | Origin the handlers match. Pass the **origin**, not `getBaseUrl()` — that returns a trailing slash. |
426
+ | `auth` | `false` | Reject unauthenticated requests with a 401. See below. |
427
+ | `dataPlanes` | one default record | Seeds the in-memory records the handlers read and write. |
428
+
429
+ Each call gets its own copy of the seed records, so writes in one suite never leak into
430
+ another. Handlers back a small read/write model rather than fixed responses: setting a
431
+ data plane's default compute pool is visible on the next read, and an unknown id returns
432
+ a 404 (which the client surfaces as a thrown `HttpError`).
433
+
434
+ The auth guard is off by default. Turn it on to assert that your code sends credentials —
435
+ it accepts a bearer token, a cookie, or your own predicate, and the same handlers then work
436
+ for both test servers and a browser dev-mode worker:
437
+
438
+ ```typescript
439
+ createHandlers({ auth: { type: 'bearer' } }); // any non-empty token
440
+ createHandlers({ auth: { type: 'bearer', token: 'expected' } }); // that exact token
441
+ createHandlers({ auth: { type: 'cookie', name: 'session' } });
442
+ createHandlers({ auth: ({ request }) => request.headers.has('X-My-Header') });
443
+ ```
444
+
445
+ Coverage currently spans the data-planes endpoints; more modules follow.
446
+
447
+ **Using Jest?** MSW's CommonJS build reaches a few ESM-only packages, so they need to be
448
+ transformed rather than ignored:
449
+
450
+ ```js
451
+ // jest.config.js
452
+ transform: { '\\.[cm]?[jt]sx?$': 'babel-jest' },
453
+ transformIgnorePatterns: [
454
+ '/node_modules/(?!.*(?:openapi-msw|rettime|until-async|@open-draft/deferred-promise)/)',
455
+ ],
456
+ ```
457
+
388
458
  ## API Reference
389
459
 
390
460
  For detailed API documentation, please visit the [Narrative.io API Documentation](https://api.narrative.dev).
@@ -465,6 +535,6 @@ bun add @narrative.io/data-collaboration-sdk-ts # or @<semver>
465
535
 
466
536
  - **Build artifacts not updating:** Ensure `bun run dev` is running in the SDK repo (TypeScript watch), and that the app is linked (or reinstalled from a fresh tarball).
467
537
 
468
- - **Accidental imports from `src/*`:** Consumers should import from the package root only. This SDK publishes `build/**` and defines `main`/`types` (and may include an `exports` map) to prevent `src/*` imports.
538
+ - **Accidental imports from `src/*`:** Consumers should import from the package root only. This SDK publishes `build/**` and defines both `main`/`types` and an `exports` map, so `src/*` is unreachable. The map keeps the existing `build/*` deep-import paths working: each built module directory has its own entry, because Vite turns off directory/index resolution for any package that declares `exports`.
469
539
 
470
540
  - **Type/version drift (e.g., Zod):** If your app and the SDK use different major versions of a library whose types appear in public APIs, align versions or declare that library as a `peerDependency` in the SDK.
@@ -38,10 +38,11 @@ export interface SnowflakePlatform {
38
38
  account_name: string;
39
39
  organization_name: string;
40
40
  /**
41
- * Optional: the platform can fail to parse a region when provisioning the
42
- * Snowflake Native App. Informational only.
41
+ * The platform can fail to parse a region when provisioning the Snowflake
42
+ * Native App, so this is informational only. Responses always carry the key,
43
+ * with an explicit `null` when there is no region.
43
44
  */
44
- region?: DataPlaneRegion;
45
+ region?: DataPlaneRegion | null;
45
46
  }
46
47
  /** Platform details returned for a data plane the calling company owns. */
47
48
  export type DataPlanePlatform = AwsPlatform | SnowflakePlatform;
@@ -51,7 +52,8 @@ export interface AwsSharedPlatform {
51
52
  }
52
53
  export interface SnowflakeSharedPlatform {
53
54
  type: "platform_shared_snowflake";
54
- region?: DataPlaneRegion;
55
+ /** Nullable for the same reason as {@link SnowflakePlatform.region}. */
56
+ region?: DataPlaneRegion | null;
55
57
  }
56
58
  /**
57
59
  * Platform details returned for a data plane shared with you. Account
@@ -84,11 +86,21 @@ export interface DataPlaneOwnedResponse {
84
86
  status: DataPlaneStatus;
85
87
  updated_at: string;
86
88
  collaborators: DataPlaneCollaborators;
89
+ /**
90
+ * Always present, empty when the plane has no pools: the backend field is a
91
+ * plain `List`, never an `Option`.
92
+ */
87
93
  compute_pools: ComputePoolResponse[];
88
- description?: string;
89
- tags?: string[];
90
- last_heartbeat_at?: string;
91
- default_compute_pool_id?: string;
94
+ /**
95
+ * The response encoder does not drop nulls, so each of these four arrives as
96
+ * an explicit `null` when absent rather than being left out. `tags` is `null`
97
+ * for a plane with no tags too — `DataPlaneResponse.apply` maps an empty list
98
+ * to `None` — though a directly constructed response can still send `[]`.
99
+ */
100
+ description?: string | null;
101
+ tags?: string[] | null;
102
+ last_heartbeat_at?: string | null;
103
+ default_compute_pool_id?: string | null;
92
104
  }
93
105
  /**
94
106
  * A data plane shared with the calling company. Omits details like the external
@@ -104,11 +116,13 @@ export interface DataPlaneSharedResponse {
104
116
  platform: DataPlaneSharedPlatform;
105
117
  status: DataPlaneStatus;
106
118
  updated_at: string;
119
+ /** Required for the same reason as {@link DataPlaneOwnedResponse.compute_pools}. */
107
120
  compute_pools: ComputePoolResponse[];
108
- description?: string;
109
- tags?: string[];
110
- last_heartbeat_at?: string;
111
- default_compute_pool_id?: string;
121
+ /** Nullable for the same reason as on {@link DataPlaneOwnedResponse}. */
122
+ description?: string | null;
123
+ tags?: string[] | null;
124
+ last_heartbeat_at?: string | null;
125
+ default_compute_pool_id?: string | null;
112
126
  }
113
127
  /** Discriminated union of the owned/shared data plane response shapes. */
114
128
  export type DataPlane = DataPlaneOwnedResponse | DataPlaneSharedResponse;