@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.
- package/CHANGELOG.md +28 -0
- package/dist/application-B1_7Wm37.mjs +3 -0
- package/dist/{application-C1d52JP9.mjs → application-DDe0er6L.mjs} +149 -20
- package/dist/{application-C1d52JP9.mjs.map → application-DDe0er6L.mjs.map} +1 -1
- package/dist/cli/index.mjs +13 -28
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.mjs +2 -2
- package/dist/completion/zsh-worker.zsh +1 -1
- package/dist/configure/index.d.mts +3 -2
- package/dist/configure/index.mjs +9 -0
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/idp/index.d.mts +1 -1
- package/dist/configure/services/idp/permission.d.mts +1 -16
- package/dist/configure/services/idp/types.d.mts +5 -1
- package/dist/configure/services/index.d.mts +3 -2
- package/dist/configure/services/resolver/index.d.mts +2 -1
- package/dist/configure/services/resolver/permission.d.mts +64 -0
- package/dist/configure/services/resolver/resolver.d.mts +14 -2
- package/dist/configure/services/tailordb/permission.d.mts +1 -16
- package/dist/configure/types/permission-operand.types.d.mts +20 -0
- package/dist/{globals-B-n_3uYu.mjs → globals-DUqEetIh.mjs} +18 -2
- package/dist/{globals-B-n_3uYu.mjs.map → globals-DUqEetIh.mjs.map} +1 -1
- package/dist/logger-B-fqZe2W.mjs +50 -0
- package/dist/logger-B-fqZe2W.mjs.map +1 -0
- package/dist/{register-typescript-runtime-Bd4bcLbW.mjs → register-typescript-runtime-QAWucsg0.mjs} +40 -9
- package/dist/register-typescript-runtime-QAWucsg0.mjs.map +1 -0
- package/dist/runtime/globals.d.mts +7 -0
- package/dist/runtime/index.d.mts +3 -1
- package/dist/runtime/index.mjs +2 -1
- package/dist/runtime/logger.d.mts +98 -0
- package/dist/runtime/logger.mjs +3 -0
- package/dist/{service_pb-BoLM0X4K.mjs → service_pb-BwZRiNug.mjs} +2 -2
- package/dist/{service_pb-BoLM0X4K.mjs.map → service_pb-BwZRiNug.mjs.map} +1 -1
- package/dist/{service_pb-r1T7kpJc.mjs → service_pb-wRkxUta6.mjs} +1 -1
- package/dist/types/auth.generated.d.mts +14 -2
- package/dist/types/executor.generated.d.mts +19 -0
- package/dist/types/http-adapter.generated.d.mts +5 -0
- package/dist/types/idp.generated.d.mts +30 -0
- package/dist/types/resolver.generated.d.mts +28 -2
- package/dist/types/tailordb.generated.d.mts +15 -1
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/index.d.mts +4 -2
- package/dist/vitest/index.mjs +83 -2
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock.d.mts +2 -1
- package/dist/vitest/mocks/logger.d.mts +45 -0
- package/dist/vitest/setup.mjs +1 -1
- package/dist/{workspace_resource_pb-Db3fv68L.mjs → workspace_resource_pb-UGK1SSn_.mjs} +7 -2
- package/dist/{workspace_resource_pb-Db3fv68L.mjs.map → workspace_resource_pb-UGK1SSn_.mjs.map} +1 -1
- package/docs/runtime.md +4 -0
- package/docs/services/resolver.md +54 -0
- package/docs/testing.md +33 -6
- package/package.json +12 -7
- package/dist/application-Nlh9BHQf.mjs +0 -3
- 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 {
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
209
|
-
"oxlint": "1.
|
|
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.
|
|
217
|
+
"tsdown": "0.22.9",
|
|
213
218
|
"typescript": "6.0.3",
|
|
214
219
|
"vitest": "4.1.10",
|
|
215
|
-
"zinfer": "0.2.
|
|
220
|
+
"zinfer": "0.2.7",
|
|
216
221
|
"@tailor-platform/tailor-proto": "^0.0.0"
|
|
217
222
|
},
|
|
218
223
|
"peerDependencies": {
|