@tailor-platform/sdk 1.80.0 → 1.81.0

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/application-B1_7Wm37.mjs +3 -0
  3. package/dist/{application-C1d52JP9.mjs → application-DDe0er6L.mjs} +149 -20
  4. package/dist/{application-C1d52JP9.mjs.map → application-DDe0er6L.mjs.map} +1 -1
  5. package/dist/cli/index.mjs +13 -28
  6. package/dist/cli/index.mjs.map +1 -1
  7. package/dist/cli/lib.mjs +2 -2
  8. package/dist/completion/zsh-worker.zsh +1 -1
  9. package/dist/configure/index.d.mts +3 -2
  10. package/dist/configure/index.mjs +9 -0
  11. package/dist/configure/index.mjs.map +1 -1
  12. package/dist/configure/services/idp/index.d.mts +1 -1
  13. package/dist/configure/services/idp/permission.d.mts +1 -16
  14. package/dist/configure/services/idp/types.d.mts +5 -1
  15. package/dist/configure/services/index.d.mts +3 -2
  16. package/dist/configure/services/resolver/index.d.mts +2 -1
  17. package/dist/configure/services/resolver/permission.d.mts +64 -0
  18. package/dist/configure/services/resolver/resolver.d.mts +14 -2
  19. package/dist/configure/services/tailordb/permission.d.mts +1 -16
  20. package/dist/configure/types/permission-operand.types.d.mts +20 -0
  21. package/dist/{globals-B-n_3uYu.mjs → globals-DUqEetIh.mjs} +18 -2
  22. package/dist/{globals-B-n_3uYu.mjs.map → globals-DUqEetIh.mjs.map} +1 -1
  23. package/dist/logger-B-fqZe2W.mjs +50 -0
  24. package/dist/logger-B-fqZe2W.mjs.map +1 -0
  25. package/dist/{register-typescript-runtime-Bd4bcLbW.mjs → register-typescript-runtime-QAWucsg0.mjs} +40 -9
  26. package/dist/register-typescript-runtime-QAWucsg0.mjs.map +1 -0
  27. package/dist/runtime/globals.d.mts +7 -0
  28. package/dist/runtime/index.d.mts +3 -1
  29. package/dist/runtime/index.mjs +2 -1
  30. package/dist/runtime/logger.d.mts +98 -0
  31. package/dist/runtime/logger.mjs +3 -0
  32. package/dist/{service_pb-BoLM0X4K.mjs → service_pb-BwZRiNug.mjs} +2 -2
  33. package/dist/{service_pb-BoLM0X4K.mjs.map → service_pb-BwZRiNug.mjs.map} +1 -1
  34. package/dist/{service_pb-r1T7kpJc.mjs → service_pb-wRkxUta6.mjs} +1 -1
  35. package/dist/types/auth.generated.d.mts +14 -2
  36. package/dist/types/executor.generated.d.mts +19 -0
  37. package/dist/types/http-adapter.generated.d.mts +5 -0
  38. package/dist/types/idp.generated.d.mts +30 -0
  39. package/dist/types/resolver.generated.d.mts +28 -2
  40. package/dist/types/tailordb.generated.d.mts +15 -1
  41. package/dist/vitest/environment.mjs +1 -1
  42. package/dist/vitest/index.d.mts +4 -2
  43. package/dist/vitest/index.mjs +83 -2
  44. package/dist/vitest/index.mjs.map +1 -1
  45. package/dist/vitest/mock.d.mts +2 -1
  46. package/dist/vitest/mocks/logger.d.mts +45 -0
  47. package/dist/vitest/setup.mjs +1 -1
  48. package/dist/{workspace_resource_pb-Db3fv68L.mjs → workspace_resource_pb-UGK1SSn_.mjs} +7 -2
  49. package/dist/{workspace_resource_pb-Db3fv68L.mjs.map → workspace_resource_pb-UGK1SSn_.mjs.map} +1 -1
  50. package/docs/runtime.md +4 -0
  51. package/docs/services/resolver.md +54 -0
  52. package/docs/testing.md +33 -6
  53. package/package.json +12 -7
  54. package/dist/application-Nlh9BHQf.mjs +0 -3
  55. package/dist/register-typescript-runtime-Bd4bcLbW.mjs.map +0 -1
