@tailor-platform/sdk 0.0.1 → 0.8.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 +784 -0
- package/LICENSE +21 -0
- package/README.md +8 -41
- package/dist/auth-Di3vQUrT.mjs +743 -0
- package/dist/cli/api.d.mts +213 -0
- package/dist/cli/api.mjs +4 -0
- package/dist/cli/index.d.mts +3 -0
- package/dist/cli/index.mjs +996 -0
- package/dist/configure/index.d.mts +5 -0
- package/dist/configure/index.mjs +108 -0
- package/dist/index-D-0knE68.d.mts +232 -0
- package/dist/plugin-generated.d.ts +14 -0
- package/dist/token-PbgBrNwb.mjs +5498 -0
- package/dist/types-DSAthMLp.d.mts +1389 -0
- package/dist/utils/test/index.d.mts +40 -0
- package/dist/utils/test/index.mjs +63 -0
- package/docs/cli-reference.md +484 -0
- package/docs/configuration.md +132 -0
- package/docs/core-concepts.md +504 -0
- package/docs/quickstart.md +96 -0
- package/docs/testing.md +298 -0
- package/package.json +87 -8
- package/postinstall.mjs +87 -0
package/docs/testing.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# Testing Guide
|
|
2
|
+
|
|
3
|
+
This guide covers testing patterns for Tailor Platform SDK applications using [Vitest](https://vitest.dev/). For a complete working example with full test code, use:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @tailor-platform/sdk <your-project-name> --template testing
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Unit Tests
|
|
10
|
+
|
|
11
|
+
Unit tests verify resolver logic without requiring deployment.
|
|
12
|
+
|
|
13
|
+
### Simple Resolver Testing
|
|
14
|
+
|
|
15
|
+
Test resolvers by directly calling `resolver.body()` with mock inputs.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { unauthenticatedTailorUser } from "@tailor-platform/sdk";
|
|
19
|
+
import resolver from "../src/resolver/add";
|
|
20
|
+
|
|
21
|
+
describe("add resolver", () => {
|
|
22
|
+
test("basic functionality", async () => {
|
|
23
|
+
const result = await resolver.body({
|
|
24
|
+
input: { left: 1, right: 2 },
|
|
25
|
+
user: unauthenticatedTailorUser,
|
|
26
|
+
});
|
|
27
|
+
expect(result).toBe(3);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Key points:**
|
|
33
|
+
|
|
34
|
+
- Use `unauthenticatedTailorUser` for testing logic that doesn't depend on user context
|
|
35
|
+
- **Best for:** Calculations, data transformations without database dependencies
|
|
36
|
+
|
|
37
|
+
### Mock TailorDB Client
|
|
38
|
+
|
|
39
|
+
Mock the global `tailordb.Client` using `vi.stubGlobal()` to simulate database operations and control responses for each query.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { unauthenticatedTailorUser } from "@tailor-platform/sdk";
|
|
43
|
+
import resolver from "../src/resolver/incrementUserAge";
|
|
44
|
+
|
|
45
|
+
describe("incrementUserAge resolver", () => {
|
|
46
|
+
const mockQueryObject = vi.fn();
|
|
47
|
+
|
|
48
|
+
beforeAll(() => {
|
|
49
|
+
vi.stubGlobal("tailordb", {
|
|
50
|
+
Client: vi.fn(
|
|
51
|
+
class {
|
|
52
|
+
connect = vi.fn();
|
|
53
|
+
end = vi.fn();
|
|
54
|
+
queryObject = mockQueryObject;
|
|
55
|
+
},
|
|
56
|
+
),
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterAll(() => {
|
|
61
|
+
vi.unstubAllGlobals();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
mockQueryObject.mockReset();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("basic functionality", async () => {
|
|
69
|
+
// Mock database responses for each query in sequence
|
|
70
|
+
mockQueryObject.mockResolvedValueOnce({}); // Begin transaction
|
|
71
|
+
mockQueryObject.mockResolvedValueOnce({ rows: [{ age: 30 }] }); // Select
|
|
72
|
+
mockQueryObject.mockResolvedValueOnce({}); // Update
|
|
73
|
+
mockQueryObject.mockResolvedValueOnce({}); // Commit
|
|
74
|
+
|
|
75
|
+
const result = await resolver.body({
|
|
76
|
+
input: { email: "test@example.com" },
|
|
77
|
+
user: unauthenticatedTailorUser,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(result).toEqual({ oldAge: 30, newAge: 31 });
|
|
81
|
+
expect(mockQueryObject).toHaveBeenCalledTimes(4);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Key points:**
|
|
87
|
+
|
|
88
|
+
- Control exact database responses (query results, errors)
|
|
89
|
+
- Verify database interaction flow (transactions, queries)
|
|
90
|
+
- Test transaction rollback scenarios
|
|
91
|
+
- **Best for:** Business logic with simple database operations
|
|
92
|
+
|
|
93
|
+
### Dependency Injection Pattern
|
|
94
|
+
|
|
95
|
+
Extract database operations into a `DbOperations` interface, allowing business logic to be tested independently from Kysely implementation.
|
|
96
|
+
|
|
97
|
+
First, structure your resolver to accept database operations:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { createResolver, t } from "@tailor-platform/sdk";
|
|
101
|
+
import { getDB } from "generated/tailordb";
|
|
102
|
+
|
|
103
|
+
export interface DbOperations {
|
|
104
|
+
transaction: (fn: (ops: DbOperations) => Promise<unknown>) => Promise<void>;
|
|
105
|
+
getUser: (
|
|
106
|
+
email: string,
|
|
107
|
+
forUpdate: boolean,
|
|
108
|
+
) => Promise<{ email: string; age: number }>;
|
|
109
|
+
updateUser: (user: { email: string; age: number }) => Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function decrementUserAge(
|
|
113
|
+
email: string,
|
|
114
|
+
dbOperations: DbOperations,
|
|
115
|
+
): Promise<{ oldAge: number; newAge: number }> {
|
|
116
|
+
let oldAge: number;
|
|
117
|
+
let newAge: number;
|
|
118
|
+
|
|
119
|
+
await dbOperations.transaction(async (ops) => {
|
|
120
|
+
const user = await ops.getUser(email, true);
|
|
121
|
+
oldAge = user.age;
|
|
122
|
+
newAge = user.age - 1;
|
|
123
|
+
await ops.updateUser({ ...user, age: newAge });
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return { oldAge, newAge };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default createResolver({
|
|
130
|
+
name: "decrementUserAge",
|
|
131
|
+
operation: "mutation",
|
|
132
|
+
input: { email: t.string() },
|
|
133
|
+
body: async (context) => {
|
|
134
|
+
const db = getDB("tailordb");
|
|
135
|
+
const dbOperations = createDbOperations(db);
|
|
136
|
+
return await decrementUserAge(context.input.email, dbOperations);
|
|
137
|
+
},
|
|
138
|
+
output: t.object({ oldAge: t.number(), newAge: t.number() }),
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Then test by mocking the interface:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import {
|
|
146
|
+
DbOperations,
|
|
147
|
+
decrementUserAge,
|
|
148
|
+
} from "../src/resolver/decrementUserAge";
|
|
149
|
+
|
|
150
|
+
describe("decrementUserAge resolver", () => {
|
|
151
|
+
test("basic functionality", async () => {
|
|
152
|
+
// Mock DbOperations implementation
|
|
153
|
+
const dbOperations = {
|
|
154
|
+
transaction: vi.fn(
|
|
155
|
+
async (fn: (ops: DbOperations) => Promise<unknown>) =>
|
|
156
|
+
await fn(dbOperations),
|
|
157
|
+
),
|
|
158
|
+
getUser: vi
|
|
159
|
+
.fn()
|
|
160
|
+
.mockResolvedValue({ email: "test@example.com", age: 30 }),
|
|
161
|
+
updateUser: vi.fn(),
|
|
162
|
+
} as DbOperations;
|
|
163
|
+
|
|
164
|
+
const result = await decrementUserAge("test@example.com", dbOperations);
|
|
165
|
+
|
|
166
|
+
expect(result).toEqual({ oldAge: 30, newAge: 29 });
|
|
167
|
+
expect(dbOperations.getUser).toHaveBeenCalledExactlyOnceWith(
|
|
168
|
+
"test@example.com",
|
|
169
|
+
true,
|
|
170
|
+
);
|
|
171
|
+
expect(dbOperations.updateUser).toHaveBeenCalledExactlyOnceWith(
|
|
172
|
+
expect.objectContaining({ age: 29 }),
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Key points:**
|
|
179
|
+
|
|
180
|
+
- Test business logic independently from Kysely implementation details
|
|
181
|
+
- Mock high-level operations instead of low-level SQL queries
|
|
182
|
+
- **Best for:** Complex business logic with multiple database operations
|
|
183
|
+
|
|
184
|
+
## End-to-End (E2E) Tests
|
|
185
|
+
|
|
186
|
+
E2E tests verify your application works correctly when deployed to Tailor Platform. They test the full stack including GraphQL API, database operations, and authentication.
|
|
187
|
+
|
|
188
|
+
### Setting Up E2E Tests
|
|
189
|
+
|
|
190
|
+
**1. Global Setup**
|
|
191
|
+
|
|
192
|
+
Create a global setup file that retrieves deployment information before running tests:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// e2e/globalSetup.ts
|
|
196
|
+
import { machineUserToken, show } from "@tailor-platform/sdk/cli";
|
|
197
|
+
import type { TestProject } from "vitest/node";
|
|
198
|
+
|
|
199
|
+
declare module "vitest" {
|
|
200
|
+
export interface ProvidedContext {
|
|
201
|
+
url: string;
|
|
202
|
+
token: string;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function setup(project: TestProject) {
|
|
207
|
+
const app = await show();
|
|
208
|
+
const tokens = await machineUserToken({
|
|
209
|
+
name: "admin",
|
|
210
|
+
});
|
|
211
|
+
project.provide("url", app.url);
|
|
212
|
+
project.provide("token", tokens.accessToken);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**2. Test Files**
|
|
217
|
+
|
|
218
|
+
Create tests that use injected credentials to send real queries to your deployed application:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// e2e/resolver.test.ts
|
|
222
|
+
import { randomUUID } from "node:crypto";
|
|
223
|
+
import { gql, GraphQLClient } from "graphql-request";
|
|
224
|
+
import { describe, expect, inject, test } from "vitest";
|
|
225
|
+
|
|
226
|
+
function createGraphQLClient() {
|
|
227
|
+
const endpoint = new URL("/query", inject("url")).href;
|
|
228
|
+
return new GraphQLClient(endpoint, {
|
|
229
|
+
headers: {
|
|
230
|
+
Authorization: `Bearer ${inject("token")}`,
|
|
231
|
+
},
|
|
232
|
+
errorPolicy: "all",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
describe("resolver", () => {
|
|
237
|
+
const graphQLClient = createGraphQLClient();
|
|
238
|
+
|
|
239
|
+
describe("incrementUserAge", () => {
|
|
240
|
+
const uuid = randomUUID();
|
|
241
|
+
|
|
242
|
+
test("prepare data", async () => {
|
|
243
|
+
const query = gql`
|
|
244
|
+
mutation {
|
|
245
|
+
createUser(input: {
|
|
246
|
+
name: "alice"
|
|
247
|
+
email: "alice-${uuid}@example.com"
|
|
248
|
+
age: 30
|
|
249
|
+
}) {
|
|
250
|
+
id
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
`;
|
|
254
|
+
const result = await graphQLClient.rawRequest(query);
|
|
255
|
+
expect(result.errors).toBeUndefined();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("basic functionality", async () => {
|
|
259
|
+
const query = gql`
|
|
260
|
+
mutation {
|
|
261
|
+
incrementUserAge(email: "alice-${uuid}@example.com") {
|
|
262
|
+
oldAge
|
|
263
|
+
newAge
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
`;
|
|
267
|
+
const result = await graphQLClient.rawRequest(query);
|
|
268
|
+
expect(result.errors).toBeUndefined();
|
|
269
|
+
expect(result.data).toEqual({
|
|
270
|
+
incrementUserAge: { oldAge: 30, newAge: 31 },
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**3. Vitest Configuration**
|
|
278
|
+
|
|
279
|
+
Configure Vitest to use the global setup:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { defineConfig } from "vitest/config";
|
|
283
|
+
|
|
284
|
+
export default defineConfig({
|
|
285
|
+
test: {
|
|
286
|
+
include: ["e2e/**/*.test.ts"],
|
|
287
|
+
globalSetup: ["e2e/globalSetup.ts"],
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Key points:**
|
|
293
|
+
|
|
294
|
+
- Tests run against actual deployed application
|
|
295
|
+
- `inject("url")` and `inject("token")` provide deployment credentials automatically
|
|
296
|
+
- Machine user authentication enables API access without manual token management
|
|
297
|
+
- Verify database persistence and API contracts
|
|
298
|
+
- **Best for:** Integration testing, end-to-end API validation
|
package/package.json
CHANGED
|
@@ -1,10 +1,89 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailor-platform/sdk",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/configure/index.mjs",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/tailor-platform/sdk.git",
|
|
11
|
+
"directory": "packages/sdk"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"postinstall.mjs",
|
|
16
|
+
"docs",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/configure/index.d.mts",
|
|
23
|
+
"import": "./dist/configure/index.mjs",
|
|
24
|
+
"default": "./dist/configure/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./cli": {
|
|
27
|
+
"types": "./dist/cli/api.d.mts",
|
|
28
|
+
"import": "./dist/cli/api.mjs",
|
|
29
|
+
"default": "./dist/cli/api.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./test": {
|
|
32
|
+
"types": "./dist/utils/test/index.d.mts",
|
|
33
|
+
"import": "./dist/utils/test/index.mjs",
|
|
34
|
+
"default": "./dist/utils/test/index.mjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"bin": {
|
|
38
|
+
"tailor-sdk": "./dist/cli/index.mjs"
|
|
39
|
+
},
|
|
40
|
+
"types": "./dist/configure/index.d.mts",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@bufbuild/protobuf": "2.10.0",
|
|
43
|
+
"@connectrpc/connect": "2.1.0",
|
|
44
|
+
"@connectrpc/connect-node": "2.1.0",
|
|
45
|
+
"@standard-schema/spec": "1.0.0",
|
|
46
|
+
"@urql/core": "5.2.0",
|
|
47
|
+
"chalk": "5.6.2",
|
|
48
|
+
"chokidar": "4.0.3",
|
|
49
|
+
"citty": "0.1.6",
|
|
50
|
+
"confbox": "0.2.2",
|
|
51
|
+
"consola": "3.4.2",
|
|
52
|
+
"es-toolkit": "1.41.0",
|
|
53
|
+
"inflection": "3.0.2",
|
|
54
|
+
"madge": "8.0.0",
|
|
55
|
+
"multiline-ts": "4.0.1",
|
|
56
|
+
"open": "10.2.0",
|
|
57
|
+
"pkg-types": "2.3.0",
|
|
58
|
+
"rolldown": "1.0.0-beta.41",
|
|
59
|
+
"smol-toml": "1.4.2",
|
|
60
|
+
"table": "6.9.0",
|
|
61
|
+
"ts-cron-validator": "1.1.5",
|
|
62
|
+
"tsx": "4.20.6",
|
|
63
|
+
"type-fest": "5.2.0",
|
|
64
|
+
"uuid": "13.0.0",
|
|
65
|
+
"xdg-basedir": "5.1.0",
|
|
66
|
+
"zod": "4.1.12"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@eslint/js": "9.39.1",
|
|
70
|
+
"@tailor-platform/function-types": "0.7.2",
|
|
71
|
+
"@types/madge": "5.0.3",
|
|
72
|
+
"@types/node": "22.19.0",
|
|
73
|
+
"eslint": "9.39.1",
|
|
74
|
+
"globals": "16.5.0",
|
|
75
|
+
"tsdown": "0.15.6",
|
|
76
|
+
"typescript": "5.9.3",
|
|
77
|
+
"typescript-eslint": "8.46.3",
|
|
78
|
+
"vitest": "4.0.8"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"test": "vitest",
|
|
82
|
+
"build": "tsdown && FORCE_CREATE=1 node postinstall.mjs",
|
|
83
|
+
"lint": "eslint --cache .",
|
|
84
|
+
"lint:fix": "eslint --cache . --fix",
|
|
85
|
+
"typecheck": "tsc --noEmit",
|
|
86
|
+
"prepublish": "pnpm run build",
|
|
87
|
+
"postinstall": "node postinstall.mjs"
|
|
88
|
+
}
|
|
89
|
+
}
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { register } from "node:module";
|
|
7
|
+
|
|
8
|
+
const __dirname = import.meta.dirname;
|
|
9
|
+
|
|
10
|
+
async function install() {
|
|
11
|
+
const cwd = process.env.INIT_CWD || process.cwd();
|
|
12
|
+
const forceCreate = process.env.FORCE_CREATE === "1";
|
|
13
|
+
|
|
14
|
+
// Skip if running in the tailor-sdk package itself (unless forced)
|
|
15
|
+
if (
|
|
16
|
+
!forceCreate &&
|
|
17
|
+
(cwd === __dirname || cwd === resolve(__dirname, "..", ".."))
|
|
18
|
+
) {
|
|
19
|
+
console.log("⚠️ Skipping postinstall in tailor-sdk package itself");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log("🔧 Initializing Tailor Platform SDK type definitions...");
|
|
24
|
+
|
|
25
|
+
// Create plugin-generated.d.ts in the dist directory
|
|
26
|
+
const distDir = resolve(__dirname, "dist");
|
|
27
|
+
const pluginTypesPath = resolve(distDir, "plugin-generated.d.ts");
|
|
28
|
+
|
|
29
|
+
// Check if dist directory exists
|
|
30
|
+
if (!existsSync(distDir)) {
|
|
31
|
+
console.log("⚠️ Dist directory not found, skipping type initialization");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Try to find and load the user's tailor.config.ts
|
|
36
|
+
const configPath = process.env.TAILOR_PLATFORM_SDK_CONFIG_PATH
|
|
37
|
+
? resolve(cwd, process.env.TAILOR_PLATFORM_SDK_CONFIG_PATH)
|
|
38
|
+
: resolve(cwd, "tailor.config.ts");
|
|
39
|
+
|
|
40
|
+
if (existsSync(configPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const configDir = dirname(configPath);
|
|
43
|
+
process.chdir(configDir);
|
|
44
|
+
register("tsx", import.meta.url, { data: {} });
|
|
45
|
+
|
|
46
|
+
const { generateUserTypes, loadConfig } = await import(
|
|
47
|
+
pathToFileURL(resolve(__dirname, "dist", "cli", "api.mjs")).href
|
|
48
|
+
);
|
|
49
|
+
const { config } = await loadConfig(configPath);
|
|
50
|
+
await generateUserTypes(config, configPath);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn("⚠️ Failed to generate types from config:", error.message);
|
|
53
|
+
// Fall through to create empty type definition
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Create empty type definition file as fallback
|
|
58
|
+
const initialContent = `// This file is auto-generated by @tailor-platform/sdk
|
|
59
|
+
// Do not edit this file manually
|
|
60
|
+
// Regenerated automatically when running 'tailor-sdk apply' or 'tailor-sdk generate'
|
|
61
|
+
|
|
62
|
+
declare global {
|
|
63
|
+
namespace TailorSDK {
|
|
64
|
+
interface AttributeMap {}
|
|
65
|
+
interface AttributeList {
|
|
66
|
+
__tuple: string[];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {};
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(dirname(pluginTypesPath), { recursive: true });
|
|
76
|
+
writeFileSync(pluginTypesPath, initialContent);
|
|
77
|
+
console.log("✅ Created initial type definitions");
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error("❌ Failed to create type definitions:", error.message);
|
|
80
|
+
// Don't exit with error - this shouldn't break the installation
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Only run if this is the main module
|
|
85
|
+
if (import.meta.url === new URL(import.meta.url).href) {
|
|
86
|
+
install();
|
|
87
|
+
}
|