@tailor-platform/create-sdk 1.20.0 → 1.21.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 (149) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/index.js +7 -2
  3. package/package.json +1 -1
  4. package/templates/executor/README.md +32 -0
  5. package/templates/{testing → executor}/eslint.config.js +0 -5
  6. package/templates/executor/package.json +30 -0
  7. package/templates/executor/src/db/auditLog.ts +24 -0
  8. package/templates/executor/src/db/notification.ts +27 -0
  9. package/templates/executor/src/db/user.ts +22 -0
  10. package/templates/executor/src/executor/dailyCleanup.ts +16 -0
  11. package/templates/executor/src/executor/externalWebhook.ts +18 -0
  12. package/templates/executor/src/executor/onDataProcessed.ts +26 -0
  13. package/templates/executor/src/executor/onIdpUserCreated.ts +19 -0
  14. package/templates/executor/src/executor/onIdpUserDeleted.ts +19 -0
  15. package/templates/executor/src/executor/onIdpUserUpdated.ts +19 -0
  16. package/templates/executor/src/executor/onTokenIssued.ts +19 -0
  17. package/templates/executor/src/executor/onTokenRefreshed.ts +19 -0
  18. package/templates/executor/src/executor/onTokenRevoked.ts +19 -0
  19. package/templates/executor/src/executor/onUserCreated.ts +23 -0
  20. package/templates/executor/src/executor/onUserDeleted.ts +22 -0
  21. package/templates/executor/src/executor/onUserUpdated.ts +22 -0
  22. package/templates/executor/src/executor/shared.test.ts +36 -0
  23. package/templates/executor/src/executor/shared.ts +13 -0
  24. package/templates/executor/src/generated/db.ts +59 -0
  25. package/templates/executor/src/resolver/processData.ts +22 -0
  26. package/templates/executor/src/workflow/cleanup.ts +13 -0
  27. package/templates/executor/tailor.config.ts +40 -0
  28. package/templates/executor/vitest.config.ts +15 -0
  29. package/templates/generators/.oxfmtrc.json +3 -0
  30. package/templates/generators/.oxlintrc.json +197 -0
  31. package/templates/generators/.prettierignore +1 -0
  32. package/templates/generators/.prettierrc +1 -0
  33. package/templates/generators/README.md +30 -0
  34. package/templates/generators/__dot__gitignore +3 -0
  35. package/templates/generators/eslint.config.js +24 -0
  36. package/templates/generators/package.json +30 -0
  37. package/templates/generators/src/db/category.ts +25 -0
  38. package/templates/generators/src/db/order.ts +38 -0
  39. package/templates/generators/src/db/product.ts +34 -0
  40. package/templates/generators/src/db/user.ts +26 -0
  41. package/templates/generators/src/generated/db.ts +68 -0
  42. package/templates/generators/src/generated/enums.ts +39 -0
  43. package/templates/generators/src/generated/files.ts +51 -0
  44. package/templates/generators/src/resolver/getProduct.test.ts +92 -0
  45. package/templates/generators/src/resolver/getProduct.ts +53 -0
  46. package/templates/generators/src/seed/data/Category.jsonl +0 -0
  47. package/templates/generators/src/seed/data/Category.schema.ts +23 -0
  48. package/templates/generators/src/seed/data/Order.jsonl +0 -0
  49. package/templates/generators/src/seed/data/Order.schema.ts +21 -0
  50. package/templates/generators/src/seed/data/Product.jsonl +0 -0
  51. package/templates/generators/src/seed/data/Product.schema.ts +23 -0
  52. package/templates/generators/src/seed/data/User.jsonl +0 -0
  53. package/templates/generators/src/seed/data/User.schema.ts +20 -0
  54. package/templates/generators/src/seed/exec.mjs +419 -0
  55. package/templates/generators/tailor.config.ts +36 -0
  56. package/templates/generators/tsconfig.json +16 -0
  57. package/templates/generators/vitest.config.ts +15 -0
  58. package/templates/hello-world/package.json +1 -1
  59. package/templates/inventory-management/package.json +1 -1
  60. package/templates/inventory-management/user-defined.d.ts +15 -0
  61. package/templates/multi-application/package.json +1 -1
  62. package/templates/resolver/.oxfmtrc.json +3 -0
  63. package/templates/resolver/.oxlintrc.json +197 -0
  64. package/templates/resolver/.prettierrc +1 -0
  65. package/templates/resolver/README.md +31 -0
  66. package/templates/resolver/__dot__gitignore +3 -0
  67. package/templates/resolver/eslint.config.js +24 -0
  68. package/templates/resolver/package.json +30 -0
  69. package/templates/resolver/src/resolver/add.test.ts +23 -0
  70. package/templates/{testing/src/resolver/mockTailordb.test.ts → resolver/src/resolver/queryUser.test.ts} +5 -6
  71. package/templates/{testing/src/resolver/mockTailordb.ts → resolver/src/resolver/queryUser.ts} +0 -5
  72. package/templates/resolver/src/resolver/showEnv.test.ts +14 -0
  73. package/templates/resolver/src/resolver/showEnv.ts +19 -0
  74. package/templates/resolver/src/resolver/showUserInfo.test.ts +37 -0
  75. package/templates/resolver/src/resolver/showUserInfo.ts +21 -0
  76. package/templates/{testing/src/resolver/wrapTailordb.test.ts → resolver/src/resolver/updateUser.test.ts} +3 -5
  77. package/templates/{testing/src/resolver/wrapTailordb.ts → resolver/src/resolver/updateUser.ts} +0 -5
  78. package/templates/resolver/tailor.config.ts +26 -0
  79. package/templates/resolver/tsconfig.json +16 -0
  80. package/templates/resolver/user-defined.d.ts +18 -0
  81. package/templates/resolver/vitest.config.ts +15 -0
  82. package/templates/static-web-site/.oxfmtrc.json +3 -0
  83. package/templates/static-web-site/.oxlintrc.json +197 -0
  84. package/templates/static-web-site/.prettierrc +1 -0
  85. package/templates/static-web-site/README.md +21 -0
  86. package/templates/static-web-site/__dot__gitignore +3 -0
  87. package/templates/static-web-site/eslint.config.js +24 -0
  88. package/templates/static-web-site/package.json +27 -0
  89. package/templates/static-web-site/public/callback.html +34 -0
  90. package/templates/static-web-site/public/index.html +55 -0
  91. package/templates/static-web-site/public/style.css +62 -0
  92. package/templates/static-web-site/src/db/user.ts +22 -0
  93. package/templates/static-web-site/tailor.config.ts +55 -0
  94. package/templates/static-web-site/tsconfig.json +16 -0
  95. package/templates/tailordb/.oxfmtrc.json +3 -0
  96. package/templates/tailordb/.oxlintrc.json +197 -0
  97. package/templates/tailordb/.prettierrc +1 -0
  98. package/templates/tailordb/README.md +29 -0
  99. package/templates/tailordb/__dot__gitignore +3 -0
  100. package/templates/tailordb/eslint.config.js +24 -0
  101. package/templates/tailordb/package.json +30 -0
  102. package/templates/tailordb/src/db/category.ts +15 -0
  103. package/templates/tailordb/src/db/comment.ts +26 -0
  104. package/templates/tailordb/src/db/permission.ts +43 -0
  105. package/templates/tailordb/src/db/task.test.ts +41 -0
  106. package/templates/tailordb/src/db/task.ts +58 -0
  107. package/templates/tailordb/src/db/user.ts +19 -0
  108. package/templates/tailordb/src/generated/db.ts +75 -0
  109. package/templates/tailordb/tailor.config.ts +26 -0
  110. package/templates/tailordb/tsconfig.json +16 -0
  111. package/templates/tailordb/user-defined.d.ts +15 -0
  112. package/templates/tailordb/vitest.config.ts +15 -0
  113. package/templates/workflow/.oxfmtrc.json +3 -0
  114. package/templates/workflow/.oxlintrc.json +197 -0
  115. package/templates/workflow/.prettierrc +1 -0
  116. package/templates/workflow/README.md +25 -0
  117. package/templates/workflow/__dot__gitignore +3 -0
  118. package/templates/{testing → workflow}/e2e/globalSetup.ts +5 -5
  119. package/templates/workflow/e2e/resolver.test.ts +90 -0
  120. package/templates/workflow/e2e/workflow.test.ts +31 -0
  121. package/templates/workflow/eslint.config.js +24 -0
  122. package/templates/{testing → workflow}/package.json +3 -2
  123. package/templates/workflow/src/db/order.ts +22 -0
  124. package/templates/workflow/src/db/user.ts +22 -0
  125. package/templates/workflow/src/generated/db.ts +48 -0
  126. package/templates/workflow/src/resolver/incrementAge.ts +40 -0
  127. package/templates/workflow/src/workflow/order-fulfillment.test.ts +148 -0
  128. package/templates/workflow/src/workflow/order-fulfillment.ts +69 -0
  129. package/templates/{testing/src/workflow/wrapTailordb.test.ts → workflow/src/workflow/sync-profile.test.ts} +1 -1
  130. package/templates/{testing → workflow}/tailor.config.ts +1 -1
  131. package/templates/workflow/tsconfig.json +16 -0
  132. package/templates/workflow/user-defined.d.ts +15 -0
  133. package/templates/testing/README.md +0 -130
  134. package/templates/testing/e2e/resolver.test.ts +0 -57
  135. package/templates/testing/e2e/workflow.test.ts +0 -47
  136. package/templates/testing/src/resolver/simple.test.ts +0 -14
  137. package/templates/testing/src/workflow/simple.test.ts +0 -88
  138. package/templates/testing/src/workflow/simple.ts +0 -36
  139. package/templates/testing/src/workflow/trigger.test.ts +0 -104
  140. /package/templates/{testing → executor}/.oxfmtrc.json +0 -0
  141. /package/templates/{testing → executor}/.oxlintrc.json +0 -0
  142. /package/templates/{testing → executor}/.prettierrc +0 -0
  143. /package/templates/{testing → executor}/__dot__gitignore +0 -0
  144. /package/templates/{testing → executor}/tsconfig.json +0 -0
  145. /package/templates/{testing → resolver}/src/db/user.ts +0 -0
  146. /package/templates/{testing → resolver}/src/generated/db.ts +0 -0
  147. /package/templates/{testing/src/resolver/simple.ts → resolver/src/resolver/add.ts} +0 -0
  148. /package/templates/{testing/src/workflow/wrapTailordb.ts → workflow/src/workflow/sync-profile.ts} +0 -0
  149. /package/templates/{testing → workflow}/vitest.config.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @tailor-platform/create-sdk