package/docs/runtime.md CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  context,
21
21
  file,
22
22
  aigateway,
23
+ logger,
23
24
  } from "@tailor-platform/sdk/runtime";
24
25
 
25
26
  const utf8 = iconv.convert(sjisBuffer, "Shift_JIS", "UTF-8");
@@ -38,6 +39,8 @@ const invoker = context.getInvoker();
38
39
  const { metadata } = await file.upload("my-namespace", "Document", "attachment", recordId, bytes);
39
40
 
40
41
  const { url } = await aigateway.get("my-aigateway");
42
+
43
+ logger.info("order processed", { orderId: "o-1", total: 99.5 });
41
44
  ```
42
45
 
43
46
  ## Subpath imports
@@ -83,6 +86,7 @@ The runtime entry re-exports the following namespaces. Detailed signatures, para
83
86
  - `context` — execution context (`getInvoker`)
84
87
  - `file` — `tailordb.file` BLOB API (`upload`, `download`, `downloadAsBase64`, `delete`, `getMetadata`, `downloadStream`, `uploadStream`, `openDownloadStream` _(deprecated)_)
85
88
  - `aigateway` — AI Gateway URL resolution (`get`)
89
+ - `logger` — structured logging with severity and attributes; the message is written to standard output, the full entry with its attributes is exported over OpenTelemetry where the attributes are queryable (`debug`, `info`, `warn`, `error`, `setAttributes`)
86
90
 
87
91
  ## Testing
88
92
 
@@ -350,6 +350,60 @@ createResolver({
350
350
  });
351
351
  ```
352
352
 
353
+ ## Permissions
354
+
355
+ ### Access Requirement (`permission`)
356
+
357
+ By default, a resolver with no in-body check is reachable by an anonymous (unauthenticated) caller. Set `permission` to reject callers that don't match a condition, evaluated before `body` runs:
358
+
359
+ ```typescript
360
+ import { createResolver, t } from "@tailor-platform/sdk";
361
+
362
+ export default createResolver({
363
+ name: "getMyOrders",
364
+ operation: "query",
365
+ permission: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
366
+ output: t.object({ count: t.int() }),
367
+ body: async (context) => {
368
+ // context.user is guaranteed to be an authenticated caller here
369
+ return { count: 0 };
370
+ },
371
+ });
372
+ ```
373
+
374
+ `permission` uses the same `conditions`/`permit` notation as TailorDB's `.permission()` — an array of policies, restricted to `user` operands (a resolver has no associated record to compare against) with equality (`=`/`!=`) comparisons:
375
+
376
+ - `{ user: "_loggedIn" }` — whether the caller is authenticated
377
+ - `{ user: "id" }` — the caller's user ID
378
+ - `{ user: "someAttribute" }` — any string or boolean attribute enabled in `auth.userProfile.attributes` (or `auth.machineUserAttributes` for machine users); array attributes aren't supported, since conditions only compare against a single string/boolean value
379
+
380
+ Multiple conditions within the same policy's `conditions` array are combined with AND. `permit` is required, with no implicit default. At least one `permit: true` policy is required: `permission` is an allow-list, denied by default and granted only by a matching `permit: true` policy. This lets you express different eligibility paths, e.g. allowing machine-user callers unconditionally while gating regular users behind a role check:
381
+
382
+ ```typescript
383
+ permission: [
384
+ { conditions: [[{ user: "isServiceAccount" }, "=", true]], permit: true },
385
+ { conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true },
386
+ ],
387
+ ```
388
+
389
+ A `permit: false` policy always denies matching callers, even ones another policy would otherwise allow. Combine it with a `permit: true` policy to carve out an explicit exception, e.g. granting access broadly but rejecting one banned role:
390
+
391
+ ```typescript
392
+ permission: [
393
+ { conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true },
394
+ { conditions: [[{ user: "role" }, "=", "BANNED"]], permit: false },
395
+ ],
396
+ ```
397
+
398
+ A policy array made up of only `permit: false` policies is rejected: since none of its conditions apply to a caller presenting no user attributes at all, it wouldn't actually keep anyone out who's willing to drop their credentials, so it can't stand in for an allow-list.
399
+
400
+ Besides a policy array, `permission` also accepts:
401
+
402
+ - `"allowAnonymous"` — explicitly documents that anonymous callers are allowed. Behaves the same as omitting `permission`, but records the decision so it isn't mistaken for an oversight.
403
+ - Omitted (default) — unchanged: anonymous callers can still reach the resolver.
404
+
405
+ This check is based on `context.user`, the original caller, so it still applies even when `authInvoker` swaps in a machine user for database access.
406
+
353
407
  ## Authentication
