@tailor-platform/sdk-plugin-app-shell 0.1.1
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/LICENSE.md +21 -0
- package/README.md +64 -0
- package/dist/index.d.mts +17 -0
- package/dist/index.mjs +119 -0
- package/package.json +54 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2025 Tailor Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @tailor-platform/sdk-plugin-app-shell
|
|
2
|
+
|
|
3
|
+
Tailor Platform SDK plugin that generates `tableMetadata` from TailorDB table definitions for use with `@tailor-platform/app-shell`'s `DataTable` component.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
`tableMetadata` bridges your TailorDB schema to the DataTable. It tells `inferColumns` how to render and filter each field — for example, which fields get a date picker, which get an enum dropdown (and with what options), and which are numeric. Without it, you would need to declare all of this manually per column.
|
|
8
|
+
|
|
9
|
+
The metadata is generated at SDK generate time from your TailorDB table definitions, so it stays in sync with your schema automatically.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add -D @tailor-platform/sdk-plugin-app-shell
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Supports `@tailor-platform/sdk` `^1 || ^2`. The examples below use the v2 CLI and TailorDB table API.
|
|
20
|
+
|
|
21
|
+
Register the plugin in `tailor.config.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { definePlugins } from "@tailor-platform/sdk";
|
|
25
|
+
import { appShellPlugin } from "@tailor-platform/sdk-plugin-app-shell";
|
|
26
|
+
|
|
27
|
+
export const plugins = definePlugins(
|
|
28
|
+
appShellPlugin({
|
|
29
|
+
dataTable: {
|
|
30
|
+
metadataOutputPath: "src/generated/app-shell-datatable.generated.ts",
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then run `tailor generate` to produce the metadata file.
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
Pass `tableMetadata` to `inferColumns` to get type-safe column definitions with filter editors automatically configured:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { tableMetadata } from "@/generated/app-shell-datatable.generated";
|
|
44
|
+
import { createColumnHelper } from "@tailor-platform/app-shell";
|
|
45
|
+
|
|
46
|
+
const { column, inferColumns } = createColumnHelper<Order>();
|
|
47
|
+
const infer = inferColumns(tableMetadata.order);
|
|
48
|
+
|
|
49
|
+
const columns = [
|
|
50
|
+
column(infer("title")), // string column → text filter
|
|
51
|
+
column(infer("status")), // enum column → dropdown filter with generated values
|
|
52
|
+
column(infer("createdAt")), // datetime column → date picker filter
|
|
53
|
+
];
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
You can still override per-column defaults when needed. For example, pass a non-empty `filter.operators` allowlist to narrow the built-in DataTable filter UI for one field:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const columns = [column(infer("title", { filter: { operators: ["contains", "eq"] } }))];
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`operators` only affects the built-in DataTable filter UI. Programmatic `CollectionControl.addFilter(...)`, URL state, and saved/persisted filters still use the broader backend operator set.
|
|
63
|
+
|
|
64
|
+
For full `DataTable` usage, see [`@tailor-platform/app-shell`](../core/README.md).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Plugin } from "@tailor-platform/sdk";
|
|
2
|
+
//#region src/plugin.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Options for the AppShell plugin.
|
|
5
|
+
*/
|
|
6
|
+
interface AppShellPluginOptions {
|
|
7
|
+
dataTable?: {
|
|
8
|
+
/**
|
|
9
|
+
* Output file path for generated DataTable metadata (relative to project root).
|
|
10
|
+
* @default "app-shell-datatable.generated.ts"
|
|
11
|
+
*/
|
|
12
|
+
metadataOutputPath?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
declare function appShellPlugin(options?: AppShellPluginOptions): Plugin<void, AppShellPluginOptions>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { type AppShellPluginOptions, appShellPlugin };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//#region src/plugin.ts
|
|
2
|
+
/**
|
|
3
|
+
* Map TailorDB field type to AppShell FieldType.
|
|
4
|
+
*/
|
|
5
|
+
function mapFieldType(tailorType, isArray) {
|
|
6
|
+
const mappedType = {
|
|
7
|
+
string: "string",
|
|
8
|
+
integer: "number",
|
|
9
|
+
float: "number",
|
|
10
|
+
decimal: "number",
|
|
11
|
+
boolean: "boolean",
|
|
12
|
+
uuid: "uuid",
|
|
13
|
+
datetime: "datetime",
|
|
14
|
+
date: "date",
|
|
15
|
+
time: "time",
|
|
16
|
+
enum: "enum",
|
|
17
|
+
nested: "nested",
|
|
18
|
+
file: "file"
|
|
19
|
+
}[tailorType] ?? "string";
|
|
20
|
+
if (isArray) return {
|
|
21
|
+
type: "array",
|
|
22
|
+
arrayItemType: mappedType
|
|
23
|
+
};
|
|
24
|
+
return { type: mappedType };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert PascalCase to camelCase.
|
|
28
|
+
*/
|
|
29
|
+
function toCamelCase(str) {
|
|
30
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
31
|
+
}
|
|
32
|
+
function appShellPlugin(options = {}) {
|
|
33
|
+
const { metadataOutputPath = "app-shell-datatable.generated.ts" } = options.dataTable ?? {};
|
|
34
|
+
return {
|
|
35
|
+
id: "app-shell-metadata",
|
|
36
|
+
description: "Generates table metadata for AppShell DataTable (inferColumns, createColumnHelper)",
|
|
37
|
+
pluginConfig: options,
|
|
38
|
+
onTailorDBReady(context) {
|
|
39
|
+
const metadataMap = {};
|
|
40
|
+
for (const ns of context.tailordb) {
|
|
41
|
+
const schemaEntries = "tables" in ns ? ns.tables : ns.types;
|
|
42
|
+
for (const type of Object.values(schemaEntries)) {
|
|
43
|
+
const fields = [];
|
|
44
|
+
const relations = [];
|
|
45
|
+
for (const [fieldName, field] of Object.entries(type.fields)) {
|
|
46
|
+
const config = field.config;
|
|
47
|
+
const { type: fieldType, arrayItemType } = mapFieldType(config.type, config.array);
|
|
48
|
+
let fieldRelation;
|
|
49
|
+
if (config.rawRelation) {
|
|
50
|
+
const raw = config.rawRelation;
|
|
51
|
+
if ((raw.type === "manyToOne" || raw.type === "n-1" || raw.type === "N-1" || raw.type === "oneToOne" || raw.type === "1-1") && raw.toward) {
|
|
52
|
+
const targetName = raw.toward.table ?? raw.toward.type;
|
|
53
|
+
if (targetName) {
|
|
54
|
+
const targetTableName = toCamelCase(targetName);
|
|
55
|
+
fieldRelation = {
|
|
56
|
+
fieldName: raw.toward.as ?? targetTableName,
|
|
57
|
+
targetTable: targetTableName
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const enumValues = config.allowedValues && config.allowedValues.length > 0 ? config.allowedValues.map((v) => typeof v === "string" ? v : v.value) : void 0;
|
|
63
|
+
const fieldMetadata = {
|
|
64
|
+
name: fieldName,
|
|
65
|
+
type: fieldType,
|
|
66
|
+
required: config.required ?? false,
|
|
67
|
+
...config.description && { description: config.description },
|
|
68
|
+
...enumValues && { enumValues },
|
|
69
|
+
...arrayItemType && { arrayItemType },
|
|
70
|
+
...fieldRelation && { relation: fieldRelation }
|
|
71
|
+
};
|
|
72
|
+
fields.push(fieldMetadata);
|
|
73
|
+
}
|
|
74
|
+
for (const [relName, rel] of Object.entries(type.forwardRelationships ?? {})) {
|
|
75
|
+
const fkField = type.fields[rel.sourceField];
|
|
76
|
+
const isOneToOne = fkField?.config.rawRelation?.type === "1-1" || fkField?.config.rawRelation?.type === "oneToOne";
|
|
77
|
+
relations.push({
|
|
78
|
+
fieldName: relName,
|
|
79
|
+
targetTable: toCamelCase(rel.targetType),
|
|
80
|
+
relationType: isOneToOne ? "oneToOne" : "manyToOne",
|
|
81
|
+
foreignKeyField: rel.sourceField
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
for (const [relName, rel] of Object.entries(type.backwardRelationships ?? {})) relations.push({
|
|
85
|
+
fieldName: relName,
|
|
86
|
+
targetTable: toCamelCase(rel.targetType),
|
|
87
|
+
relationType: "oneToMany",
|
|
88
|
+
foreignKeyField: rel.sourceField
|
|
89
|
+
});
|
|
90
|
+
const camelName = toCamelCase(type.name);
|
|
91
|
+
metadataMap[camelName] = {
|
|
92
|
+
name: camelName,
|
|
93
|
+
pluralForm: toCamelCase(type.pluralForm),
|
|
94
|
+
...type.description && { description: type.description },
|
|
95
|
+
fields,
|
|
96
|
+
...relations.length > 0 && { relations }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const content = [
|
|
101
|
+
"// This file is auto-generated by app-shell-metadata plugin",
|
|
102
|
+
"// Do not edit manually",
|
|
103
|
+
"",
|
|
104
|
+
`export const tableMetadata = ${JSON.stringify(metadataMap, null, 2)} as const;`,
|
|
105
|
+
"",
|
|
106
|
+
`export const tableNames = ${JSON.stringify(Object.keys(metadataMap), null, 2)} as const;`,
|
|
107
|
+
"",
|
|
108
|
+
`export type TableName = (typeof tableNames)[number];`,
|
|
109
|
+
""
|
|
110
|
+
].join("\n");
|
|
111
|
+
return { files: [{
|
|
112
|
+
path: metadataOutputPath,
|
|
113
|
+
content
|
|
114
|
+
}] };
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { appShellPlugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tailor-platform/sdk-plugin-app-shell",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Tailor Platform SDK plugins for AppShell (metadata generator)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"app-shell",
|
|
7
|
+
"metadata-generator",
|
|
8
|
+
"sdk-plugin",
|
|
9
|
+
"tailor-platform"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/tailor-platform/app-shell/tree/main/packages/sdk-plugin#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/tailor-platform"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/tailor-platform/app-shell",
|
|
19
|
+
"directory": "packages/sdk-plugin"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/**"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.mts",
|
|
28
|
+
"default": "./dist/index.mjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@tailor-platform/sdk": "^2.7.0",
|
|
37
|
+
"@tailor-platform/sdk-v1": "npm:@tailor-platform/sdk@1.85.1",
|
|
38
|
+
"oxlint": "^1.64.0",
|
|
39
|
+
"tsdown": "^0.22.14",
|
|
40
|
+
"typescript": "5.9.3",
|
|
41
|
+
"vitest": "^4.1.10",
|
|
42
|
+
"@tailor-platform/app-shell": "1.13.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@tailor-platform/sdk": "^1.0.0 || ^2.0.0"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"dev": "tsdown --watch",
|
|
49
|
+
"build": "tsdown",
|
|
50
|
+
"lint": "oxlint -c .oxlintrc.jsonc",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"type-check": "tsc --incremental"
|
|
53
|
+
}
|
|
54
|
+
}
|