2
2
 
3
+ ## 1.21.0
4
+
3
5
  ## 1.20.0
4
6
 
5
7
  ## 1.19.0
package/dist/index.js CHANGED
@@ -18,8 +18,13 @@ const availableTemplates = async () => {
18
18
  const templateHints = {
19
19
  "hello-world": "Initial project to get started with Tailor Platform SDK",
20
20
  "inventory-management": "Simple inventory management system",
21
- testing: "Testing guide for Tailor Platform SDK",
22
- "multi-application": "Multi-application setup with shared databases"
21
+ "multi-application": "Multi-application setup with shared databases",
22
+ tailordb: "Comprehensive TailorDB type definitions with all features",
23
+ resolver: "Resolver patterns with testing (simple, DB, env, user)",
24
+ workflow: "Workflow patterns with job chaining and testing",
25
+ executor: "Executor trigger types (record, resolver, schedule, webhook)",
26
+ "static-web-site": "Static website with auth and IdP integration",
27
+ generators: "Built-in generators: kysely, enums, files, seed"
23
28
  };
24
29
  const validateName = (name) => {
25
30
  if (name.length < 3 || name.length > 30) return "Project name must be between 3 and 30 characters long.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/create-sdk",
3
- "version": "1.20.0",
3
+ "version": "1.21.0",
4
4
  "description": "A CLI tool to quickly create a new Tailor Platform SDK project",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,32 @@
1
+ # Executor Template
2
+
3
+ Demonstrates all executor trigger types with supporting infrastructure.
4
+
5
+ ## Trigger Types
6
+
7
+ - `recordCreatedTrigger` - React to new record creation (with condition)
8
+ - `recordUpdatedTrigger` - React to record updates
9
+ - `resolverExecutedTrigger` - React to resolver execution
10
+ - `scheduleTrigger` - CRON-based scheduled execution
11
+ - `incomingWebhookTrigger` - React to external webhook calls
12
+
13
+ ## Operation Kinds
14
+
15
+ - `function` - Custom function body
16
+ - `graphql` - Execute GraphQL mutations
17
+ - `webhook` - Send HTTP requests
18
+ - `workflow` - Trigger a workflow
19
+
20
+ ## Getting Started
21
+
22
+ ```bash
23
+ pnpm install
24
+ pnpm generate
25
+ pnpm deploy
26
+ ```
27
+
28
+ ## Testing
29
+
30
+ ```bash
31
+ pnpm test
32
+ ```
@@ -4,10 +4,7 @@ import { defineConfig, globalIgnores } from "eslint/config";
4
4
  import oxlint from "eslint-plugin-oxlint";
5
5
 
6
6
  export default defineConfig([
7
- // Ignore sdk's output directory.
8
7
  globalIgnores([".tailor-sdk/", "src/generated/"]),
9
- // Use recommended rules.
10
- // https://typescript-eslint.io/users/configs#projects-with-type-checking
11
8
  eslint.configs.recommended,
12
9
  tseslint.configs.recommendedTypeChecked,
13
10
  tseslint.configs.stylisticTypeChecked,
@@ -19,8 +16,6 @@ export default defineConfig([
19
16
  },
20
17
  },
21
18
  },
22
- // Disable type-checked linting for root config files.
23
- // https://typescript-eslint.io/troubleshooting/typed-linting/#how-do-i-disable-type-checked-linting-for-a-file
24
19
  {
25
20
  files: ["eslint.config.js"],
26
21
  extends: [tseslint.configs.disableTypeChecked],
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "executor",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "generate": "tailor-sdk generate",
7
+ "deploy": "tailor-sdk apply",
8
+ "test": "vitest --project unit",
9
+ "test:unit": "vitest --project unit",
10
+ "format": "oxfmt --write .",
11
+ "format:check": "oxfmt --check .",
12
+ "lint": "oxlint --type-aware . && eslint --cache .",
13
+ "lint:fix": "oxlint --type-aware --fix . && eslint --cache --fix .",
14
+ "typecheck": "tsc --noEmit"
15
+ },
16
+ "devDependencies": {
17
+ "@eslint/js": "9.39.2",
18
+ "@tailor-platform/function-types": "0.8.0",
19
+ "@tailor-platform/sdk": "1.21.0",
20
+ "@types/node": "24.10.9",
21
+ "eslint": "9.39.2",
22
+ "eslint-plugin-oxlint": "1.39.0",
23
+ "oxfmt": "0.24.0",
24
+ "oxlint": "1.39.0",
25
+ "oxlint-tsgolint": "0.11.1",
26
+ "typescript": "5.9.3",
27
+ "typescript-eslint": "8.53.0",
28
+ "vitest": "4.0.17"
29
+ }
30
+ }
@@ -0,0 +1,24 @@
1
+ import { db } from "@tailor-platform/sdk";
2
+
3
+ export const auditLog = db
4
+ .type("AuditLog", "Records system events for auditing", {
5
+ action: db.string(),
6
+ entityType: db.string(),
7
+ entityId: db.uuid(),
8
+ message: db.string(),
9
+ ...db.fields.timestamps(),
10
+ })
11
+ .indexes({ fields: ["entityType", "entityId"], unique: false })
12
+ .permission({
13
+ create: [[{ user: "_loggedIn" }, "=", true]],
14
+ read: [[{ user: "_loggedIn" }, "=", true]],
15
+ update: [[{ user: "_loggedIn" }, "=", true]],
16
+ delete: [[{ user: "_loggedIn" }, "=", true]],
17
+ })
18
+ .gqlPermission([
19
+ {
20
+ conditions: [[{ user: "_loggedIn" }, "=", true]],
21
+ actions: "all",
22
+ permit: true,
23
+ },
24
+ ]);
@@ -0,0 +1,27 @@
1
+ import { db } from "@tailor-platform/sdk";
2
+ import { user } from "./user";
3
+
4
+ export const notification = db
5
+ .type("Notification", {
6
+ userId: db.uuid().relation({
7
+ type: "n-1",
8
+ toward: { type: user },
9
+ }),
10
+ title: db.string(),
11
+ body: db.string(),
12
+ isRead: db.bool(),
13
+ ...db.fields.timestamps(),
14
+ })
15
+ .permission({
16
+ create: [[{ user: "_loggedIn" }, "=", true]],
17
+ read: [[{ user: "_loggedIn" }, "=", true]],
18
+ update: [[{ user: "_loggedIn" }, "=", true]],
19
+ delete: [[{ user: "_loggedIn" }, "=", true]],
20
+ })
21
+ .gqlPermission([
22
+ {
23
+ conditions: [[{ user: "_loggedIn" }, "=", true]],
24
+ actions: "all",
25
+ permit: true,
26
+ },
27
+ ]);
@@ -0,0 +1,22 @@
1
+ import { db } from "@tailor-platform/sdk";
2
+
3
+ export const user = db
4
+ .type("User", {
5
+ name: db.string(),
6
+ email: db.string().unique(),
7
+ role: db.enum(["ADMIN", "MEMBER"]),
8
+ ...db.fields.timestamps(),
9
+ })
10
+ .permission({
11
+ create: [[{ user: "_loggedIn" }, "=", true]],
12
+ read: [[{ user: "_loggedIn" }, "=", true]],
13
+ update: [[{ user: "_loggedIn" }, "=", true]],
14
+ delete: [[{ user: "_loggedIn" }, "=", true]],
15
+ })
16
+ .gqlPermission([
17
+ {
18
+ conditions: [[{ user: "_loggedIn" }, "=", true]],
19
+ actions: "all",
20
+ permit: true,
21
+ },
22
+ ]);
@@ -0,0 +1,16 @@
1
+ import { createExecutor, scheduleTrigger } from "@tailor-platform/sdk";
2
+ import cleanupWorkflow from "../workflow/cleanup";
3
+
4
+ export default createExecutor({
5
+ name: "daily-cleanup",
6
+ description: "Runs cleanup workflow daily at midnight",
7
+ trigger: scheduleTrigger({
8
+ cron: "0 0 * * *",
9
+ timezone: "UTC",
10
+ }),
11
+ operation: {
12
+ kind: "workflow",
13
+ workflow: cleanupWorkflow,
14
+ args: () => ({ daysOld: 30 }),
15
+ },
16
+ });
@@ -0,0 +1,18 @@
1
+ import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";
2
+
3
+ export default createExecutor({
4
+ name: "external-webhook",
5
+ description: "Handles incoming webhook events",
6
+ trigger: incomingWebhookTrigger<{
7
+ body: { event: string; payload: Record<string, unknown> };
8
+ headers: Record<string, string>;
9
+ }>(),
10
+ operation: {
11
+ kind: "function",
12
+ body: (args) => {
13
+ console.log("Webhook event:", args.body.event);
14
+ console.log("Payload:", args.body.payload);
15
+ console.log("Headers:", args.headers);
16
+ },
17
+ },
18
+ });
@@ -0,0 +1,26 @@
1
+ import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
2
+ import processData from "../resolver/processData";
3
+ import { createAuditLog } from "./shared";
4
+
5
+ export default createExecutor({
6
+ name: "on-data-processed",
7
+ description: "Creates an audit log when data is processed",
8
+ trigger: resolverExecutedTrigger({
9
+ resolver: processData,
10
+ condition: ({ result }) => {
11
+ if (!result) return false;
12
+ return result.processed;
13
+ },
14
+ }),
15
+ operation: {
16
+ kind: "function",
17
+ body: async () => {
18
+ await createAuditLog({
19
+ action: "DATA_PROCESSED",
20
+ entityType: "Resolver",
21
+ entityId: "00000000-0000-0000-0000-000000000000",
22
+ message: "Data processing completed",
23
+ });
24
+ },
25
+ },
26
+ });
@@ -0,0 +1,19 @@
1
+ import { createExecutor, idpUserCreatedTrigger } from "@tailor-platform/sdk";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ export default createExecutor({
5
+ name: "on-idp-user-created",
6
+ description: "Creates an audit log when an IdP user registers",
7
+ trigger: idpUserCreatedTrigger(),
8
+ operation: {
9
+ kind: "function",
10
+ body: async (args) => {
11
+ await createAuditLog({
12
+ action: "IDP_USER_CREATED",
13
+ entityType: "IdpUser",
14
+ entityId: args.userId,
15
+ message: `IdP user registered in namespace: ${args.namespaceName}`,
16
+ });
17
+ },
18
+ },
19
+ });
@@ -0,0 +1,19 @@
1
+ import { createExecutor, idpUserDeletedTrigger } from "@tailor-platform/sdk";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ export default createExecutor({
5
+ name: "on-idp-user-deleted",
6
+ description: "Creates an audit log when an IdP user is deleted",
7
+ trigger: idpUserDeletedTrigger(),
8
+ operation: {
9
+ kind: "function",
10
+ body: async (args) => {
11
+ await createAuditLog({
12
+ action: "IDP_USER_DELETED",
13
+ entityType: "IdpUser",
14
+ entityId: args.userId,
15
+ message: `IdP user deleted from namespace: ${args.namespaceName}`,
16
+ });
17
+ },
18
+ },
19
+ });
@@ -0,0 +1,19 @@
1
+ import { createExecutor, idpUserUpdatedTrigger } from "@tailor-platform/sdk";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ export default createExecutor({
5
+ name: "on-idp-user-updated",
6
+ description: "Creates an audit log when an IdP user profile is updated",
7
+ trigger: idpUserUpdatedTrigger(),
8
+ operation: {
9
+ kind: "function",
10
+ body: async (args) => {
11
+ await createAuditLog({
12
+ action: "IDP_USER_UPDATED",
13
+ entityType: "IdpUser",
14
+ entityId: args.userId,
15
+ message: `IdP user updated in namespace: ${args.namespaceName}`,
16
+ });
17
+ },
18
+ },
19
+ });
@@ -0,0 +1,19 @@
1
+ import { createExecutor, authAccessTokenIssuedTrigger } from "@tailor-platform/sdk";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ export default createExecutor({
5
+ name: "on-token-issued",
6
+ description: "Creates an audit log when an access token is issued",
7
+ trigger: authAccessTokenIssuedTrigger(),
8
+ operation: {
9
+ kind: "function",
10
+ body: async (args) => {
11
+ await createAuditLog({
12
+ action: "TOKEN_ISSUED",
13
+ entityType: "AuthToken",
14
+ entityId: args.userId,
15
+ message: `Access token issued for user: ${args.userId}`,
16
+ });
17
+ },
18
+ },
19
+ });
@@ -0,0 +1,19 @@
1
+ import { createExecutor, authAccessTokenRefreshedTrigger } from "@tailor-platform/sdk";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ export default createExecutor({
5
+ name: "on-token-refreshed",
6
+ description: "Creates an audit log when an access token is refreshed",
7
+ trigger: authAccessTokenRefreshedTrigger(),
8
+ operation: {
9
+ kind: "function",
10
+ body: async (args) => {
11
+ await createAuditLog({
12
+ action: "TOKEN_REFRESHED",
13
+ entityType: "AuthToken",
14
+ entityId: args.userId,
15
+ message: `Access token refreshed for user: ${args.userId}`,
16
+ });
17
+ },
18
+ },
19
+ });
@@ -0,0 +1,19 @@
1
+ import { createExecutor, authAccessTokenRevokedTrigger } from "@tailor-platform/sdk";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ export default createExecutor({
5
+ name: "on-token-revoked",
6
+ description: "Creates an audit log when an access token is revoked",
7
+ trigger: authAccessTokenRevokedTrigger(),
8
+ operation: {
9
+ kind: "function",
10
+ body: async (args) => {
11
+ await createAuditLog({
12
+ action: "TOKEN_REVOKED",
13
+ entityType: "AuthToken",
14
+ entityId: args.userId,
15
+ message: `Access token revoked for user: ${args.userId}`,
16
+ });
17
+ },
18
+ },
19
+ });
@@ -0,0 +1,23 @@
1
+ import { createExecutor, recordCreatedTrigger, t } from "@tailor-platform/sdk";
2
+ import { user } from "../db/user";
3
+ import { createAuditLog } from "./shared";
4
+
5
+ export default createExecutor({
6
+ name: "on-user-created",
7
+ description: "Creates an audit log when a new admin user is created",
8
+ trigger: recordCreatedTrigger({
9
+ type: user,
10
+ condition: ({ newRecord }) => newRecord.role === "ADMIN",
11
+ }),
12
+ operation: {
13
+ kind: "function",
14
+ body: async (args: { newRecord: t.infer<typeof user> }) => {
15
+ await createAuditLog({
16
+ action: "USER_CREATED",
17
+ entityType: "User",
18
+ entityId: args.newRecord.id,
19
+ message: `Admin user created: ${args.newRecord.name} (${args.newRecord.email})`,
20
+ });
21
+ },
22
+ },
23
+ });
@@ -0,0 +1,22 @@
1
+ import { createExecutor, recordDeletedTrigger, t } from "@tailor-platform/sdk";
2
+ import { user } from "../db/user";
3
+ import { createAuditLog } from "./shared";
4
+
5
+ export default createExecutor({
6
+ name: "on-user-deleted",
7
+ description: "Creates an audit log when a user is deleted",
8
+ trigger: recordDeletedTrigger({
9
+ type: user,
10
+ }),
11
+ operation: {
12
+ kind: "function",
13
+ body: async (args: { oldRecord: t.infer<typeof user> }) => {
14
+ await createAuditLog({
15
+ action: "USER_DELETED",
16
+ entityType: "User",
17
+ entityId: args.oldRecord.id,
18
+ message: `User deleted: ${args.oldRecord.name} (${args.oldRecord.email})`,
19
+ });
20
+ },
21
+ },
22
+ });
@@ -0,0 +1,22 @@
1
+ import { createExecutor, recordUpdatedTrigger, t } from "@tailor-platform/sdk";
2
+ import { user } from "../db/user";
3
+ import { createAuditLog } from "./shared";
4
+
5
+ export default createExecutor({
6
+ name: "on-user-updated",
7
+ description: "Creates an audit log when a user is updated",
8
+ trigger: recordUpdatedTrigger({
9
+ type: user,
10
+ }),
11
+ operation: {
12
+ kind: "function",
13
+ body: async (args: { newRecord: t.infer<typeof user>; oldRecord: t.infer<typeof user> }) => {
14
+ await createAuditLog({
15
+ action: "USER_UPDATED",
16
+ entityType: "User",
17
+ entityId: args.newRecord.id,
18
+ message: `User updated: ${args.oldRecord.name} -> ${args.newRecord.name}`,
19
+ });
20
+ },
21
+ },
22
+ });
@@ -0,0 +1,36 @@
1
+ import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from "vitest";
2
+ import { createAuditLog } from "./shared";
3
+
4
+ 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();
22
+ });
23
+
24
+ test("inserts audit log record", async () => {
25
+ mockQueryObject.mockResolvedValueOnce({});
26
+
27
+ await createAuditLog({
28
+ action: "USER_CREATED",
29
+ entityType: "User",
30
+ entityId: "test-id",
31
+ message: "Test audit log",
32
+ });
33
+
34
+ expect(mockQueryObject).toHaveBeenCalledTimes(1);
35
+ });
36
+ });
@@ -0,0 +1,13 @@
1
+ import { getDB } from "../generated/db";
2
+
3
+ interface AuditLogInput {
4
+ action: string;
5
+ entityType: string;
6
+ entityId: string;
7
+ message: string;
8
+ }
9
+
10
+ export async function createAuditLog(input: AuditLogInput): Promise<void> {
11
+ const db = getDB("main-db");
12
+ await db.insertInto("AuditLog").values(input).execute();
13
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ createGetDB,
3
+ type Generated,
4
+ type Timestamp,
5
+ type NamespaceDB,
6
+ type NamespaceInsertable,
7
+ type NamespaceSelectable,
8
+ type NamespaceTable,
9
+ type NamespaceTableName,
10
+ type NamespaceTransaction,
11
+ type NamespaceUpdateable,
12
+ } from "@tailor-platform/sdk/kysely";
13
+
14
+ export interface Namespace {
15
+ "main-db": {
16
+ AuditLog: {
17
+ id: Generated<string>;
18
+ action: string;
19
+ entityType: string;
20
+ entityId: string;
21
+ message: string;
22
+ createdAt: Generated<Timestamp>;
23
+ updatedAt: Timestamp | null;
24
+ }
25
+
26
+ Notification: {
27
+ id: Generated<string>;
28
+ userId: string;
29
+ title: string;
30
+ body: string;
31
+ isRead: boolean;
32
+ createdAt: Generated<Timestamp>;
33
+ updatedAt: Timestamp | null;
34
+ }
35
+
36
+ User: {
37
+ id: Generated<string>;
38
+ name: string;
39
+ email: string;
40
+ role: "ADMIN" | "MEMBER";
41
+ createdAt: Generated<Timestamp>;
42
+ updatedAt: Timestamp | null;
43
+ }
44
+ }
45
+ }
46
+
47
+ export const getDB = createGetDB<Namespace>();
48
+
49
+ export type DB<N extends keyof Namespace = keyof Namespace> = NamespaceDB<Namespace, N>;
50
+
51
+ export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =
52
+ NamespaceTransaction<Namespace, K>;
53
+
54
+ type TableName = NamespaceTableName<Namespace>;
55
+ export type Table<T extends TableName> = NamespaceTable<Namespace, T>;
56
+
57
+ export type Insertable<T extends TableName> = NamespaceInsertable<Namespace, T>;
58
+ export type Selectable<T extends TableName> = NamespaceSelectable<Namespace, T>;
59
+ export type Updateable<T extends TableName> = NamespaceUpdateable<Namespace, T>;
@@ -0,0 +1,22 @@
1
+ import { createResolver, t } from "@tailor-platform/sdk";
2
+
3
+ const resolver = createResolver({
4
+ name: "processData",
5
+ description: "Processes data and returns a summary",
6
+ operation: "mutation",
7
+ input: {
8
+ data: t.string(),
9
+ },
10
+ body: (context) => {
11
+ return {
12
+ processed: true,
13
+ summary: `Processed: ${context.input.data}`,
14
+ };
15
+ },
16
+ output: t.object({
17
+ processed: t.bool(),
18
+ summary: t.string(),
19
+ }),
20
+ });
21
+
22
+ export default resolver;
@@ -0,0 +1,13 @@
1
+ import { createWorkflow, createWorkflowJob } from "@tailor-platform/sdk";
2
+
3
+ export const cleanupOldRecords = createWorkflowJob({
4
+ name: "cleanup-old-records",
5
+ body: (input: { daysOld: number }) => {
6
+ return { deletedCount: 0, daysOld: input.daysOld };
7
+ },
8
+ });
9
+
10
+ export default createWorkflow({
11
+ name: "cleanup-workflow",
12
+ mainJob: cleanupOldRecords,
13
+ });
@@ -0,0 +1,40 @@
1
+ import { defineAuth, defineConfig, defineIdp, definePlugins, t } from "@tailor-platform/sdk";
2
+ import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type";
3
+
4
+ const idp = defineIdp("main-idp", {
5
+ authorization: "loggedIn",
6
+ clients: ["default-idp-client"],
7
+ userAuthPolicy: {
8
+ useNonEmailIdentifier: false,
9
+ allowSelfPasswordReset: true,
10
+ passwordRequireUppercase: true,
11
+ passwordRequireLowercase: true,
12
+ passwordRequireNonAlphanumeric: true,
13
+ passwordRequireNumeric: true,
14
+ passwordMinLength: 8,
15
+ passwordMaxLength: 128,
16
+ },
17
+ });
18
+
19
+ export default defineConfig({
20
+ name: "executor",
21
+ auth: defineAuth("main-auth", {
22
+ machineUserAttributes: {
23
+ role: t.string(),
24
+ },
25
+ machineUsers: {
26
+ admin: {
27
+ attributes: {
28
+ role: "admin",
29
+ },
30
+ },
31
+ },
32
+ }),
33
+ idp: [idp],
34
+ db: { "main-db": { files: ["./src/db/*.ts"] } },
35
+ resolver: { "main-resolver": { files: ["./src/resolver/*.ts"] } },
36
+ executor: { files: ["./src/executor/*.ts"] },
37
+ workflow: { files: ["./src/workflow/*.ts"] },
38
+ });
39
+
40
+ export const plugins = definePlugins(kyselyTypePlugin({ distPath: "./src/generated/db.ts" }));
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ watch: false,
6
+ projects: [
7
+ {
8
+ test: {
9
+ name: { label: "unit", color: "blue" },
10
+ include: ["src/**/*.test.ts"],
11
+ },
12
+ },
13
+ ],
14
+ },
15
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "ignorePatterns": ["src/generated/", "pnpm-lock.yaml"]
3
+ }