cloesce 0.0.3 → 0.0.4-unstable.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/README.md +488 -23
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +221 -254
- package/dist/common.d.ts +69 -1
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +72 -11
- package/dist/{extract.d.ts → extractor/extract.d.ts} +5 -2
- package/dist/extractor/extract.d.ts.map +1 -0
- package/dist/{extract.js → extractor/extract.js} +242 -43
- package/dist/generator.wasm +0 -0
- package/dist/orm.wasm +0 -0
- package/dist/router/crud.d.ts +22 -0
- package/dist/router/crud.d.ts.map +1 -0
- package/dist/router/crud.js +65 -0
- package/dist/router/router.d.ts +77 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +358 -0
- package/dist/router/wasm.d.ts +37 -0
- package/dist/router/wasm.d.ts.map +1 -0
- package/dist/router/wasm.js +98 -0
- package/dist/ui/backend.d.ts +124 -0
- package/dist/ui/backend.d.ts.map +1 -0
- package/dist/ui/backend.js +201 -0
- package/dist/ui/client.d.ts +5 -0
- package/dist/ui/client.d.ts.map +1 -0
- package/dist/ui/client.js +7 -0
- package/package.json +70 -58
- package/LICENSE +0 -201
- package/dist/cli.wasm +0 -0
- package/dist/cloesce.d.ts +0 -108
- package/dist/cloesce.d.ts.map +0 -1
- package/dist/cloesce.js +0 -453
- package/dist/decorators.d.ts +0 -13
- package/dist/decorators.d.ts.map +0 -1
- package/dist/decorators.js +0 -13
- package/dist/dog.cloesce.js +0 -111
- package/dist/extract.d.ts.map +0 -1
- package/dist/index.d.ts +0 -24
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -24
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/README.md
CHANGED
|
@@ -1,23 +1,488 @@
|
|
|
1
|
-
# cloesce
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
# cloesce (unstable, `v0.0.4`)
|
|
2
|
+
|
|
3
|
+
Cloesce is a full stack compiler for the Cloudflare developer platform, allowing class definitions in high level languages to serve as a metadata basis to create a database schema, backend REST API, frontend API client, and Cloudflare infrastructure (as of v0.0.4, D1 + Workers).
|
|
4
|
+
|
|
5
|
+
Cloesce is working towards an alpha MVP (v0.1.0), with the general milestones being [here](https://cloesce.pages.dev/schreiber/v0.1.0_milestones/).
|
|
6
|
+
|
|
7
|
+
Internal documentation going over design decisions and general thoughts for each milestone can be found [here](https://cloesce.pages.dev/).
|
|
8
|
+
|
|
9
|
+
# Documentation `v0.0.4`
|
|
10
|
+
|
|
11
|
+
## Getting Started
|
|
12
|
+
|
|
13
|
+
`v0.0.4` supports only Typescript-to-Typescript projects. An example project is shown [here](https://github.com/bens-schreiber/cloesce/tree/main/examples).
|
|
14
|
+
|
|
15
|
+
1. NPM
|
|
16
|
+
|
|
17
|
+
- Create an NPM project and install cloesce
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
# check https://www.npmjs.com/package/cloesce/v/0.0.4-unstable.0?activeTab=versions for the most recent patch
|
|
21
|
+
npm i cloesce@0.0.4-unstable.0
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. TypeScript
|
|
25
|
+
|
|
26
|
+
- Create a `tsconfig.json` with the following values:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"compilerOptions": {
|
|
31
|
+
// ...
|
|
32
|
+
"resolveJsonModule": true,
|
|
33
|
+
"strict": true,
|
|
34
|
+
"strictPropertyInitialization": false,
|
|
35
|
+
"experimentalDecorators": true,
|
|
36
|
+
"emitDecoratorMetadata": true
|
|
37
|
+
},
|
|
38
|
+
"include": ["<your_src_dir>/**/*.ts", ".generated/*.ts"]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. Cloesce Config
|
|
43
|
+
|
|
44
|
+
- Create a `cloesce.config.json` with the following keys:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"source": "./src",
|
|
49
|
+
"workersUrl": "http://localhost:5000/api",
|
|
50
|
+
"clientUrl": "http://localhost:5173/api"
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
4. Vite
|
|
55
|
+
|
|
56
|
+
To prevent CORS issues, a Vite proxy can be used for the frontend:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { defineConfig } from "vite";
|
|
60
|
+
|
|
61
|
+
export default defineConfig({
|
|
62
|
+
server: {
|
|
63
|
+
port: 5173,
|
|
64
|
+
proxy: {
|
|
65
|
+
"/api": {
|
|
66
|
+
target: "http://localhost:5000",
|
|
67
|
+
changeOrigin: true,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
5. Wrangler Config
|
|
75
|
+
|
|
76
|
+
- `v0.0.4` will generate the required areas of your wrangler config. A full config looks like this:
|
|
77
|
+
|
|
78
|
+
```toml
|
|
79
|
+
compatibility_date = "2025-10-02"
|
|
80
|
+
main = ".generated/workers.ts"
|
|
81
|
+
name = "example"
|
|
82
|
+
|
|
83
|
+
[[d1_databases]]
|
|
84
|
+
binding = "db"
|
|
85
|
+
database_id = "..."
|
|
86
|
+
database_name = "example"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## A Simple Model
|
|
90
|
+
|
|
91
|
+
A model is a type which represents:
|
|
92
|
+
|
|
93
|
+
- a database table,
|
|
94
|
+
- database views
|
|
95
|
+
- REST API
|
|
96
|
+
- Client API
|
|
97
|
+
- Cloudflare infrastructure (D1 + Workers)
|
|
98
|
+
|
|
99
|
+
Suprisingly, it's pretty compact. A basic model looks like this:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// horse.cloesce.ts
|
|
103
|
+
import { D1, GET, POST, PrimaryKey } from "cloesce/backend";
|
|
104
|
+
|
|
105
|
+
@D1
|
|
106
|
+
export class Horse {
|
|
107
|
+
@PrimaryKey
|
|
108
|
+
id: number;
|
|
109
|
+
|
|
110
|
+
name: string | null;
|
|
111
|
+
|
|
112
|
+
@POST
|
|
113
|
+
neigh(): string {
|
|
114
|
+
return `i am ${this.name}, this is my horse noise`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- `@D1` denotes that this is a SQL Table
|
|
120
|
+
- `@PrimaryKey` sets the SQL primary key. All models require a primary key.
|
|
121
|
+
- `@POST` reveals the method as an API endpoint with the `POST` HTTP Verb.
|
|
122
|
+
- All Cloesce models need to be under a `.cloesce.ts` file.
|
|
123
|
+
|
|
124
|
+
To compile this model into a working full stack application, Cloesce must undergo both **compilation** and **migrations**. Compilation is the process of extracting the metadata language that powers Cloesce, ensuring it is a valid program, and then producing code to orchestrate the program across different domains (database, backend, frontend, cloud). Migrations utilize the history of validated metadata to create SQL code, translating the evolution of your models.
|
|
125
|
+
|
|
126
|
+
To compile, run `npx cloesce compile`.
|
|
127
|
+
|
|
128
|
+
To create a migration, run `npx cloesce migrate <name>`.
|
|
129
|
+
|
|
130
|
+
After running the above commands, you will have a full project capable of being ran with:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
# Apply the generated migrations
|
|
134
|
+
npx wrangler d1 migrations apply <db-name>
|
|
135
|
+
|
|
136
|
+
# Run the backend
|
|
137
|
+
npx wrangler dev
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Note the output in the `.generated/` dir. These values should not be committed to git, as they depend on the file system of the machine running it.
|
|
141
|
+
|
|
142
|
+
- `client.ts` is an importable API with all of your backend types and endpoints
|
|
143
|
+
- `workers.ts` is the workers entrypoint.
|
|
144
|
+
- `cidl.json` is the working metadata for the project
|
|
145
|
+
|
|
146
|
+
Note the output in `migrations`, ex after running `npx cloesce migrate Initial`
|
|
147
|
+
|
|
148
|
+
- `<date>_Initial.json` contains all model information necessary from SQL
|
|
149
|
+
- `<date>_Initial.sql` contains the acual SQL migrations. In this early version of Cloesce, it's important to check migrations every time.
|
|
150
|
+
|
|
151
|
+
## Features
|
|
152
|
+
|
|
153
|
+
### Wrangler Environment
|
|
154
|
+
|
|
155
|
+
In order to interact with your database, you will need to define a WranglerEnv
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
// horse.cloesce.ts
|
|
159
|
+
|
|
160
|
+
import { WranglerEnv } from "cloesce/backend";
|
|
161
|
+
|
|
162
|
+
@WranglerEnv
|
|
163
|
+
export class Env {
|
|
164
|
+
db: D1Database; // only one DB is supported for now-- make sure it matches the name in `wrangler.toml`
|
|
165
|
+
|
|
166
|
+
// you can also define values in the toml under [[vars]]
|
|
167
|
+
motd: string;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The wrangler environment is dependency injected into your method calls:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
@D1
|
|
175
|
+
export class Horse {
|
|
176
|
+
@PrimaryKey
|
|
177
|
+
id: number;
|
|
178
|
+
|
|
179
|
+
@POST
|
|
180
|
+
async neigh(@Inject env: WranglerEnv): Promise<string> {
|
|
181
|
+
await env.db.prepare(...);
|
|
182
|
+
|
|
183
|
+
return `i am ${this.name}, this is my horse noise`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Foreign Keys, One to One, Data Sources
|
|
189
|
+
|
|
190
|
+
Complex model relationships are permitted via the `@ForeignKey`, `@OneToOne / @OneToMany @ManyToMany` and `@DataSource` decorators.
|
|
191
|
+
Foreign keys are scalar attributes which must reference some other model's primary key:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
@D1
|
|
195
|
+
export class Dog {
|
|
196
|
+
@PrimaryKey
|
|
197
|
+
id: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@D1
|
|
201
|
+
export class Person {
|
|
202
|
+
@PrimaryKey
|
|
203
|
+
id: number;
|
|
204
|
+
|
|
205
|
+
@ForeignKey(Dog)
|
|
206
|
+
dogId: number;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
This representation is true to the underlying SQL table: `Person` has a column `dogId` which is a foreign key to `Dog`. Cloesce allows you to actually join these tables together in your model representation:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
@D1
|
|
214
|
+
export class Dog {
|
|
215
|
+
@PrimaryKey
|
|
216
|
+
id: number;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
@D1
|
|
220
|
+
export class Person {
|
|
221
|
+
@PrimaryKey
|
|
222
|
+
id: number;
|
|
223
|
+
|
|
224
|
+
@ForeignKey(Dog)
|
|
225
|
+
dogId: number;
|
|
226
|
+
|
|
227
|
+
@OneToOne("dogId") // references Person.dogId
|
|
228
|
+
dog: Dog | undefined; // This value is a "navigation property", which may or may not exist at runtime
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
In `v0.0.4`, there are no defaults, only very explicit decisons. Because of that, navigation properties won't exist at runtime unless you tell them to. Cloesce does this via a `DataSource`, which describes the foreign key dependencies you wish to include. All scalar properties are included by default and cannot be excluded.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
@D1
|
|
236
|
+
export class Dog {
|
|
237
|
+
@PrimaryKey
|
|
238
|
+
id: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@D1
|
|
242
|
+
export class Person {
|
|
243
|
+
@PrimaryKey
|
|
244
|
+
id: number;
|
|
245
|
+
|
|
246
|
+
@ForeignKey(Dog)
|
|
247
|
+
dogId: number;
|
|
248
|
+
|
|
249
|
+
@OneToOne("dogId")
|
|
250
|
+
dog: Dog | undefined;
|
|
251
|
+
|
|
252
|
+
@DataSource
|
|
253
|
+
static readonly default: IncludeTree<Person> = {
|
|
254
|
+
dog: {}, // says: on model population, join Persons's Dog
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Data sources are just SQL views and can be invoked in your queries. They are aliased in such a way that its similiar to object properties. The frontend chooses which datasource to use in it's API client. `null` is a valid option, meaning no joins will occur.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
@D1
|
|
263
|
+
export class Person {
|
|
264
|
+
@PrimaryKey
|
|
265
|
+
id: number;
|
|
266
|
+
|
|
267
|
+
@ForeignKey(Dog)
|
|
268
|
+
dogId: number;
|
|
269
|
+
|
|
270
|
+
@OneToOne("dogId")
|
|
271
|
+
dog: Dog | undefined;
|
|
272
|
+
|
|
273
|
+
@DataSource
|
|
274
|
+
static readonly default: IncludeTree<Person> = {
|
|
275
|
+
dog: {},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
@GET
|
|
279
|
+
static async get(id: number, @Inject env: WranglerEnv): Promise<Person> {
|
|
280
|
+
let records = await env.db
|
|
281
|
+
.prepare("SELECT * FROM [Person.default] WHERE [Person.id] = ?") // Person.default is the SQL view generated from the IncludeTree
|
|
282
|
+
.bind(id)
|
|
283
|
+
.run();
|
|
284
|
+
|
|
285
|
+
let persons = Orm.fromSql(Person, records.results, Person.default);
|
|
286
|
+
return persons.value[0];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Note that the `get` code can be simplified using CRUD methods or ORM primitives.
|
|
292
|
+
|
|
293
|
+
### One to Many
|
|
294
|
+
|
|
295
|
+
Cloesce supports models with `1:M` relationships:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
@D1
|
|
299
|
+
export class Person {
|
|
300
|
+
@PrimaryKey
|
|
301
|
+
id: number;
|
|
302
|
+
|
|
303
|
+
@OneToMany("personId") // directly references the FK on Dog
|
|
304
|
+
dogs: Dog[];
|
|
305
|
+
|
|
306
|
+
@DataSource
|
|
307
|
+
static readonly default: IncludeTree<Person> = {
|
|
308
|
+
dogs: {
|
|
309
|
+
person: {
|
|
310
|
+
dogs: {
|
|
311
|
+
// essentially means: "When you get a person, get their dogs, and get all of those dog's Person, ..."
|
|
312
|
+
// we could go on as long as we want
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@D1
|
|
320
|
+
export class Dog {
|
|
321
|
+
@PrimaryKey
|
|
322
|
+
id: number;
|
|
323
|
+
|
|
324
|
+
@ForeignKey(Person)
|
|
325
|
+
personId: number;
|
|
326
|
+
|
|
327
|
+
// optional navigation property, not needed.
|
|
328
|
+
@OneToOne("personId")
|
|
329
|
+
person: Person | undefined;
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Many to Many
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
@D1
|
|
337
|
+
export class Student {
|
|
338
|
+
@PrimaryKey
|
|
339
|
+
id: number;
|
|
340
|
+
|
|
341
|
+
@ManyToMany("StudentsCourses") // unique ID for the generated junction table
|
|
342
|
+
courses: Course[];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@D1
|
|
346
|
+
export class Course {
|
|
347
|
+
@PrimaryKey
|
|
348
|
+
id: number;
|
|
349
|
+
|
|
350
|
+
@ManyToMany("StudentsCourses") // same unique id => same jct table.
|
|
351
|
+
students: Student[];
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### ORM Methods
|
|
356
|
+
|
|
357
|
+
Cloesce provides a suite of ORM methods for getting, listing, updating and inserting models.
|
|
358
|
+
|
|
359
|
+
#### Upsert
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
@D1
|
|
363
|
+
class Horse {
|
|
364
|
+
// ...
|
|
365
|
+
|
|
366
|
+
@POST
|
|
367
|
+
static async post(@Inject { db }: Env, horse: Horse): Promise<Horse> {
|
|
368
|
+
const orm = Orm.fromD1(db);
|
|
369
|
+
await orm.upsert(Horse, horse, null);
|
|
370
|
+
return (await orm.get(Horse, horse.id, null)).value;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### List, Get
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
@D1
|
|
379
|
+
class Horse {
|
|
380
|
+
// ...
|
|
381
|
+
@GET
|
|
382
|
+
static async get(@Inject { db }: Env, id: number): Promise<Horse> {
|
|
383
|
+
const orm = Orm.fromD1(db);
|
|
384
|
+
return (await orm.get(Horse, id, "default")).value;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@GET
|
|
388
|
+
static async list(@Inject { db }: Env): Promise<Horse[]> {
|
|
389
|
+
const orm = Orm.fromD1(db);
|
|
390
|
+
return (await orm.list(Horse, "default")).value;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### CRUD Methods
|
|
396
|
+
|
|
397
|
+
Generic GET, POST, PATCH (and in a future version, DEL) boilerplate methods do not need to be copied around. Cloesce supports CRUD generation, a syntactic sugar that adds the methods to the compiler output.
|
|
398
|
+
|
|
399
|
+
```ts
|
|
400
|
+
@CRUD(["POST", "GET", "LIST"])
|
|
401
|
+
@D1
|
|
402
|
+
export class CrudHaver {
|
|
403
|
+
@PrimaryKey
|
|
404
|
+
id: number;
|
|
405
|
+
name: string;
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
which will generate client API methods like:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
static async get(
|
|
413
|
+
id: number,
|
|
414
|
+
dataSource: "none" = "none",
|
|
415
|
+
): Promise<HttpResult<CrudHaver>>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Plain Old Objects
|
|
419
|
+
|
|
420
|
+
Simple non-model objects can be returned and serialized from a model method:
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
@PlainOldObject
|
|
424
|
+
export class CatStuff {
|
|
425
|
+
catFacts: string[],
|
|
426
|
+
catNames: string[],
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
@D1
|
|
430
|
+
export class Cat {
|
|
431
|
+
@PrimaryKey
|
|
432
|
+
id: number;
|
|
433
|
+
|
|
434
|
+
@GET
|
|
435
|
+
query(): CatStuff {
|
|
436
|
+
return {
|
|
437
|
+
catFacts: ["cats r cool"],
|
|
438
|
+
catNames: ["reginald"]
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### HttpResult
|
|
445
|
+
|
|
446
|
+
Methods can return any kind of status code via the `HttpResult` wrapper:
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
@D1
|
|
450
|
+
class Foo {
|
|
451
|
+
...
|
|
452
|
+
|
|
453
|
+
@GET
|
|
454
|
+
async foo(): Promise<HttpResult<number>> {
|
|
455
|
+
return { ok: false, status: 500, message: "divided by 0"};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
# Testing the Compiler
|
|
461
|
+
|
|
462
|
+
## Unit Tests
|
|
463
|
+
|
|
464
|
+
- `src/frontend/ts` run `npm test`
|
|
465
|
+
- `src/generator` run `cargo test`
|
|
466
|
+
|
|
467
|
+
## Integration Tests
|
|
468
|
+
|
|
469
|
+
- Regression tests: `cargo run --bin test regression`
|
|
470
|
+
- Pass fail extractor tests: `cargo run --bin test run-fail`
|
|
471
|
+
|
|
472
|
+
Optionally, pass `--check` if new snapshots should not be created.
|
|
473
|
+
|
|
474
|
+
To update integration snapshots, run:
|
|
475
|
+
|
|
476
|
+
- `cargo run --bin update`
|
|
477
|
+
|
|
478
|
+
To delete any generated snapshots run:
|
|
479
|
+
|
|
480
|
+
- `cargo run --bin update -- -d`
|
|
481
|
+
|
|
482
|
+
## E2E
|
|
483
|
+
|
|
484
|
+
- `tests/e2e` run `npm test`
|
|
485
|
+
|
|
486
|
+
## Code Formatting
|
|
487
|
+
|
|
488
|
+
- `cargo fmt`, `cargo clippy`, `npm run format:fix`
|
package/dist/cli.d.ts
CHANGED