@tailor-platform/create-sdk 1.45.2 → 1.47.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 (36) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +2 -2
  3. package/templates/executor/package.json +2 -2
  4. package/templates/executor/src/executor/onUserCreated.test.ts +30 -0
  5. package/templates/executor/src/executor/shared.test.ts +5 -21
  6. package/templates/executor/tailor.d.ts +1 -1
  7. package/templates/executor/vitest.config.ts +4 -0
  8. package/templates/generators/package.json +2 -2
  9. package/templates/generators/src/resolver/getProduct.test.ts +25 -49
  10. package/templates/generators/tailor.d.ts +1 -1
  11. package/templates/generators/vitest.config.ts +4 -0
  12. package/templates/hello-world/package.json +2 -2
  13. package/templates/hello-world/tailor.d.ts +1 -1
  14. package/templates/inventory-management/package.json +2 -2
  15. package/templates/inventory-management/tailor.d.ts +1 -1
  16. package/templates/multi-application/package.json +3 -3
  17. package/templates/resolver/README.md +1 -1
  18. package/templates/resolver/package.json +2 -4
  19. package/templates/resolver/src/resolver/incrementUserAge.test.ts +43 -0
  20. package/templates/resolver/tailor.d.ts +1 -1
  21. package/templates/resolver/vitest.config.ts +4 -6
  22. package/templates/static-web-site/package.json +2 -2
  23. package/templates/static-web-site/tailor.d.ts +1 -1
  24. package/templates/tailordb/package.json +2 -2
  25. package/templates/tailordb/tailor.d.ts +1 -1
  26. package/templates/tailordb/vitest.config.ts +4 -0
  27. package/templates/workflow/e2e/globalSetup.ts +2 -2
  28. package/templates/workflow/package.json +2 -4
  29. package/templates/workflow/src/resolver/resolveApproval.test.ts +15 -18
  30. package/templates/workflow/src/workflow/approval.test.ts +12 -18
  31. package/templates/workflow/tailor.d.ts +1 -1
  32. package/templates/workflow/vitest.config.ts +5 -6
  33. package/templates/resolver/src/resolver/queryUser.test.ts +0 -64
  34. package/templates/resolver/tests/bundled.test.ts +0 -98
  35. package/templates/workflow/tests/bundled.test.ts +0 -174
  36. /package/templates/resolver/src/resolver/{queryUser.ts → incrementUserAge.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @tailor-platform/create-sdk
2
2
 
3
+ ## 1.47.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1115](https://github.com/tailor-platform/sdk/pull/1115) [`8dd619e`](https://github.com/tailor-platform/sdk/commit/8dd619e9c58f4662b117bbd968ecf9528d688fe4) Thanks [@toiroakr](https://github.com/toiroakr)! - Add `@tailor-platform/sdk/vitest` (beta) — a Vitest plugin and environment that emulates the Tailor Platform function runtime locally. Catches `node:*` imports and Node.js globals usage that would fail at deploy time, and provides mock control objects (`tailordbMock`, `workflowMock`, `secretmanagerMock`, `authconnectionMock`, `idpMock`, `fileMock`, `iconvMock`) for all platform APIs with response configuration and call recording.
8
+
9
+ Revamp `packages/sdk/docs/testing.md` into a 2-layer model (Unit Tests / E2E Tests). The previous structure split Unit, Bundled, and Workflow tests across overlapping sections and contained broken vitest imports and references to a non-existent `--template testing`. The new docs cover testing resolvers (simple, with TailorDB mocks, with DI, and with wait points) and workflow jobs (simple, with `triggerJobFunction` mocks, with wait-point mocks, and full-workflow integration), all anchored on the actual `resolver` and `workflow` templates.
10
+
11
+ Mark `createImportMain` and `setupInvokerMock` from `@tailor-platform/sdk/test` as `@deprecated`. `createImportMain` is an SDK-internal helper for verifying bundled output; applications should test their TypeScript source directly (unit) and verify deployed behavior via E2E. `setupInvokerMock` is superseded by the `tailor-runtime` Vitest environment, where bundled tests can drive the invoker via `vi.spyOn(globalThis.tailor.context, "getInvoker").mockReturnValue(...)` and unit tests can pass `invoker` directly to `.body()`. Both exports remain in place for now to avoid a breaking change and will be removed in a future release.
12
+
13
+ Remove the broken `tests/bundled.test.ts` from the `resolver` and `workflow` templates along with the related `bundled` vitest project and `test:bundled` / `test:bundled:prepare` scripts. These tests were not exercised by CI and had drifted out of sync with the SDK, producing failures on a fresh scaffold.
14
+
15
+ Fix a broken anchor in `docs/services/workflow.md` that pointed at the removed `#testing-wait-points` heading; it now links to `../testing.md#jobs-that-wait-on-approval` to match the new testing docs structure.
16
+
17
+ ## 1.46.0
18
+
3
19
  ## 1.45.2
4
20
 
5
21
  ## 1.45.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/create-sdk",
3
- "version": "1.45.2",
3
+ "version": "1.47.0",
4
4
  "description": "A CLI tool to quickly create a new Tailor Platform SDK project",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "type": "module",
20
20
  "dependencies": {
21
- "@clack/prompts": "1.2.0",
21
+ "@clack/prompts": "1.3.0",
22
22
  "execa": "9.6.1",
23
23
  "picocolors": "1.1.1",
24
24
  "pkg-types": "2.3.1",
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "generate": "tailor-sdk generate",
7
- "deploy": "tailor-sdk apply",
7
+ "deploy": "tailor-sdk deploy",
8
8
  "test": "vitest --project unit",
9
9
  "test:unit": "vitest --project unit",
10
10
  "format": "oxfmt --write .",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@eslint/js": "10.0.1",
18
- "@tailor-platform/sdk": "1.45.2",
18
+ "@tailor-platform/sdk": "1.47.0",
19
19
  "@types/node": "24.12.2",
20
20
  "eslint": "10.3.0",
21
21
  "eslint-plugin-oxlint": "1.61.0",
@@ -0,0 +1,30 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import onUserCreated from "./onUserCreated";
3
+ import * as shared from "./shared";
4
+
5
+ describe("onUserCreated executor", () => {
6
+ test("creates an audit log with the new user's name and email", async () => {
7
+ const createAuditLog = vi.spyOn(shared, "createAuditLog").mockResolvedValue(undefined);
8
+
9
+ if (onUserCreated.operation.kind !== "function") {
10
+ throw new Error("expected function operation");
11
+ }
12
+ await onUserCreated.operation.body({
13
+ newRecord: {
14
+ id: "user-1",
15
+ name: "Alice",
16
+ email: "alice@example.com",
17
+ role: "ADMIN",
18
+ createdAt: "2025-01-01T00:00:00Z",
19
+ updatedAt: "2025-01-01T00:00:00Z",
20
+ },
21
+ });
22
+
23
+ expect(createAuditLog).toHaveBeenCalledExactlyOnceWith({
24
+ action: "USER_CREATED",
25
+ entityType: "User",
26
+ entityId: "user-1",
27
+ message: "Admin user created: Alice (alice@example.com)",
28
+ });
29
+ });
30
+ });
@@ -1,29 +1,13 @@
1
- import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from "vitest";
1
+ import { tailordbMock } from "@tailor-platform/sdk/vitest";
2
+ import { beforeEach, describe, expect, test } from "vitest";
2
3
  import { createAuditLog } from "./shared";
3
4
 
4
5
  describe("createAuditLog", () => {
5
- const mockQueryObject = vi.fn();
6
- beforeAll(() => {
7
- vi.stubGlobal("tailordb", {
8
- Client: vi.fn(
9
- class {
10
- connect = vi.fn();
11
- end = vi.fn();
12
- queryObject = mockQueryObject;
13
- },
14
- ),
15
- });
16
- });
17
- afterAll(() => {
18
- vi.unstubAllGlobals();
19
- });
20
- afterEach(() => {
21
- mockQueryObject.mockReset();
6
+ beforeEach(() => {
7
+ tailordbMock.reset();
22
8
  });
23
9
 
24
10
  test("inserts audit log record", async () => {
25
- mockQueryObject.mockResolvedValueOnce({});
26
-
27
11
  await createAuditLog({
28
12
  action: "USER_CREATED",
29
13
  entityType: "User",
@@ -31,6 +15,6 @@ describe("createAuditLog", () => {
31
15
  message: "Test audit log",
32
16
  });
33
17
 
34
- expect(mockQueryObject).toHaveBeenCalledTimes(1);
18
+ expect(tailordbMock.executedQueries).toHaveLength(1);
35
19
  });
36
20
  });
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -1,12 +1,16 @@
1
1
  import { defineConfig } from "vitest/config";
2
+ import { tailorRuntime } from "@tailor-platform/sdk/vitest";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [tailorRuntime()],
4
6
  test: {
5
7
  watch: false,
6
8
  projects: [
7
9
  {
10
+ extends: true,
8
11
  test: {
9
12
  name: { label: "unit", color: "blue" },
13
+ environment: "tailor-runtime",
10
14
  include: ["src/**/*.test.ts"],
11
15
  },
12
16
  },
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "generate": "tailor-sdk generate",
7
- "deploy": "tailor-sdk apply",
7
+ "deploy": "tailor-sdk deploy",
8
8
  "test": "vitest --project unit",
9
9
  "test:unit": "vitest --project unit",
10
10
  "format": "oxfmt --write .",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@eslint/js": "10.0.1",
18
- "@tailor-platform/sdk": "1.45.2",
18
+ "@tailor-platform/sdk": "1.47.0",
19
19
  "@types/node": "24.12.2",
20
20
  "eslint": "10.3.0",
21
21
  "eslint-plugin-oxlint": "1.61.0",
@@ -1,47 +1,27 @@
1
1
  import { unauthenticatedTailorUser } from "@tailor-platform/sdk/test";
2
- import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from "vitest";
2
+ import { tailordbMock } from "@tailor-platform/sdk/vitest";
3
+ import { beforeEach, describe, expect, test } from "vitest";
3
4
  import resolver from "./getProduct";
4
5
 
5
6
  describe("getProduct resolver", () => {
6
- const mockQueryObject = vi.fn();
7
- beforeAll(() => {
8
- vi.stubGlobal("tailordb", {
9
- Client: vi.fn(
10
- class {
11
- connect = vi.fn();
12
- end = vi.fn();
13
- queryObject = mockQueryObject;
14
- },
15
- ),
16
- });
17
- });
18
- afterAll(() => {
19
- vi.unstubAllGlobals();
20
- });
21
- afterEach(() => {
22
- mockQueryObject.mockReset();
7
+ beforeEach(() => {
8
+ tailordbMock.reset();
23
9
  });
24
10
 
25
11
  test("returns product with category", async () => {
26
12
  // Select product
27
- mockQueryObject.mockResolvedValueOnce({
28
- rows: [
29
- {
30
- id: "product-1",
31
- name: "Widget",
32
- price: 9.99,
33
- status: "ACTIVE",
34
- categoryId: "cat-1",
35
- description: null,
36
- createdAt: new Date(),
37
- updatedAt: null,
38
- },
39
- ],
13
+ tailordbMock.enqueueResult({
14
+ id: "product-1",
15
+ name: "Widget",
16
+ price: 9.99,
17
+ status: "ACTIVE",
18
+ categoryId: "cat-1",
19
+ description: null,
20
+ createdAt: new Date(),
21
+ updatedAt: null,
40
22
  });
41
23
  // Select category
42
- mockQueryObject.mockResolvedValueOnce({
43
- rows: [{ name: "Gadgets" }],
44
- });
24
+ tailordbMock.enqueueResult({ name: "Gadgets" });
45
25
 
46
26
  const result = await resolver.body({
47
27
  input: { productId: "product-1" },
@@ -55,24 +35,20 @@ describe("getProduct resolver", () => {
55
35
  status: "ACTIVE",
56
36
  categoryName: "Gadgets",
57
37
  });
58
- expect(mockQueryObject).toHaveBeenCalledTimes(2);
38
+ expect(tailordbMock.executedQueries).toHaveLength(2);
59
39
  });
60
40
 
61
41
  test("returns product without category", async () => {
62
42
  // Select product (no categoryId)
63
- mockQueryObject.mockResolvedValueOnce({
64
- rows: [
65
- {
66
- id: "product-2",
67
- name: "Standalone Item",
68
- price: 19.99,
69
- status: "DRAFT",
70
- categoryId: null,
71
- description: null,
72
- createdAt: new Date(),
73
- updatedAt: null,
74
- },
75
- ],
43
+ tailordbMock.enqueueResult({
44
+ id: "product-2",
45
+ name: "Standalone Item",
46
+ price: 19.99,
47
+ status: "DRAFT",
48
+ categoryId: null,
49
+ description: null,
50
+ createdAt: new Date(),
51
+ updatedAt: null,
76
52
  });
77
53
 
78
54
  const result = await resolver.body({
@@ -87,6 +63,6 @@ describe("getProduct resolver", () => {
87
63
  status: "DRAFT",
88
64
  categoryName: null,
89
65
  });
90
- expect(mockQueryObject).toHaveBeenCalledTimes(1);
66
+ expect(tailordbMock.executedQueries).toHaveLength(1);
91
67
  });
92
68
  });
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -1,12 +1,16 @@
1
1
  import { defineConfig } from "vitest/config";
2
+ import { tailorRuntime } from "@tailor-platform/sdk/vitest";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [tailorRuntime()],
4
6
  test: {
5
7
  watch: false,
6
8
  projects: [
7
9
  {
10
+ extends: true,
8
11
  test: {
9
12
  name: { label: "unit", color: "blue" },
13
+ environment: "tailor-runtime",
10
14
  include: ["src/**/*.test.ts"],
11
15
  },
12
16
  },
@@ -3,7 +3,7 @@
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "scripts": {
6
- "deploy": "tailor-sdk apply",
6
+ "deploy": "tailor-sdk deploy",
7
7
  "generate": "tailor-sdk generate",
8
8
  "format": "oxfmt --write .",
9
9
  "format:check": "oxfmt --check .",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "devDependencies": {
15
15
  "@eslint/js": "10.0.1",
16
- "@tailor-platform/sdk": "1.45.2",
16
+ "@tailor-platform/sdk": "1.47.0",
17
17
  "@types/node": "24.12.2",
18
18
  "eslint": "10.3.0",
19
19
  "eslint-plugin-oxlint": "1.61.0",
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {}
@@ -3,7 +3,7 @@
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "scripts": {
6
- "deploy": "tailor-sdk apply",
6
+ "deploy": "tailor-sdk deploy",
7
7
  "generate": "tailor-sdk generate",
8
8
  "format": "oxfmt --write .",
9
9
  "format:check": "oxfmt --check .",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "devDependencies": {
15
15
  "@eslint/js": "10.0.1",
16
- "@tailor-platform/sdk": "1.45.2",
16
+ "@tailor-platform/sdk": "1.47.0",
17
17
  "@types/node": "24.12.2",
18
18
  "eslint": "10.3.0",
19
19
  "eslint-plugin-oxlint": "1.61.0",
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -4,8 +4,8 @@
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "deploy": "pnpm run deploy:user && pnpm run deploy:admin",
7
- "deploy:user": "tailor-sdk apply -c apps/user/tailor.config.ts",
8
- "deploy:admin": "tailor-sdk apply -c apps/admin/tailor.config.ts",
7
+ "deploy:user": "tailor-sdk deploy -c apps/user/tailor.config.ts",
8
+ "deploy:admin": "tailor-sdk deploy -c apps/admin/tailor.config.ts",
9
9
  "format": "oxfmt --write .",
10
10
  "format:check": "oxfmt --check .",
11
11
  "lint": "oxlint --type-aware . && eslint --cache .",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@eslint/js": "10.0.1",
17
- "@tailor-platform/sdk": "1.45.2",
17
+ "@tailor-platform/sdk": "1.47.0",
18
18
  "@types/node": "24.12.2",
19
19
  "eslint": "10.3.0",
20
20
  "eslint-plugin-oxlint": "1.61.0",
@@ -13,7 +13,7 @@ Demonstrates all resolver patterns with comprehensive testing approaches.
13
13
  ## Testing Approaches
14
14
 
15
15
  1. **Direct `body()` call** - Simple resolvers with `unauthenticatedTailorUser`
16
- 2. **Mock `tailordb.Client`** - Database resolvers via `vi.stubGlobal`
16
+ 2. **`tailor-runtime` environment + `tailordbMock`** - Database resolvers via `tailordbMock` from `@tailor-platform/sdk/vitest` (no `vi.stubGlobal` needed)
17
17
  3. **Dependency injection** - Extract `DbOperations` interface for testability
18
18
 
19
19
  ## Getting Started
@@ -4,11 +4,9 @@
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "generate": "tailor-sdk generate",
7
- "deploy": "tailor-sdk apply",
7
+ "deploy": "tailor-sdk deploy",
8
8
  "test": "vitest --project unit",
9
9
  "test:unit": "vitest --project unit",
10
- "test:bundled:prepare": "TAILOR_PLATFORM_SDK_BUILD_ONLY=true tailor-sdk apply -c tailor.config.ts",
11
- "test:bundled": "pnpm test:bundled:prepare && vitest --project bundled",
12
10
  "format": "oxfmt --write .",
13
11
  "format:check": "oxfmt --check .",
14
12
  "lint": "oxlint --type-aware . && eslint --cache .",
@@ -17,7 +15,7 @@
17
15
  },
18
16
  "devDependencies": {
19
17
  "@eslint/js": "10.0.1",
20
- "@tailor-platform/sdk": "1.45.2",
18
+ "@tailor-platform/sdk": "1.47.0",
21
19
  "@types/node": "24.12.2",
22
20
  "eslint": "10.3.0",
23
21
  "eslint-plugin-oxlint": "1.61.0",
@@ -0,0 +1,43 @@
1
+ import { unauthenticatedTailorUser } from "@tailor-platform/sdk/test";
2
+ import { tailordbMock } from "@tailor-platform/sdk/vitest";
3
+ import { beforeEach, describe, expect, test } from "vitest";
4
+ import resolver from "./incrementUserAge";
5
+
6
+ describe("incrementUserAge resolver", () => {
7
+ beforeEach(() => {
8
+ tailordbMock.reset();
9
+ });
10
+
11
+ test("increments user age", async () => {
12
+ tailordbMock.enqueueResults(
13
+ [], // BEGIN
14
+ [{ age: 30 }], // SELECT
15
+ [], // UPDATE
16
+ [], // COMMIT
17
+ );
18
+
19
+ const result = await resolver.body({
20
+ input: { email: "test@example.com" },
21
+ user: unauthenticatedTailorUser,
22
+ env: { appName: "Resolver Template", version: 1 },
23
+ });
24
+ expect(result).toEqual({ oldAge: 30, newAge: 31 });
25
+ expect(tailordbMock.executedQueries).toHaveLength(4);
26
+ });
27
+
28
+ test("throws when user not found", async () => {
29
+ tailordbMock.enqueueResults(
30
+ [], // BEGIN
31
+ [], // SELECT (empty)
32
+ [], // ROLLBACK
33
+ );
34
+
35
+ const result = resolver.body({
36
+ input: { email: "test@example.com" },
37
+ user: unauthenticatedTailorUser,
38
+ env: { appName: "Resolver Template", version: 1 },
39
+ });
40
+ await expect(result).rejects.toThrowError();
41
+ expect(tailordbMock.executedQueries).toHaveLength(3);
42
+ });
43
+ });
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -1,21 +1,19 @@
1
1
  import { defineConfig } from "vitest/config";
2
+ import { tailorRuntime } from "@tailor-platform/sdk/vitest";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [tailorRuntime()],
4
6
  test: {
5
7
  watch: false,
6
8
  projects: [
7
9
  {
10
+ extends: true,
8
11
  test: {
9
12
  name: { label: "unit", color: "blue" },
13
+ environment: "tailor-runtime",
10
14
  include: ["src/**/*.test.ts"],
11
15
  },
12
16
  },
13
- {
14
- test: {
15
- name: { label: "bundled", color: "yellow" },
16
- include: ["tests/**/*.test.ts"],
17
- },
18
- },
19
17
  ],
20
18
  },
21
19
  });
@@ -3,7 +3,7 @@
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "scripts": {
6
- "deploy": "tailor-sdk apply",
6
+ "deploy": "tailor-sdk deploy",
7
7
  "generate": "tailor-sdk generate",
8
8
  "format": "oxfmt --write .",
9
9
  "format:check": "oxfmt --check .",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "devDependencies": {
15
15
  "@eslint/js": "10.0.1",
16
- "@tailor-platform/sdk": "1.45.2",
16
+ "@tailor-platform/sdk": "1.47.0",
17
17
  "@types/node": "24.12.2",
18
18
  "eslint": "10.3.0",
19
19
  "eslint-plugin-oxlint": "1.61.0",
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "generate": "tailor-sdk generate",
7
- "deploy": "tailor-sdk apply",
7
+ "deploy": "tailor-sdk deploy",
8
8
  "test": "vitest --project unit",
9
9
  "test:unit": "vitest --project unit",
10
10
  "format": "oxfmt --write .",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@eslint/js": "10.0.1",
18
- "@tailor-platform/sdk": "1.45.2",
18
+ "@tailor-platform/sdk": "1.47.0",
19
19
  "@types/node": "24.12.2",
20
20
  "eslint": "10.3.0",
21
21
  "eslint-plugin-oxlint": "1.61.0",
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -1,12 +1,16 @@
1
1
  import { defineConfig } from "vitest/config";
2
+ import { tailorRuntime } from "@tailor-platform/sdk/vitest";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [tailorRuntime()],
4
6
  test: {
5
7
  watch: false,
6
8
  projects: [
7
9
  {
10
+ extends: true,
8
11
  test: {
9
12
  name: { label: "unit", color: "blue" },
13
+ environment: "tailor-runtime",
10
14
  include: ["src/**/*.test.ts"],
11
15
  },
12
16
  },
@@ -1,5 +1,5 @@
1
1
  import {
2
- apply,
2
+ deploy,
3
3
  getMachineUserToken,
4
4
  show,
5
5
  createWorkspace,
@@ -31,7 +31,7 @@ async function setupWorkspace(name: string, region: string): Promise<WorkspaceIn
31
31
 
32
32
  async function deployApplication(): Promise<void> {
33
33
  console.log("Deploying application...");
34
- await apply();
34
+ await deploy();
35
35
  console.log("Application deployed successfully.");
36
36
  }
37
37
 
@@ -4,11 +4,9 @@
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "generate": "tailor-sdk generate",
7
- "deploy": "tailor-sdk apply",
7
+ "deploy": "tailor-sdk deploy",
8
8
  "test": "vitest --project unit",
9
9
  "test:unit": "vitest --project unit",
10
- "test:bundled:prepare": "TAILOR_PLATFORM_SDK_BUILD_ONLY=true tailor-sdk apply -c tailor.config.ts",
11
- "test:bundled": "pnpm test:bundled:prepare && vitest --project bundled",
12
10
  "test:e2e": "vitest --project e2e",
13
11
  "format": "oxfmt --write .",
14
12
  "format:check": "oxfmt --check .",
@@ -18,7 +16,7 @@
18
16
  },
19
17
  "devDependencies": {
20
18
  "@eslint/js": "10.0.1",
21
- "@tailor-platform/sdk": "1.45.2",
19
+ "@tailor-platform/sdk": "1.47.0",
22
20
  "@types/node": "24.12.2",
23
21
  "eslint": "10.3.0",
24
22
  "eslint-plugin-oxlint": "1.61.0",
@@ -1,20 +1,20 @@
1
- import { afterEach, describe, expect, test } from "vitest";
2
- import { setupWaitPointMock, unauthenticatedTailorUser } from "@tailor-platform/sdk/test";
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+ import { workflowMock } from "@tailor-platform/sdk/vitest";
3
+ import { unauthenticatedTailorUser } from "@tailor-platform/sdk/test";
3
4
  import resolver from "./resolveApproval";
4
5
 
5
- const TailorGlobal = globalThis as { tailor?: { workflow?: Record<string, unknown> } };
6
-
7
6
  describe("resolveApproval resolver", () => {
8
- afterEach(() => {
9
- delete TailorGlobal.tailor;
7
+ beforeEach(() => {
8
+ workflowMock.reset();
10
9
  });
11
10
 
12
11
  test("resolves approval with approved=true", async () => {
13
- const { resolveCalls } = setupWaitPointMock({
14
- onResolve: (_execId, _key, callback) => {
15
- const result = callback({ message: "Please approve order order-1", orderId: "order-1" });
16
- expect(result).toEqual({ approved: true });
17
- },
12
+ workflowMock.setResolveHandler((_executionId, _key, callback) => {
13
+ const callbackResult = callback({
14
+ message: "Please approve order order-1",
15
+ orderId: "order-1",
16
+ });
17
+ expect(callbackResult).toEqual({ approved: true });
18
18
  });
19
19
 
20
20
  const result = await resolver.body({
@@ -24,16 +24,13 @@ describe("resolveApproval resolver", () => {
24
24
  });
25
25
 
26
26
  expect(result).toEqual({ resolved: true });
27
- expect(resolveCalls).toHaveLength(1);
28
- expect(resolveCalls[0]).toEqual({ executionId: "exec-1", key: "approval" });
27
+ expect(workflowMock.resolveCalls).toEqual([{ executionId: "exec-1", key: "approval" }]);
29
28
  });
30
29
 
31
30
  test("resolves approval with approved=false", async () => {
32
- setupWaitPointMock({
33
- onResolve: (_execId, _key, callback) => {
34
- const result = callback({ message: "Please approve", orderId: "order-2" });
35
- expect(result).toEqual({ approved: false });
36
- },
31
+ workflowMock.setResolveHandler((_executionId, _key, callback) => {
32
+ const callbackResult = callback({ message: "Please approve", orderId: "order-2" });
33
+ expect(callbackResult).toEqual({ approved: false });
37
34
  });
38
35
 
39
36
  const result = await resolver.body({
@@ -1,34 +1,28 @@
1
- import { afterEach, describe, expect, test, vi } from "vitest";
2
- import { setupWaitPointMock } from "@tailor-platform/sdk/test";
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+ import { workflowMock } from "@tailor-platform/sdk/vitest";
3
3
  import workflow, { processWithApproval } from "./approval";
4
4
 
5
- const TailorGlobal = globalThis as { tailor?: { workflow?: Record<string, unknown> } };
6
-
7
5
  describe("approval workflow", () => {
8
- afterEach(() => {
9
- delete TailorGlobal.tailor;
10
- vi.restoreAllMocks();
6
+ beforeEach(() => {
7
+ workflowMock.reset();
11
8
  });
12
9
 
13
10
  test("approved flow returns approved status", async () => {
14
- const { waitCalls } = setupWaitPointMock({
15
- onWait: (_key, _payload) => ({ approved: true }),
16
- });
11
+ workflowMock.setWaitHandler((_key, _payload) => ({ approved: true }));
17
12
 
18
13
  const result = await processWithApproval.body({ orderId: "order-1" }, { env: {} });
19
14
 
20
15
  expect(result).toEqual({ orderId: "order-1", status: "approved" });
21
- expect(waitCalls).toHaveLength(1);
22
- expect(waitCalls[0]).toEqual({
23
- key: "approval",
24
- payload: { message: "Please approve order order-1", orderId: "order-1" },
25
- });
16
+ expect(workflowMock.waitCalls).toEqual([
17
+ {
18
+ key: "approval",
19
+ payload: { message: "Please approve order order-1", orderId: "order-1" },
20
+ },
21
+ ]);
26
22
  });
27
23
 
28
24
  test("rejected flow returns rejected status", async () => {
29
- setupWaitPointMock({
30
- onWait: () => ({ approved: false }),
31
- });
25
+ workflowMock.setWaitHandler({ approved: false });
32
26
 
33
27
  const result = await processWithApproval.body({ orderId: "order-2" }, { env: {} });
34
28
 
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @tailor-platform/sdk
2
2
  // Do not edit this file manually
3
- // Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
3
+ // Regenerated automatically when running 'tailor-sdk deploy' or 'tailor-sdk generate'
4
4
 
5
5
  declare module "@tailor-platform/sdk" {
6
6
  interface AttributeMap {
@@ -1,22 +1,21 @@
1
1
  import { defineConfig } from "vitest/config";
2
+ import { tailorRuntime } from "@tailor-platform/sdk/vitest";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [tailorRuntime()],
4
6
  test: {
5
7
  watch: false,
6
8
  projects: [
7
9
  {
10
+ extends: true,
8
11
  test: {
9
12
  name: { label: "unit", color: "blue" },
13
+ environment: "tailor-runtime",
10
14
  include: ["src/**/*.test.ts"],
11
15
  },
12
16
  },
13
17
  {
14
- test: {
15
- name: { label: "bundled", color: "yellow" },
16
- include: ["tests/**/*.test.ts"],
17
- },
18
- },
19
- {
18
+ extends: true,
20
19
  test: {
21
20
  name: { label: "e2e", color: "green" },
22
21
  include: ["e2e/**/*.test.ts"],
@@ -1,64 +0,0 @@
1
- import { unauthenticatedTailorUser } from "@tailor-platform/sdk/test";
2
- import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from "vitest";
3
- import resolver from "./queryUser";
4
-
5
- describe("incrementUserAge resolver", () => {
6
- const mockQueryObject = vi.fn();
7
- beforeAll(() => {
8
- vi.stubGlobal("tailordb", {
9
- Client: vi.fn(
10
- class {
11
- connect = vi.fn();
12
- end = vi.fn();
13
- queryObject = mockQueryObject;
14
- },
15
- ),
16
- });
17
- });
18
- afterAll(() => {
19
- vi.unstubAllGlobals();
20
- });
21
- afterEach(() => {
22
- mockQueryObject.mockReset();
23
- });
24
-
25
- test("increments user age", async () => {
26
- // 1: Begin transaction
27
- mockQueryObject.mockResolvedValueOnce({});
28
- // 2: Select current age
29
- mockQueryObject.mockResolvedValueOnce({
30
- rows: [{ age: 30 }],
31
- });
32
- // 3: Update age
33
- mockQueryObject.mockResolvedValueOnce({});
34
- // 4: Commit transaction
35
- mockQueryObject.mockResolvedValueOnce({});
36
-
37
- const result = await resolver.body({
38
- input: { email: "test@example.com" },
39
- user: unauthenticatedTailorUser,
40
- env: { appName: "Resolver Template", version: 1 },
41
- });
42
- expect(result).toEqual({ oldAge: 30, newAge: 31 });
43
- expect(mockQueryObject).toHaveBeenCalledTimes(4);
44
- });
45
-
46
- test("throws when user not found", async () => {
47
- // 1: Begin transaction
48
- mockQueryObject.mockResolvedValueOnce({});
49
- // 2: Select current age (no rows returned)
50
- mockQueryObject.mockResolvedValueOnce({
51
- rows: [],
52
- });
53
- // 3: Rollback transaction
54
- mockQueryObject.mockResolvedValueOnce({});
55
-
56
- const result = resolver.body({
57
- input: { email: "test@example.com" },
58
- user: unauthenticatedTailorUser,
59
- env: { appName: "Resolver Template", version: 1 },
60
- });
61
- await expect(result).rejects.toThrowError();
62
- expect(mockQueryObject).toHaveBeenCalledTimes(3);
63
- });
64
- });
@@ -1,98 +0,0 @@
1
- import path from "node:path";
2
- import { createImportMain, setupInvokerMock, setupTailordbMock } from "@tailor-platform/sdk/test";
3
- import { beforeAll, beforeEach, describe, expect, test } from "vitest";
4
-
5
- const outputDir = path.join(__dirname, "../.tailor-sdk");
6
-
7
- describe("bundled resolver execution", () => {
8
- let executedQueries: { query: string; params: unknown[] }[];
9
-
10
- const importMain = createImportMain(outputDir);
11
-
12
- beforeAll(() => {
13
- ({ executedQueries } = setupTailordbMock());
14
- setupInvokerMock(null);
15
- });
16
-
17
- beforeEach(() => {
18
- executedQueries.length = 0;
19
- });
20
-
21
- describe("add resolver", () => {
22
- test("returns sum of inputs", async () => {
23
- const main = await importMain("resolvers/add.js");
24
- const result = await main({ input: { left: 3, right: 4 } });
25
- expect(result).toBe(7);
26
- });
27
- });
28
-
29
- describe("incrementUserAge resolver", () => {
30
- test("increments user age with DB mock", async () => {
31
- setupTailordbMock((query) => {
32
- if (query.includes("SELECT") || query.includes("select")) {
33
- return [{ age: 30 }];
34
- }
35
- return [];
36
- });
37
-
38
- const main = await importMain("resolvers/incrementUserAge.js");
39
- const result = await main({ input: { email: "test@example.com" } });
40
- expect(result).toEqual({ oldAge: 30, newAge: 31 });
41
- });
42
- });
43
-
44
- describe("decrementUserAge resolver", () => {
45
- test("decrements user age with DB mock", async () => {
46
- setupTailordbMock((query) => {
47
- if (query.includes("SELECT") || query.includes("select")) {
48
- return [
49
- {
50
- id: "user-1",
51
- email: "test@example.com",
52
- name: "Test",
53
- age: 30,
54
- createdAt: null,
55
- updatedAt: null,
56
- },
57
- ];
58
- }
59
- return [];
60
- });
61
-
62
- const main = await importMain("resolvers/decrementUserAge.js");
63
- const result = await main({ input: { email: "test@example.com" } });
64
- expect(result).toEqual({ oldAge: 30, newAge: 29 });
65
- });
66
- });
67
-
68
- describe("showUserInfo resolver", () => {
69
- test("returns user info from context", async () => {
70
- const main = await importMain("resolvers/showUserInfo.js");
71
- const result = await main({
72
- user: {
73
- id: "test-id",
74
- type: "machine_user",
75
- workspaceId: "ws-id",
76
- attributes: { role: "admin" },
77
- attributeList: [],
78
- },
79
- });
80
- expect(result).toEqual({
81
- userId: "test-id",
82
- userType: "machine_user",
83
- workspaceId: "ws-id",
84
- });
85
- });
86
- });
87
-
88
- describe("showEnv resolver", () => {
89
- test("returns env values embedded from config", async () => {
90
- const main = await importMain("resolvers/showEnv.js");
91
- const result = await main({ env: { appName: "Resolver Template", version: 1 } });
92
- expect(result).toEqual({
93
- appName: "Resolver Template",
94
- version: 1,
95
- });
96
- });
97
- });
98
- });
@@ -1,174 +0,0 @@
1
- import path from "node:path";
2
- import {
3
- createImportMain,
4
- setupInvokerMock,
5
- setupTailordbMock,
6
- setupWaitPointMock,
7
- setupWorkflowMock,
8
- } from "@tailor-platform/sdk/test";
9
- import { beforeAll, beforeEach, describe, expect, test } from "vitest";
10
-
11
- const outputDir = path.join(__dirname, "../.tailor-sdk");
12
-
13
- describe("bundled workflow execution", () => {
14
- let executedQueries: { query: string; params: unknown[] }[];
15
-
16
- const importMain = createImportMain(outputDir);
17
-
18
- beforeAll(() => {
19
- ({ executedQueries } = setupTailordbMock());
20
- setupInvokerMock(null);
21
- });
22
-
23
- beforeEach(() => {
24
- executedQueries.length = 0;
25
- });
26
-
27
- describe("sync-profile job", () => {
28
- test("creates new user when not found", async () => {
29
- let selectCalled = false;
30
- setupTailordbMock((query) => {
31
- if (query.includes("SELECT") || query.includes("select")) {
32
- if (!selectCalled) {
33
- selectCalled = true;
34
- return [];
35
- }
36
- }
37
- if (query.includes("INSERT") || query.includes("insert")) {
38
- return [
39
- {
40
- id: "new-id",
41
- name: "Alice",
42
- email: "alice@example.com",
43
- age: 25,
44
- createdAt: "2024-01-01",
45
- updatedAt: null,
46
- },
47
- ];
48
- }
49
- return [];
50
- });
51
-
52
- const main = await importMain("workflow-jobs/sync-profile.js");
53
- const result = await main({ name: "Alice", email: "alice@example.com", age: 25 });
54
- expect(result).toEqual({
55
- created: true,
56
- profile: { name: "Alice", email: "alice@example.com", age: 25 },
57
- });
58
- });
59
-
60
- test("updates existing user when found", async () => {
61
- setupTailordbMock((query) => {
62
- if (query.includes("SELECT") || query.includes("select")) {
63
- return [
64
- {
65
- id: "existing-id",
66
- name: "Old Name",
67
- email: "alice@example.com",
68
- age: 20,
69
- createdAt: "2024-01-01",
70
- updatedAt: null,
71
- },
72
- ];
73
- }
74
- return [];
75
- });
76
-
77
- const main = await importMain("workflow-jobs/sync-profile.js");
78
- const result = await main({ name: "Alice Updated", email: "alice@example.com", age: 26 });
79
- expect(result).toEqual({
80
- created: false,
81
- profile: { name: "Alice Updated", email: "alice@example.com", age: 26 },
82
- });
83
- });
84
- });
85
-
86
- describe("approval workflow job", () => {
87
- test("process-with-approval returns approved when resolved with true", async () => {
88
- setupWaitPointMock({
89
- onWait: (_key, _payload) => ({ approved: true }),
90
- });
91
-
92
- const main = await importMain("workflow-jobs/process-with-approval.js");
93
- const result = await main({ orderId: "order-1" });
94
- expect(result).toEqual({ orderId: "order-1", status: "approved" });
95
- });
96
-
97
- test("process-with-approval returns rejected when resolved with false", async () => {
98
- setupWaitPointMock({
99
- onWait: (_key, _payload) => ({ approved: false }),
100
- });
101
-
102
- const main = await importMain("workflow-jobs/process-with-approval.js");
103
- const result = await main({ orderId: "order-2" });
104
- expect(result).toEqual({ orderId: "order-2", status: "rejected" });
105
- });
106
- });
107
-
108
- describe("order-fulfillment jobs", () => {
109
- test("validate-order validates positive amount", async () => {
110
- const main = await importMain("workflow-jobs/validate-order.js");
111
- const result = await main({ orderId: "order-1", amount: 100 });
112
- expect(result).toEqual({ valid: true, orderId: "order-1" });
113
- });
114
-
115
- test("validate-order throws for non-positive amount", async () => {
116
- const main = await importMain("workflow-jobs/validate-order.js");
117
- await expect(main({ orderId: "order-1", amount: 0 })).rejects.toThrow(
118
- "Order amount must be positive",
119
- );
120
- });
121
-
122
- test("process-payment returns transaction", async () => {
123
- const main = await importMain("workflow-jobs/process-payment.js");
124
- const result = await main({ orderId: "order-1", amount: 100 });
125
- expect(result).toEqual({
126
- transactionId: "txn-order-1",
127
- amount: 100,
128
- status: "completed",
129
- });
130
- });
131
-
132
- test("send-confirmation returns confirmation", async () => {
133
- const main = await importMain("workflow-jobs/send-confirmation.js");
134
- const result = await main({ orderId: "order-1", transactionId: "txn-order-1" });
135
- expect(result).toEqual({
136
- orderId: "order-1",
137
- transactionId: "txn-order-1",
138
- confirmed: true,
139
- });
140
- });
141
-
142
- test("fulfill-order orchestrates all jobs", async () => {
143
- setupWorkflowMock((jobName, args) => {
144
- switch (jobName) {
145
- case "validate-order":
146
- return { valid: true, orderId: (args as { orderId: string }).orderId };
147
- case "process-payment":
148
- return {
149
- transactionId: `txn-${(args as { orderId: string }).orderId}`,
150
- amount: (args as { amount: number }).amount,
151
- status: "completed",
152
- };
153
- case "send-confirmation":
154
- return {
155
- orderId: (args as { orderId: string }).orderId,
156
- transactionId: (args as { transactionId: string }).transactionId,
157
- confirmed: true,
158
- };
159
- default:
160
- throw new Error(`Unknown job: ${jobName}`);
161
- }
162
- });
163
-
164
- const main = await importMain("workflow-jobs/fulfill-order.js");
165
- const result = await main({ orderId: "order-1", amount: 100 });
166
- expect(result).toEqual({
167
- orderId: "order-1",
168
- transactionId: "txn-order-1",
169
- confirmed: true,
170
- paymentStatus: "completed",
171
- });
172
- });
173
- });
174
- });