@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.
- package/CHANGELOG.md +2 -0
- package/dist/index.js +7 -2
- package/package.json +1 -1
- package/templates/executor/README.md +32 -0
- package/templates/{testing → executor}/eslint.config.js +0 -5
- package/templates/executor/package.json +30 -0
- package/templates/executor/src/db/auditLog.ts +24 -0
- package/templates/executor/src/db/notification.ts +27 -0
- package/templates/executor/src/db/user.ts +22 -0
- package/templates/executor/src/executor/dailyCleanup.ts +16 -0
- package/templates/executor/src/executor/externalWebhook.ts +18 -0
- package/templates/executor/src/executor/onDataProcessed.ts +26 -0
- package/templates/executor/src/executor/onIdpUserCreated.ts +19 -0
- package/templates/executor/src/executor/onIdpUserDeleted.ts +19 -0
- package/templates/executor/src/executor/onIdpUserUpdated.ts +19 -0
- package/templates/executor/src/executor/onTokenIssued.ts +19 -0
- package/templates/executor/src/executor/onTokenRefreshed.ts +19 -0
- package/templates/executor/src/executor/onTokenRevoked.ts +19 -0
- package/templates/executor/src/executor/onUserCreated.ts +23 -0
- package/templates/executor/src/executor/onUserDeleted.ts +22 -0
- package/templates/executor/src/executor/onUserUpdated.ts +22 -0
- package/templates/executor/src/executor/shared.test.ts +36 -0
- package/templates/executor/src/executor/shared.ts +13 -0
- package/templates/executor/src/generated/db.ts +59 -0
- package/templates/executor/src/resolver/processData.ts +22 -0
- package/templates/executor/src/workflow/cleanup.ts +13 -0
- package/templates/executor/tailor.config.ts +40 -0
- package/templates/executor/vitest.config.ts +15 -0
- package/templates/generators/.oxfmtrc.json +3 -0
- package/templates/generators/.oxlintrc.json +197 -0
- package/templates/generators/.prettierignore +1 -0
- package/templates/generators/.prettierrc +1 -0
- package/templates/generators/README.md +30 -0
- package/templates/generators/__dot__gitignore +3 -0
- package/templates/generators/eslint.config.js +24 -0
- package/templates/generators/package.json +30 -0
- package/templates/generators/src/db/category.ts +25 -0
- package/templates/generators/src/db/order.ts +38 -0
- package/templates/generators/src/db/product.ts +34 -0
- package/templates/generators/src/db/user.ts +26 -0
- package/templates/generators/src/generated/db.ts +68 -0
- package/templates/generators/src/generated/enums.ts +39 -0
- package/templates/generators/src/generated/files.ts +51 -0
- package/templates/generators/src/resolver/getProduct.test.ts +92 -0
- package/templates/generators/src/resolver/getProduct.ts +53 -0
- package/templates/generators/src/seed/data/Category.jsonl +0 -0
- package/templates/generators/src/seed/data/Category.schema.ts +23 -0
- package/templates/generators/src/seed/data/Order.jsonl +0 -0
- package/templates/generators/src/seed/data/Order.schema.ts +21 -0
- package/templates/generators/src/seed/data/Product.jsonl +0 -0
- package/templates/generators/src/seed/data/Product.schema.ts +23 -0
- package/templates/generators/src/seed/data/User.jsonl +0 -0
- package/templates/generators/src/seed/data/User.schema.ts +20 -0
- package/templates/generators/src/seed/exec.mjs +419 -0
- package/templates/generators/tailor.config.ts +36 -0
- package/templates/generators/tsconfig.json +16 -0
- package/templates/generators/vitest.config.ts +15 -0
- package/templates/hello-world/package.json +1 -1
- package/templates/inventory-management/package.json +1 -1
- package/templates/inventory-management/user-defined.d.ts +15 -0
- package/templates/multi-application/package.json +1 -1
- package/templates/resolver/.oxfmtrc.json +3 -0
- package/templates/resolver/.oxlintrc.json +197 -0
- package/templates/resolver/.prettierrc +1 -0
- package/templates/resolver/README.md +31 -0
- package/templates/resolver/__dot__gitignore +3 -0
- package/templates/resolver/eslint.config.js +24 -0
- package/templates/resolver/package.json +30 -0
- package/templates/resolver/src/resolver/add.test.ts +23 -0
- package/templates/{testing/src/resolver/mockTailordb.test.ts → resolver/src/resolver/queryUser.test.ts} +5 -6
- package/templates/{testing/src/resolver/mockTailordb.ts → resolver/src/resolver/queryUser.ts} +0 -5
- package/templates/resolver/src/resolver/showEnv.test.ts +14 -0
- package/templates/resolver/src/resolver/showEnv.ts +19 -0
- package/templates/resolver/src/resolver/showUserInfo.test.ts +37 -0
- package/templates/resolver/src/resolver/showUserInfo.ts +21 -0
- package/templates/{testing/src/resolver/wrapTailordb.test.ts → resolver/src/resolver/updateUser.test.ts} +3 -5
- package/templates/{testing/src/resolver/wrapTailordb.ts → resolver/src/resolver/updateUser.ts} +0 -5
- package/templates/resolver/tailor.config.ts +26 -0
- package/templates/resolver/tsconfig.json +16 -0
- package/templates/resolver/user-defined.d.ts +18 -0
- package/templates/resolver/vitest.config.ts +15 -0
- package/templates/static-web-site/.oxfmtrc.json +3 -0
- package/templates/static-web-site/.oxlintrc.json +197 -0
- package/templates/static-web-site/.prettierrc +1 -0
- package/templates/static-web-site/README.md +21 -0
- package/templates/static-web-site/__dot__gitignore +3 -0
- package/templates/static-web-site/eslint.config.js +24 -0
- package/templates/static-web-site/package.json +27 -0
- package/templates/static-web-site/public/callback.html +34 -0
- package/templates/static-web-site/public/index.html +55 -0
- package/templates/static-web-site/public/style.css +62 -0
- package/templates/static-web-site/src/db/user.ts +22 -0
- package/templates/static-web-site/tailor.config.ts +55 -0
- package/templates/static-web-site/tsconfig.json +16 -0
- package/templates/tailordb/.oxfmtrc.json +3 -0
- package/templates/tailordb/.oxlintrc.json +197 -0
- package/templates/tailordb/.prettierrc +1 -0
- package/templates/tailordb/README.md +29 -0
- package/templates/tailordb/__dot__gitignore +3 -0
- package/templates/tailordb/eslint.config.js +24 -0
- package/templates/tailordb/package.json +30 -0
- package/templates/tailordb/src/db/category.ts +15 -0
- package/templates/tailordb/src/db/comment.ts +26 -0
- package/templates/tailordb/src/db/permission.ts +43 -0
- package/templates/tailordb/src/db/task.test.ts +41 -0
- package/templates/tailordb/src/db/task.ts +58 -0
- package/templates/tailordb/src/db/user.ts +19 -0
- package/templates/tailordb/src/generated/db.ts +75 -0
- package/templates/tailordb/tailor.config.ts +26 -0
- package/templates/tailordb/tsconfig.json +16 -0
- package/templates/tailordb/user-defined.d.ts +15 -0
- package/templates/tailordb/vitest.config.ts +15 -0
- package/templates/workflow/.oxfmtrc.json +3 -0
- package/templates/workflow/.oxlintrc.json +197 -0
- package/templates/workflow/.prettierrc +1 -0
- package/templates/workflow/README.md +25 -0
- package/templates/workflow/__dot__gitignore +3 -0
- package/templates/{testing → workflow}/e2e/globalSetup.ts +5 -5
- package/templates/workflow/e2e/resolver.test.ts +90 -0
- package/templates/workflow/e2e/workflow.test.ts +31 -0
- package/templates/workflow/eslint.config.js +24 -0
- package/templates/{testing → workflow}/package.json +3 -2
- package/templates/workflow/src/db/order.ts +22 -0
- package/templates/workflow/src/db/user.ts +22 -0
- package/templates/workflow/src/generated/db.ts +48 -0
- package/templates/workflow/src/resolver/incrementAge.ts +40 -0
- package/templates/workflow/src/workflow/order-fulfillment.test.ts +148 -0
- package/templates/workflow/src/workflow/order-fulfillment.ts +69 -0
- package/templates/{testing/src/workflow/wrapTailordb.test.ts → workflow/src/workflow/sync-profile.test.ts} +1 -1
- package/templates/{testing → workflow}/tailor.config.ts +1 -1
- package/templates/workflow/tsconfig.json +16 -0
- package/templates/workflow/user-defined.d.ts +15 -0
- package/templates/testing/README.md +0 -130
- package/templates/testing/e2e/resolver.test.ts +0 -57
- package/templates/testing/e2e/workflow.test.ts +0 -47
- package/templates/testing/src/resolver/simple.test.ts +0 -14
- package/templates/testing/src/workflow/simple.test.ts +0 -88
- package/templates/testing/src/workflow/simple.ts +0 -36
- package/templates/testing/src/workflow/trigger.test.ts +0 -104
- /package/templates/{testing → executor}/.oxfmtrc.json +0 -0
- /package/templates/{testing → executor}/.oxlintrc.json +0 -0
- /package/templates/{testing → executor}/.prettierrc +0 -0
- /package/templates/{testing → executor}/__dot__gitignore +0 -0
- /package/templates/{testing → executor}/tsconfig.json +0 -0
- /package/templates/{testing → resolver}/src/db/user.ts +0 -0
- /package/templates/{testing → resolver}/src/generated/db.ts +0 -0
- /package/templates/{testing/src/resolver/simple.ts → resolver/src/resolver/add.ts} +0 -0
- /package/templates/{testing/src/workflow/wrapTailordb.ts → workflow/src/workflow/sync-profile.ts} +0 -0
- /package/templates/{testing → workflow}/vitest.config.ts +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createResolver, t } from "@tailor-platform/sdk";
|
|
2
|
+
import { getDB } from "../generated/db";
|
|
3
|
+
|
|
4
|
+
const resolver = createResolver({
|
|
5
|
+
name: "getProduct",
|
|
6
|
+
description: "Retrieves a product by ID with its category",
|
|
7
|
+
operation: "query",
|
|
8
|
+
input: {
|
|
9
|
+
productId: t.uuid(),
|
|
10
|
+
},
|
|
11
|
+
body: async (context) => {
|
|
12
|
+
const db = getDB("main-db");
|
|
13
|
+
|
|
14
|
+
const product = await db
|
|
15
|
+
.selectFrom("Product")
|
|
16
|
+
.where("id", "=", context.input.productId)
|
|
17
|
+
.selectAll()
|
|
18
|
+
.executeTakeFirstOrThrow();
|
|
19
|
+
|
|
20
|
+
const result: {
|
|
21
|
+
name: string;
|
|
22
|
+
price: number;
|
|
23
|
+
status: string;
|
|
24
|
+
categoryName: string | null;
|
|
25
|
+
} = {
|
|
26
|
+
name: product.name,
|
|
27
|
+
price: product.price,
|
|
28
|
+
status: product.status,
|
|
29
|
+
categoryName: null,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (product.categoryId) {
|
|
33
|
+
const category = await db
|
|
34
|
+
.selectFrom("Category")
|
|
35
|
+
.where("id", "=", product.categoryId)
|
|
36
|
+
.select("name")
|
|
37
|
+
.executeTakeFirst();
|
|
38
|
+
if (category) {
|
|
39
|
+
result.categoryName = category.name;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
},
|
|
45
|
+
output: t.object({
|
|
46
|
+
name: t.string(),
|
|
47
|
+
price: t.float(),
|
|
48
|
+
status: t.string(),
|
|
49
|
+
categoryName: t.string({ optional: true }),
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default resolver;
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { t } from "@tailor-platform/sdk";
|
|
2
|
+
import { createTailorDBHook, createStandardSchema } from "@tailor-platform/sdk/test";
|
|
3
|
+
import { defineSchema } from "@toiroakr/lines-db";
|
|
4
|
+
import { category } from "../../db/category";
|
|
5
|
+
|
|
6
|
+
const schemaType = t.object({
|
|
7
|
+
...category.pickFields(["id"], { optional: true }),
|
|
8
|
+
...category.omitFields(["id"]),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const hook = createTailorDBHook(category);
|
|
12
|
+
|
|
13
|
+
export const schema = defineSchema(
|
|
14
|
+
createStandardSchema(schemaType, hook),
|
|
15
|
+
{
|
|
16
|
+
foreignKeys: [
|
|
17
|
+
{"column":"parentCategoryId","references":{"table":"Category","column":"id"}},
|
|
18
|
+
],
|
|
19
|
+
indexes: [
|
|
20
|
+
{"name":"category_slug_unique_idx","columns":["slug"],"unique":true},
|
|
21
|
+
],
|
|
22
|
+
}
|
|
23
|
+
);
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { t } from "@tailor-platform/sdk";
|
|
2
|
+
import { createTailorDBHook, createStandardSchema } from "@tailor-platform/sdk/test";
|
|
3
|
+
import { defineSchema } from "@toiroakr/lines-db";
|
|
4
|
+
import { order } from "../../db/order";
|
|
5
|
+
|
|
6
|
+
const schemaType = t.object({
|
|
7
|
+
...order.pickFields(["id","createdAt"], { optional: true }),
|
|
8
|
+
...order.omitFields(["id","createdAt"]),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const hook = createTailorDBHook(order);
|
|
12
|
+
|
|
13
|
+
export const schema = defineSchema(
|
|
14
|
+
createStandardSchema(schemaType, hook),
|
|
15
|
+
{
|
|
16
|
+
foreignKeys: [
|
|
17
|
+
{"column":"productId","references":{"table":"Product","column":"id"}},
|
|
18
|
+
{"column":"userId","references":{"table":"User","column":"id"}},
|
|
19
|
+
],
|
|
20
|
+
}
|
|
21
|
+
);
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { t } from "@tailor-platform/sdk";
|
|
2
|
+
import { createTailorDBHook, createStandardSchema } from "@tailor-platform/sdk/test";
|
|
3
|
+
import { defineSchema } from "@toiroakr/lines-db";
|
|
4
|
+
import { product } from "../../db/product";
|
|
5
|
+
|
|
6
|
+
const schemaType = t.object({
|
|
7
|
+
...product.pickFields(["id","createdAt"], { optional: true }),
|
|
8
|
+
...product.omitFields(["id","createdAt"]),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const hook = createTailorDBHook(product);
|
|
12
|
+
|
|
13
|
+
export const schema = defineSchema(
|
|
14
|
+
createStandardSchema(schemaType, hook),
|
|
15
|
+
{
|
|
16
|
+
foreignKeys: [
|
|
17
|
+
{"column":"categoryId","references":{"table":"Category","column":"id"}},
|
|
18
|
+
],
|
|
19
|
+
indexes: [
|
|
20
|
+
{"name":"idx_status_categoryId","columns":["status","categoryId"],"unique":false},
|
|
21
|
+
],
|
|
22
|
+
}
|
|
23
|
+
);
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { t } from "@tailor-platform/sdk";
|
|
2
|
+
import { createTailorDBHook, createStandardSchema } from "@tailor-platform/sdk/test";
|
|
3
|
+
import { defineSchema } from "@toiroakr/lines-db";
|
|
4
|
+
import { user } from "../../db/user";
|
|
5
|
+
|
|
6
|
+
const schemaType = t.object({
|
|
7
|
+
...user.pickFields(["id","createdAt"], { optional: true }),
|
|
8
|
+
...user.omitFields(["id","createdAt"]),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const hook = createTailorDBHook(user);
|
|
12
|
+
|
|
13
|
+
export const schema = defineSchema(
|
|
14
|
+
createStandardSchema(schemaType, hook),
|
|
15
|
+
{
|
|
16
|
+
indexes: [
|
|
17
|
+
{"name":"user_email_unique_idx","columns":["email"],"unique":true},
|
|
18
|
+
],
|
|
19
|
+
}
|
|
20
|
+
);
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parseArgs, styleText } from "node:util";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
5
|
+
import {
|
|
6
|
+
show,
|
|
7
|
+
truncate,
|
|
8
|
+
bundleSeedScript,
|
|
9
|
+
chunkSeedData,
|
|
10
|
+
executeScript,
|
|
11
|
+
initOperatorClient,
|
|
12
|
+
loadAccessToken,
|
|
13
|
+
loadWorkspaceId,
|
|
14
|
+
} from "@tailor-platform/sdk/cli";
|
|
15
|
+
|
|
16
|
+
// Parse command-line arguments
|
|
17
|
+
const { values, positionals } = parseArgs({
|
|
18
|
+
options: {
|
|
19
|
+
"machine-user": { type: "string", short: "m" },
|
|
20
|
+
namespace: { type: "string", short: "n" },
|
|
21
|
+
"skip-idp": { type: "boolean", default: false },
|
|
22
|
+
truncate: { type: "boolean", default: false },
|
|
23
|
+
yes: { type: "boolean", default: false },
|
|
24
|
+
profile: { type: "string", short: "p" },
|
|
25
|
+
help: { type: "boolean", short: "h", default: false },
|
|
26
|
+
},
|
|
27
|
+
allowPositionals: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (values.help) {
|
|
31
|
+
console.log(`
|
|
32
|
+
Usage: node exec.mjs [options] [types...]
|
|
33
|
+
|
|
34
|
+
Options:
|
|
35
|
+
-m, --machine-user <name> Machine user name for authentication (required if not configured)
|
|
36
|
+
-n, --namespace <ns> Process all types in specified namespace (excludes _User)
|
|
37
|
+
--skip-idp Skip IdP user (_User) entity
|
|
38
|
+
--truncate Truncate tables before seeding
|
|
39
|
+
--yes Skip confirmation prompts (for truncate)
|
|
40
|
+
-p, --profile <name> Workspace profile name
|
|
41
|
+
-h, --help Show help
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
node exec.mjs -m admin # Process all types with machine user
|
|
45
|
+
node exec.mjs --namespace <namespace> # Process tailordb namespace only (no _User)
|
|
46
|
+
node exec.mjs User Order # Process specific types only
|
|
47
|
+
node exec.mjs --skip-idp # Process all except _User
|
|
48
|
+
node exec.mjs --truncate # Truncate all tables, then seed all
|
|
49
|
+
node exec.mjs --truncate --yes # Truncate all tables without confirmation, then seed all
|
|
50
|
+
node exec.mjs --truncate --namespace <namespace> # Truncate tailordb, then seed tailordb
|
|
51
|
+
node exec.mjs --truncate User Order # Truncate User and Order, then seed them
|
|
52
|
+
`);
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Helper function to prompt for y/n confirmation
|
|
57
|
+
const promptConfirmation = (question) => {
|
|
58
|
+
const rl = createInterface({
|
|
59
|
+
input: process.stdin,
|
|
60
|
+
output: process.stdout,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
rl.question(styleText("yellow", question), (answer) => {
|
|
65
|
+
rl.close();
|
|
66
|
+
resolve(answer.toLowerCase().trim());
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const configDir = import.meta.dirname;
|
|
72
|
+
const configPath = join(configDir, "../../tailor.config.ts");
|
|
73
|
+
|
|
74
|
+
// Determine machine user name (CLI argument takes precedence over config default)
|
|
75
|
+
const defaultMachineUser = "admin";
|
|
76
|
+
const machineUserName = values["machine-user"] || defaultMachineUser;
|
|
77
|
+
|
|
78
|
+
if (!machineUserName) {
|
|
79
|
+
console.error(styleText("red", "Error: Machine user name is required."));
|
|
80
|
+
console.error(styleText("yellow", "Specify --machine-user <name> or configure machineUserName in generator options."));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Entity configuration
|
|
85
|
+
const namespaceEntities = {
|
|
86
|
+
"main-db": [
|
|
87
|
+
"Category",
|
|
88
|
+
"Order",
|
|
89
|
+
"Product",
|
|
90
|
+
"User",
|
|
91
|
+
]
|
|
92
|
+
};
|
|
93
|
+
const namespaceDeps = {
|
|
94
|
+
"main-db": {
|
|
95
|
+
"Category": [],
|
|
96
|
+
"Order": ["Product", "User"],
|
|
97
|
+
"Product": ["Category"],
|
|
98
|
+
"User": []
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const entities = Object.values(namespaceEntities).flat();
|
|
102
|
+
const hasIdpUser = false;
|
|
103
|
+
|
|
104
|
+
// Determine which entities to process
|
|
105
|
+
let entitiesToProcess = null;
|
|
106
|
+
|
|
107
|
+
const hasNamespace = !!values.namespace;
|
|
108
|
+
const hasTypes = positionals.length > 0;
|
|
109
|
+
const skipIdp = values["skip-idp"];
|
|
110
|
+
|
|
111
|
+
// Validate mutually exclusive options
|
|
112
|
+
const optionCount = [hasNamespace, hasTypes].filter(Boolean).length;
|
|
113
|
+
if (optionCount > 1) {
|
|
114
|
+
console.error(styleText("red", "Error: Options --namespace and type names are mutually exclusive."));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// --skip-idp and --namespace are redundant (namespace already excludes _User)
|
|
119
|
+
if (skipIdp && hasNamespace) {
|
|
120
|
+
console.warn(styleText("yellow", "Warning: --skip-idp is redundant with --namespace (namespace filtering already excludes _User)."));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Filter by namespace (automatically excludes _User as it has no namespace)
|
|
124
|
+
if (hasNamespace) {
|
|
125
|
+
const namespace = values.namespace;
|
|
126
|
+
entitiesToProcess = namespaceEntities[namespace];
|
|
127
|
+
|
|
128
|
+
if (!entitiesToProcess || entitiesToProcess.length === 0) {
|
|
129
|
+
console.error(styleText("red", `Error: No entities found in namespace "${namespace}"`));
|
|
130
|
+
console.error(styleText("yellow", `Available namespaces: ${Object.keys(namespaceEntities).join(", ")}`));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(styleText("cyan", `Filtering by namespace: ${namespace}`));
|
|
135
|
+
console.log(styleText("dim", `Entities: ${entitiesToProcess.join(", ")}`));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Filter by specific types
|
|
139
|
+
if (hasTypes) {
|
|
140
|
+
const requestedTypes = positionals;
|
|
141
|
+
const notFoundTypes = [];
|
|
142
|
+
const allTypes = hasIdpUser ? [...entities, "_User"] : entities;
|
|
143
|
+
|
|
144
|
+
entitiesToProcess = requestedTypes.filter((type) => {
|
|
145
|
+
if (!allTypes.includes(type)) {
|
|
146
|
+
notFoundTypes.push(type);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (notFoundTypes.length > 0) {
|
|
153
|
+
console.error(styleText("red", `Error: The following types were not found: ${notFoundTypes.join(", ")}`));
|
|
154
|
+
console.error(styleText("yellow", `Available types: ${allTypes.join(", ")}`));
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(styleText("cyan", `Filtering by types: ${entitiesToProcess.join(", ")}`));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Apply --skip-idp filter
|
|
162
|
+
if (skipIdp) {
|
|
163
|
+
if (entitiesToProcess) {
|
|
164
|
+
entitiesToProcess = entitiesToProcess.filter((entity) => entity !== "_User");
|
|
165
|
+
} else {
|
|
166
|
+
entitiesToProcess = entities.filter((entity) => entity !== "_User");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Get application info
|
|
171
|
+
const appInfo = await show({ configPath, profile: values.profile });
|
|
172
|
+
const authNamespace = appInfo.auth;
|
|
173
|
+
|
|
174
|
+
// Initialize operator client (once for all namespaces)
|
|
175
|
+
const accessToken = await loadAccessToken({ profile: values.profile, useProfile: true });
|
|
176
|
+
const workspaceId = await loadWorkspaceId({ profile: values.profile });
|
|
177
|
+
const operatorClient = await initOperatorClient(accessToken);
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
// Truncate tables if requested
|
|
182
|
+
if (values.truncate) {
|
|
183
|
+
const answer = values.yes ? "y" : await promptConfirmation("Are you sure you want to truncate? (y/n): ");
|
|
184
|
+
if (answer !== "y") {
|
|
185
|
+
console.log(styleText("yellow", "Truncate cancelled."));
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log(styleText("cyan", "Truncating tables..."));
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
if (hasNamespace) {
|
|
193
|
+
await truncate({
|
|
194
|
+
configPath,
|
|
195
|
+
profile: values.profile,
|
|
196
|
+
namespace: values.namespace,
|
|
197
|
+
});
|
|
198
|
+
} else if (hasTypes) {
|
|
199
|
+
const typesToTruncate = entitiesToProcess.filter((t) => t !== "_User");
|
|
200
|
+
if (typesToTruncate.length > 0) {
|
|
201
|
+
await truncate({
|
|
202
|
+
configPath,
|
|
203
|
+
profile: values.profile,
|
|
204
|
+
types: typesToTruncate,
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
console.log(styleText("dim", "No TailorDB types to truncate (only _User was specified)."));
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
await truncate({
|
|
211
|
+
configPath,
|
|
212
|
+
profile: values.profile,
|
|
213
|
+
all: true,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error(styleText("red", `Truncate failed: ${error.message}`));
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
console.log(styleText("green", "Truncate completed."));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(styleText("cyan", "\nStarting seed data generation..."));
|
|
227
|
+
if (skipIdp) {
|
|
228
|
+
console.log(styleText("dim", ` Skipping IdP user (_User)`));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Load seed data from JSONL files
|
|
232
|
+
const loadSeedData = (dataDir, typeNames) => {
|
|
233
|
+
const data = {};
|
|
234
|
+
for (const typeName of typeNames) {
|
|
235
|
+
const jsonlPath = join(dataDir, `${typeName}.jsonl`);
|
|
236
|
+
try {
|
|
237
|
+
const content = readFileSync(jsonlPath, "utf-8").trim();
|
|
238
|
+
if (content) {
|
|
239
|
+
data[typeName] = content.split("\n").map((line) => JSON.parse(line));
|
|
240
|
+
} else {
|
|
241
|
+
data[typeName] = [];
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (error.code === "ENOENT") {
|
|
245
|
+
data[typeName] = [];
|
|
246
|
+
} else {
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return data;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Topological sort for dependency order
|
|
255
|
+
const topologicalSort = (types, deps) => {
|
|
256
|
+
const visited = new Set();
|
|
257
|
+
const result = [];
|
|
258
|
+
|
|
259
|
+
const visit = (type) => {
|
|
260
|
+
if (visited.has(type)) return;
|
|
261
|
+
visited.add(type);
|
|
262
|
+
const typeDeps = deps[type] || [];
|
|
263
|
+
for (const dep of typeDeps) {
|
|
264
|
+
if (types.includes(dep)) {
|
|
265
|
+
visit(dep);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
result.push(type);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
for (const type of types) {
|
|
272
|
+
visit(type);
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Seed TailorDB types via testExecScript
|
|
278
|
+
const seedViaTestExecScript = async (namespace, typesToSeed, deps) => {
|
|
279
|
+
const dataDir = join(configDir, "data");
|
|
280
|
+
const sortedTypes = topologicalSort(typesToSeed, deps);
|
|
281
|
+
const data = loadSeedData(dataDir, sortedTypes);
|
|
282
|
+
|
|
283
|
+
// Skip if no data
|
|
284
|
+
const typesWithData = sortedTypes.filter((t) => data[t] && data[t].length > 0);
|
|
285
|
+
if (typesWithData.length === 0) {
|
|
286
|
+
console.log(styleText("dim", ` [${namespace}] No data to seed`));
|
|
287
|
+
return { success: true, processed: {} };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.log(styleText("cyan", ` [${namespace}] Seeding ${typesWithData.length} types via Kysely batch insert...`));
|
|
291
|
+
|
|
292
|
+
// Bundle seed script
|
|
293
|
+
const bundled = await bundleSeedScript(namespace, typesWithData);
|
|
294
|
+
|
|
295
|
+
// Chunk seed data to fit within gRPC message size limits
|
|
296
|
+
const chunks = chunkSeedData({
|
|
297
|
+
data,
|
|
298
|
+
order: sortedTypes,
|
|
299
|
+
codeByteSize: new TextEncoder().encode(bundled.bundledCode).length,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (chunks.length === 0) {
|
|
303
|
+
console.log(styleText("dim", ` [${namespace}] No data to seed`));
|
|
304
|
+
return { success: true, processed: {} };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (chunks.length > 1) {
|
|
308
|
+
console.log(styleText("dim", ` Split into ${chunks.length} chunks`));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const allProcessed = {};
|
|
312
|
+
let hasError = false;
|
|
313
|
+
const allErrors = [];
|
|
314
|
+
|
|
315
|
+
for (const chunk of chunks) {
|
|
316
|
+
if (chunks.length > 1) {
|
|
317
|
+
console.log(styleText("dim", ` Chunk ${chunk.index + 1}/${chunk.total}: ${chunk.order.join(", ")}`));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Execute seed script for this chunk
|
|
321
|
+
const result = await executeScript({
|
|
322
|
+
client: operatorClient,
|
|
323
|
+
workspaceId,
|
|
324
|
+
name: `seed-${namespace}.ts`,
|
|
325
|
+
code: bundled.bundledCode,
|
|
326
|
+
arg: JSON.stringify({ data: chunk.data, order: chunk.order }),
|
|
327
|
+
invoker: {
|
|
328
|
+
namespace: authNamespace,
|
|
329
|
+
machineUserName,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Parse result and display logs
|
|
334
|
+
if (result.logs) {
|
|
335
|
+
for (const line of result.logs.split("\n").filter(Boolean)) {
|
|
336
|
+
console.log(styleText("dim", ` ${line}`));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (result.success) {
|
|
341
|
+
let parsed;
|
|
342
|
+
try {
|
|
343
|
+
const parsedResult = JSON.parse(result.result || "{}");
|
|
344
|
+
parsed = parsedResult && typeof parsedResult === "object" ? parsedResult : {};
|
|
345
|
+
} catch (error) {
|
|
346
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
347
|
+
console.error(styleText("red", ` ✗ Failed to parse seed result: ${message}`));
|
|
348
|
+
hasError = true;
|
|
349
|
+
allErrors.push(message);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const processed = parsed.processed || {};
|
|
354
|
+
for (const [type, count] of Object.entries(processed)) {
|
|
355
|
+
allProcessed[type] = (allProcessed[type] || 0) + count;
|
|
356
|
+
console.log(styleText("green", ` ✓ ${type}: ${count} rows inserted`));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!parsed.success) {
|
|
360
|
+
const errors = Array.isArray(parsed.errors) ? parsed.errors : [];
|
|
361
|
+
const errorMessage =
|
|
362
|
+
errors.length > 0 ? errors.join("\n ") : "Seed script reported failure";
|
|
363
|
+
console.error(styleText("red", ` ✗ Seed failed:\n ${errorMessage}`));
|
|
364
|
+
hasError = true;
|
|
365
|
+
allErrors.push(errorMessage);
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
console.error(styleText("red", ` ✗ Seed failed: ${result.error}`));
|
|
369
|
+
hasError = true;
|
|
370
|
+
allErrors.push(result.error);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (hasError) {
|
|
375
|
+
return { success: false, error: allErrors.join("\n") };
|
|
376
|
+
}
|
|
377
|
+
return { success: true, processed: allProcessed };
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
// Main execution
|
|
383
|
+
try {
|
|
384
|
+
let allSuccess = true;
|
|
385
|
+
|
|
386
|
+
// Determine which namespaces and types to process
|
|
387
|
+
const namespacesToProcess = hasNamespace
|
|
388
|
+
? [values.namespace]
|
|
389
|
+
: Object.keys(namespaceEntities);
|
|
390
|
+
|
|
391
|
+
for (const namespace of namespacesToProcess) {
|
|
392
|
+
const nsTypes = namespaceEntities[namespace] || [];
|
|
393
|
+
const nsDeps = namespaceDeps[namespace] || {};
|
|
394
|
+
|
|
395
|
+
// Filter types if specific types requested
|
|
396
|
+
let typesToSeed = entitiesToProcess
|
|
397
|
+
? nsTypes.filter((t) => entitiesToProcess.includes(t))
|
|
398
|
+
: nsTypes;
|
|
399
|
+
|
|
400
|
+
if (typesToSeed.length === 0) continue;
|
|
401
|
+
|
|
402
|
+
const result = await seedViaTestExecScript(namespace, typesToSeed, nsDeps);
|
|
403
|
+
if (!result.success) {
|
|
404
|
+
allSuccess = false;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
if (allSuccess) {
|
|
411
|
+
console.log(styleText("green", "\n✓ Seed data generation completed successfully"));
|
|
412
|
+
} else {
|
|
413
|
+
console.error(styleText("red", "\n✗ Seed data generation completed with errors"));
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
console.error(styleText("red", `\n✗ Seed data generation failed: ${error.message}`));
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { defineAuth, defineConfig, defineIdp, definePlugins, t } from "@tailor-platform/sdk";
|
|
2
|
+
import { enumConstantsPlugin } from "@tailor-platform/sdk/plugin/enum-constants";
|
|
3
|
+
import { fileUtilsPlugin } from "@tailor-platform/sdk/plugin/file-utils";
|
|
4
|
+
import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type";
|
|
5
|
+
import { seedPlugin } from "@tailor-platform/sdk/plugin/seed";
|
|
6
|
+
|
|
7
|
+
const idp = defineIdp("main-idp", {
|
|
8
|
+
authorization: "loggedIn",
|
|
9
|
+
clients: ["default-idp-client"],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
name: "generators",
|
|
14
|
+
auth: defineAuth("main-auth", {
|
|
15
|
+
machineUserAttributes: {
|
|
16
|
+
role: t.string(),
|
|
17
|
+
},
|
|
18
|
+
machineUsers: {
|
|
19
|
+
admin: {
|
|
20
|
+
attributes: {
|
|
21
|
+
role: "admin",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
idp: [idp],
|
|
27
|
+
db: { "main-db": { files: ["./src/db/*.ts"] } },
|
|
28
|
+
resolver: { "main-resolver": { files: ["./src/resolver/*.ts"] } },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const plugins = definePlugins(
|
|
32
|
+
kyselyTypePlugin({ distPath: "./src/generated/db.ts" }),
|
|
33
|
+
enumConstantsPlugin({ distPath: "./src/generated/enums.ts" }),
|
|
34
|
+
fileUtilsPlugin({ distPath: "./src/generated/files.ts" }),
|
|
35
|
+
seedPlugin({ distPath: "./src/seed", machineUserName: "admin" }),
|
|
36
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"types": ["node", "@tailor-platform/function-types"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["**/*.ts"]
|
|
16
|
+
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@eslint/js": "9.39.2",
|
|
16
16
|
"@tailor-platform/function-types": "0.8.0",
|
|
17
|
-
"@tailor-platform/sdk": "1.
|
|
17
|
+
"@tailor-platform/sdk": "1.21.0",
|
|
18
18
|
"@types/node": "24.10.9",
|
|
19
19
|
"eslint": "9.39.2",
|
|
20
20
|
"eslint-plugin-oxlint": "1.39.0",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This file is auto-generated by @tailor-platform/sdk
|
|
2
|
+
// Do not edit this file manually
|
|
3
|
+
// Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
|
|
4
|
+
|
|
5
|
+
declare module "@tailor-platform/sdk" {
|
|
6
|
+
interface AttributeMap {
|
|
7
|
+
role: "MANAGER" | "STAFF";
|
|
8
|
+
}
|
|
9
|
+
interface AttributeList {
|
|
10
|
+
__tuple?: [];
|
|
11
|
+
}
|
|
12
|
+
interface Env {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {};
|