354
408
 
355
409
  Specify an `authInvoker` to execute the resolver with machine user credentials. Pass the machine user name as a plain string — it is type-narrowed to the names you defined in your auth config:
package/docs/testing.md CHANGED
@@ -23,7 +23,7 @@ Platform API mocks under `@tailor-platform/sdk/vitest` (for use with the [`tailo
23
23
 
24
24
  - `mockTailordb` — TailorDB query stubs and call recording
25
25
  - `mockWorkflow` — `tailor.workflow` job / wait / resolve mocks
26
- - `mockSecretmanager`, `mockAuthconnection`, `mockIdp`, `mockFile`, `mockIconv`, `mockAigateway` — corresponding platform API mocks
26
+ - `mockSecretmanager`, `mockAuthconnection`, `mockIdp`, `mockFile`, `mockIconv`, `mockAigateway`, `mockLogger` — corresponding platform API mocks
27
27
 
28
28
  For tighter alignment with the production runtime — Node.js module blocking, Web-only globals, and platform API mocks — pair the resolver helpers with the [`tailor-runtime` Vitest environment](#runtime-environment-emulation-beta) below.
29
29
 
@@ -60,7 +60,7 @@ export default defineConfig({
60
60
 
61
61
  ### Acquiring mocks with `using`
62
62
 
63
- Each mock controller (`mockTailordb`, `mockWorkflow`, `mockSecretmanager`, `mockAuthconnection`, `mockIdp`, `mockFile`, `mockIconv`, `mockAigateway`) is a **factory function**. Acquire it inside a test with a [`using` declaration](https://github.com/tc39/proposal-explicit-resource-management) — its state is reset automatically when the test scope exits, so you no longer need `beforeEach(() => mock.reset())`:
63
+ Each mock controller (`mockTailordb`, `mockWorkflow`, `mockSecretmanager`, `mockAuthconnection`, `mockIdp`, `mockFile`, `mockIconv`, `mockAigateway`, `mockLogger`) is a **factory function**. Acquire it inside a test with a [`using` declaration](https://github.com/tc39/proposal-explicit-resource-management) — its state is reset automatically when the test scope exits, so you no longer need `beforeEach(() => mock.reset())`:
64
64
 
65
65
  ```typescript
66
66
  import { mockTailordb } from "@tailor-platform/sdk/vitest";
@@ -328,6 +328,27 @@ test("resolves an AI Gateway URL", async () => {
328
328
 
329
329
  Calling `get` for a name that has not been registered throws. `setUrls` remains available when replacing the complete URL fixture.
330
330
 
331
+ ### Logger Mock
332
+
333
+ Each method is a `vi.fn`, so assert on it directly. `calls` returns the emitted `debug`/`info`/`warn`/`error` entries in order.
334
+
335
+ ```typescript
336
+ import { mockLogger } from "@tailor-platform/sdk/vitest";
337
+
338
+ test("logs the processed order", () => {
339
+ using logger = mockLogger();
340
+
341
+ tailor.logger.info("order processed", { orderId: "o-1" });
342
+
343
+ expect(logger.info).toHaveBeenCalledWith("order processed", { orderId: "o-1" });
344
+ expect(logger.calls).toEqual([
345
+ { severity: "info", message: "order processed", attributes: { orderId: "o-1" } },
346
+ ]);
347
+ });
348
+ ```
349
+
350
+ Without an explicit `mockLogger()`, `tailor.logger.*` calls are no-ops in the `tailor-runtime` environment (they neither throw nor record).
351
+
331
352
  ### Loading Secrets from Config
332
353
 
333
354
  Pass a config path to load `defineSecretManager()` values into the mock:
@@ -414,15 +435,17 @@ Stub the global `tailordb.Client` and queue raw query results in order. Best for
414
435
 
415
436
  > If you are running with the [`tailor-runtime` Vitest environment](#runtime-environment-emulation-beta), acquire `using db = mockTailordb()` to install and drive the mock `tailordb.Client` instead of `vi.stubGlobal()`.
416
437
 
438
+ > The example below uses `aroundAll` / `aroundEach`, which require Vitest ≥ 4.1.
439
+
417
440
  ```typescript
418
441
  import { unauthenticatedTailorUser } from "@tailor-platform/sdk/test";
419
- import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from "vitest";
442
+ import { aroundAll, aroundEach, describe, expect, test, vi } from "vitest";
420
443
  import resolver from "../src/resolver/incrementUserAge";
421
444
 
422
445
  describe("incrementUserAge resolver", () => {
423
446
  const mockQueryObject = vi.fn();
424
447
 
425
- beforeAll(() => {
448
+ aroundAll(async (runSuite) => {
426
449
  vi.stubGlobal("tailordb", {
427
450
  Client: vi.fn(
428
451
  class {
@@ -432,9 +455,13 @@ describe("incrementUserAge resolver", () => {
432
455
  },
433
456
  ),
434
457
  });
458
+ await runSuite();
459
+ vi.unstubAllGlobals();
460
+ });
461
+ aroundEach(async (runTest) => {
462
+ await runTest();
463
+ mockQueryObject.mockReset();
435
464
  });
436
- afterAll(() => vi.unstubAllGlobals());
437
- afterEach(() => mockQueryObject.mockReset());
438
465
 
439
466
  test("increments age inside a transaction", async () => {
440
467
  // BEGIN → SELECT → UPDATE → COMMIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk",
3
- "version": "1.80.0",
3
+ "version": "1.81.0",
4
4
  "description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -141,6 +141,11 @@
141
141
  "types": "./dist/runtime/aigateway.d.mts",
142
142
  "import": "./dist/runtime/aigateway.mjs",
143
143
  "default": "./dist/runtime/aigateway.mjs"
144
+ },
145
+ "./runtime/logger": {
146
+ "types": "./dist/runtime/logger.d.mts",
147
+ "import": "./dist/runtime/logger.mjs",
148
+ "default": "./dist/runtime/logger.mjs"
144
149
  }
145
150
  },
146
151
  "dependencies": {
@@ -179,13 +184,13 @@
179
184
  "madge": "8.0.0",
180
185
  "mime-types": "3.0.2",
181
186
  "open": "11.0.0",
182
- "oxc-parser": "0.139.0",
187
+ "oxc-parser": "0.140.0",
183
188
  "p-limit": "7.3.0",
184
189
  "pathe": "2.0.3",
185
190
  "pgsql-ast-parser": "12.0.2",
186
191
  "pkg-types": "2.3.1",
187
192
  "politty": "0.11.2",
188
- "rolldown": "1.1.5",
193
+ "rolldown": "1.2.0",
189
194
  "semver": "7.8.5",
190
195
  "sql-highlight": "6.1.0",
191
196
  "std-env": "4.2.0",
@@ -205,14 +210,14 @@
205
210
  "@types/semver": "7.7.1",
206
211
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
207
212
  "@vitest/coverage-v8": "4.1.10",
208
- "oxfmt": "0.58.0",
209
- "oxlint": "1.73.0",
213
+ "oxfmt": "0.59.0",
214
+ "oxlint": "1.74.0",
210
215
  "oxlint-tsgolint": "0.25.0",
211
216
  "sonda": "0.14.0",
212
- "tsdown": "0.22.7",
217
+ "tsdown": "0.22.9",
213
218
  "typescript": "6.0.3",
214
219
  "vitest": "4.1.10",
215
- "zinfer": "0.2.5",
220
+ "zinfer": "0.2.7",
216
221
  "@tailor-platform/tailor-proto": "^0.0.0"
217
222
  },
218
223
  "peerDependencies": {
@@ -1,3 +0,0 @@
1
- import { n as generatePluginFilesIfNeeded, r as loadApplication, t as defineApplication } from "./application-C1d52JP9.mjs";
2
-
3
- export { defineApplication, generatePluginFilesIfNeeded };