shapecraft 1.0.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 (112) hide show
  1. package/CLAUDE.md +227 -0
  2. package/README.md +22 -0
  3. package/apps/cli/node_modules/.bin/prettier +21 -0
  4. package/apps/cli/node_modules/.bin/tsc +21 -0
  5. package/apps/cli/node_modules/.bin/tsserver +21 -0
  6. package/apps/cli/node_modules/.bin/tsx +21 -0
  7. package/apps/cli/node_modules/.bin/vitest +21 -0
  8. package/apps/cli/package.json +47 -0
  9. package/apps/cli/src/index.ts +98 -0
  10. package/apps/cli/tsconfig.cjs.json +10 -0
  11. package/apps/cli/tsconfig.esm.json +10 -0
  12. package/apps/cli/tsconfig.json +22 -0
  13. package/package.json +16 -0
  14. package/packages/core/node_modules/.bin/prettier +21 -0
  15. package/packages/core/node_modules/.bin/tsc +21 -0
  16. package/packages/core/node_modules/.bin/tsserver +21 -0
  17. package/packages/core/node_modules/.bin/tsx +21 -0
  18. package/packages/core/node_modules/.bin/vitest +21 -0
  19. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  20. package/packages/core/package.json +44 -0
  21. package/packages/core/src/common/array.test.ts +19 -0
  22. package/packages/core/src/common/array.ts +15 -0
  23. package/packages/core/src/common/index.ts +5 -0
  24. package/packages/core/src/common/is.ts +23 -0
  25. package/packages/core/src/common/object.ts +35 -0
  26. package/packages/core/src/common/phantom.ts +1 -0
  27. package/packages/core/src/common/result.ts +43 -0
  28. package/packages/core/src/common/string.ts +28 -0
  29. package/packages/core/src/common/types.ts +34 -0
  30. package/packages/core/src/index.ts +1 -0
  31. package/packages/core/src/shape/annotate.ts +139 -0
  32. package/packages/core/src/shape/annotation.ts +47 -0
  33. package/packages/core/src/shape/base.ts +71 -0
  34. package/packages/core/src/shape/builder.test.ts +728 -0
  35. package/packages/core/src/shape/builder.ts +475 -0
  36. package/packages/core/src/shape/error.ts +4 -0
  37. package/packages/core/src/shape/index.ts +3 -0
  38. package/packages/core/src/shape/number.ts +118 -0
  39. package/packages/core/src/shape/shape.test.ts +792 -0
  40. package/packages/core/src/shape/shape.ts +377 -0
  41. package/packages/core/src/shape/tags.ts +14 -0
  42. package/packages/core/src/shape/transforms/index.ts +3 -0
  43. package/packages/core/src/shape/transforms/json-schema/index.ts +2 -0
  44. package/packages/core/src/shape/transforms/json-schema/transform.test.ts +850 -0
  45. package/packages/core/src/shape/transforms/json-schema/transform.ts +882 -0
  46. package/packages/core/src/shape/transforms/json-schema/types.ts +132 -0
  47. package/packages/core/src/shape/transforms/sql/dialects/dialect.ts +89 -0
  48. package/packages/core/src/shape/transforms/sql/dialects/index.ts +14 -0
  49. package/packages/core/src/shape/transforms/sql/dialects/postgres.ts +392 -0
  50. package/packages/core/src/shape/transforms/sql/dialects/sqlite.ts +333 -0
  51. package/packages/core/src/shape/transforms/sql/from-sql.test.ts +704 -0
  52. package/packages/core/src/shape/transforms/sql/from-sql.ts +210 -0
  53. package/packages/core/src/shape/transforms/sql/index.ts +3 -0
  54. package/packages/core/src/shape/transforms/sql/options.ts +6 -0
  55. package/packages/core/src/shape/transforms/sql/parser/check-decoder.ts +457 -0
  56. package/packages/core/src/shape/transforms/sql/parser/create-domain.ts +105 -0
  57. package/packages/core/src/shape/transforms/sql/parser/create-table.ts +809 -0
  58. package/packages/core/src/shape/transforms/sql/parser/create-type.ts +91 -0
  59. package/packages/core/src/shape/transforms/sql/parser/cursor.ts +179 -0
  60. package/packages/core/src/shape/transforms/sql/parser/default-decoder.ts +129 -0
  61. package/packages/core/src/shape/transforms/sql/parser/lexer.ts +289 -0
  62. package/packages/core/src/shape/transforms/sql/parser/pg-types.ts +247 -0
  63. package/packages/core/src/shape/transforms/sql/parser/sqlite-types.ts +103 -0
  64. package/packages/core/src/shape/transforms/sql/parser/statements.ts +127 -0
  65. package/packages/core/src/shape/transforms/sql/parser/type-spec.ts +159 -0
  66. package/packages/core/src/shape/transforms/sql/transform.sqlite.test.ts +448 -0
  67. package/packages/core/src/shape/transforms/sql/transform.test.ts +880 -0
  68. package/packages/core/src/shape/transforms/sql/transform.ts +295 -0
  69. package/packages/core/src/shape/transforms/typescript/index.ts +1 -0
  70. package/packages/core/src/shape/transforms/typescript/transform.ts +211 -0
  71. package/packages/core/src/shape/tuple.test.ts +171 -0
  72. package/packages/core/src/shape/validate.ts +413 -0
  73. package/packages/core/tsconfig.cjs.json +11 -0
  74. package/packages/core/tsconfig.esm.json +10 -0
  75. package/packages/core/tsconfig.json +23 -0
  76. package/packages/samples/node_modules/.bin/prettier +21 -0
  77. package/packages/samples/node_modules/.bin/tsc +21 -0
  78. package/packages/samples/node_modules/.bin/tsserver +21 -0
  79. package/packages/samples/node_modules/.bin/tsx +21 -0
  80. package/packages/samples/node_modules/.bin/vitest +21 -0
  81. package/packages/samples/package.json +47 -0
  82. package/packages/samples/src/blog.ts +49 -0
  83. package/packages/samples/src/config.ts +50 -0
  84. package/packages/samples/src/ecommerce.ts +65 -0
  85. package/packages/samples/src/embeddings.ts +43 -0
  86. package/packages/samples/src/events.ts +52 -0
  87. package/packages/samples/src/geometry.ts +62 -0
  88. package/packages/samples/src/index.ts +9 -0
  89. package/packages/samples/src/relational.ts +17 -0
  90. package/packages/samples/src/tuples.ts +67 -0
  91. package/packages/samples/src/user.ts +9 -0
  92. package/packages/samples/tsconfig.cjs.json +11 -0
  93. package/packages/samples/tsconfig.esm.json +10 -0
  94. package/packages/samples/tsconfig.json +23 -0
  95. package/pnpm-workspace.yaml +3 -0
  96. package/test-data/json-schema/address.json +35 -0
  97. package/test-data/json-schema/array-of-things.json +36 -0
  98. package/test-data/json-schema/basic.json +21 -0
  99. package/test-data/json-schema/blog-post.json +29 -0
  100. package/test-data/json-schema/calendar.json +48 -0
  101. package/test-data/json-schema/complex-object-with-nested-properties.json +41 -0
  102. package/test-data/json-schema/ecommerce-complex.json +344 -0
  103. package/test-data/json-schema/ecommerce-system.json +27 -0
  104. package/test-data/json-schema/enumerated-values.json +11 -0
  105. package/test-data/json-schema/fstab-entry.json +92 -0
  106. package/test-data/json-schema/geographical-location.json +20 -0
  107. package/test-data/json-schema/health-record.json +41 -0
  108. package/test-data/json-schema/job-posting.json +33 -0
  109. package/test-data/json-schema/movie.json +35 -0
  110. package/test-data/json-schema/regular-expression-pattern.json +12 -0
  111. package/test-data/json-schema/user-profile.json +33 -0
  112. package/test-data/sql/ecommerce.sql +641 -0
@@ -0,0 +1,413 @@
1
+ import { isNumeric, isPlainObject, R } from "../common";
2
+ import { remapObjectEntries, remapObjectValues } from "../common/object";
3
+ import { ValidationError } from "./error";
4
+ import { inferNumberTag, numberTagCanBeCastedTo } from "./number";
5
+ import { shapes } from "./shape";
6
+ import { NumberTag } from "./tags";
7
+
8
+ export function validate(
9
+ shape: shapes.Shape,
10
+ input: unknown,
11
+ ): Array<ValidationError> {
12
+ const errUnexpectedType = (
13
+ path: PropertyKey[],
14
+ input: unknown,
15
+ expected: string,
16
+ ): ValidationError => ({
17
+ path,
18
+ message: `Expected type of '${expected}' but got '${typeof input}'`,
19
+ });
20
+
21
+ const errUnexpectedLiteral = (
22
+ path: PropertyKey[],
23
+ input: unknown,
24
+ expected: string,
25
+ ): ValidationError => ({
26
+ path,
27
+ message: `Expected literal ${expected} but got ${input}`,
28
+ });
29
+
30
+ const errNumberFormatMismatch = (
31
+ path: PropertyKey[],
32
+ input: NumberTag,
33
+ expected: NumberTag,
34
+ ): ValidationError => ({
35
+ path,
36
+ message: `Expected number format ${expected} but got ${input}`,
37
+ });
38
+
39
+ const errBelowMin = (
40
+ path: PropertyKey[],
41
+ actual: number,
42
+ min: number,
43
+ kind: string,
44
+ ): ValidationError => ({
45
+ path,
46
+ message: `Expected ${kind} to be >= ${min} but got ${actual}`,
47
+ });
48
+
49
+ const errAboveMax = (
50
+ path: PropertyKey[],
51
+ actual: number,
52
+ max: number,
53
+ kind: string,
54
+ ): ValidationError => ({
55
+ path,
56
+ message: `Expected ${kind} to be <= ${max} but got ${actual}`,
57
+ });
58
+
59
+ const checkBounds = (
60
+ anno: { min?: number; max?: number },
61
+ actual: number,
62
+ path: PropertyKey[],
63
+ kind: string,
64
+ ): ValidationError[] => {
65
+ if (anno.min !== undefined && actual < anno.min)
66
+ return [errBelowMin(path, actual, anno.min, kind)];
67
+ if (anno.max !== undefined && actual > anno.max)
68
+ return [errAboveMax(path, actual, anno.max, kind)];
69
+ return [];
70
+ };
71
+
72
+ const check = (
73
+ shape: shapes.Shape,
74
+ input: unknown,
75
+ path: PropertyKey[],
76
+ ): ValidationError[] => {
77
+ switch (shape.type) {
78
+ case "unknown":
79
+ return [];
80
+ case "string": {
81
+ if (typeof input !== "string")
82
+ return [errUnexpectedType(path, input, shape.type)];
83
+ if (shape.literal && input !== shape.input)
84
+ return [errUnexpectedLiteral(path, input, `${shape.input}`)];
85
+ const boundsErrors = checkBounds(
86
+ shape.anno,
87
+ input.length,
88
+ path,
89
+ "string length",
90
+ );
91
+ if (boundsErrors.length > 0) return boundsErrors;
92
+ if (shape.anno.pattern !== undefined) {
93
+ const re =
94
+ shape.anno.pattern instanceof RegExp ?
95
+ shape.anno.pattern
96
+ : new RegExp(shape.anno.pattern);
97
+ if (!re.test(input))
98
+ return [
99
+ {
100
+ path,
101
+ message: `Expected string to match pattern ${re} but got '${input}'`,
102
+ },
103
+ ];
104
+ }
105
+ return [];
106
+ }
107
+ case "boolean": {
108
+ if (typeof input !== shape.type)
109
+ return [errUnexpectedType(path, input, shape.type)];
110
+ if (shape.literal && input !== shape.input)
111
+ return [errUnexpectedLiteral(path, input, `${shape.input}`)];
112
+ return [];
113
+ }
114
+ case "number": {
115
+ if (typeof input !== "number")
116
+ return [errUnexpectedType(path, input, shape.type)];
117
+ if (shape.literal && input !== shape.input)
118
+ return [errUnexpectedLiteral(path, input, `${shape.input}`)];
119
+ const inputTag = inferNumberTag(input);
120
+ if (!numberTagCanBeCastedTo(inputTag, shape.tag))
121
+ return [errNumberFormatMismatch(path, inputTag, shape.tag)];
122
+ const boundsErrors = checkBounds(shape.anno, input, path, "value");
123
+ if (boundsErrors.length > 0) return boundsErrors;
124
+ return [];
125
+ }
126
+ case "array": {
127
+ if (!Array.isArray(input))
128
+ return [errUnexpectedType(path, input, shape.type)];
129
+ const boundsErrors = checkBounds(
130
+ shape.anno,
131
+ input.length,
132
+ path,
133
+ "array length",
134
+ );
135
+ if (boundsErrors.length > 0) return boundsErrors;
136
+ const errors = input
137
+ .map((x, i) => check(shape.input, x, [...path, i]))
138
+ .flat();
139
+ if (errors.length > 0) return errors;
140
+ return [];
141
+ }
142
+ case "binary": {
143
+ if (input instanceof Uint8Array) return [];
144
+ if (Array.isArray(input)) {
145
+ const num = shapes.uint8();
146
+ const errors = input
147
+ .map((x, i) => check(num, x, [...path, i]))
148
+ .flat();
149
+ if (errors.length > 0) return errors;
150
+ return [];
151
+ }
152
+ return [errUnexpectedType(path, input, shape.type)];
153
+ }
154
+ case "date": {
155
+ if (!(input instanceof Date))
156
+ return [errUnexpectedType(path, input, shape.type)];
157
+ return [];
158
+ }
159
+ case "nil": {
160
+ if (input !== null)
161
+ return [errUnexpectedType(path, input, shape.type)];
162
+ return [];
163
+ }
164
+ case "undefined": {
165
+ if (typeof input !== "undefined")
166
+ return [errUnexpectedType(path, input, shape.type)];
167
+ return [];
168
+ }
169
+ case "vector": {
170
+ if (!Array.isArray(input))
171
+ return [errUnexpectedType(path, input, shape.type)];
172
+ if (input.length !== shape.input.dims)
173
+ return [
174
+ {
175
+ path,
176
+ message: `Expected a vector with dimensions ${shape.input.dims} but got an array of length ${input.length}`,
177
+ },
178
+ ];
179
+ const errors = input
180
+ .map((x, i) => check(shape.input.format, x, [...path, i]))
181
+ .flat();
182
+ if (errors.length > 0) return errors;
183
+ return [];
184
+ }
185
+ case "tuple": {
186
+ if (!Array.isArray(input))
187
+ return [errUnexpectedType(path, input, shape.type)];
188
+ if (input.length !== shape.input.length)
189
+ return [
190
+ {
191
+ path,
192
+ message: `Expected a tuple of length ${shape.input.length} but got an array of length ${input.length}`,
193
+ },
194
+ ];
195
+ const errors = shape.input
196
+ .map((item, i) => check(item, input[i], [...path, i]))
197
+ .flat();
198
+ if (errors.length > 0) return errors;
199
+ return [];
200
+ }
201
+ case "range": {
202
+ if (typeof input !== "object" || input === null)
203
+ return [errUnexpectedType(path, input, shape.type)];
204
+ const rec = input as Record<string, unknown>;
205
+ if (
206
+ !("lower" in rec) ||
207
+ !("upper" in rec) ||
208
+ !("lowerInclusive" in rec) ||
209
+ !("upperInclusive" in rec)
210
+ )
211
+ return [
212
+ {
213
+ path,
214
+ message:
215
+ "Expected range with keys 'lower', 'upper', 'lowerInclusive', 'upperInclusive'",
216
+ },
217
+ ];
218
+ if (typeof rec["lowerInclusive"] !== "boolean")
219
+ return [
220
+ {
221
+ path: [...path, "lowerInclusive"],
222
+ message: `Expected boolean but got '${typeof rec["lowerInclusive"]}'`,
223
+ },
224
+ ];
225
+ if (typeof rec["upperInclusive"] !== "boolean")
226
+ return [
227
+ {
228
+ path: [...path, "upperInclusive"],
229
+ message: `Expected boolean but got '${typeof rec["upperInclusive"]}'`,
230
+ },
231
+ ];
232
+ const errors: ValidationError[] = [];
233
+ if (rec["lower"] !== null) {
234
+ errors.push(
235
+ ...check(shape.input, rec["lower"], [...path, "lower"]),
236
+ );
237
+ }
238
+ if (rec["upper"] !== null) {
239
+ errors.push(
240
+ ...check(shape.input, rec["upper"], [...path, "upper"]),
241
+ );
242
+ }
243
+ return errors;
244
+ }
245
+ case "mapping": {
246
+ if (typeof input !== "object")
247
+ return [errUnexpectedType(path, input, shape.type)];
248
+ if (input === null)
249
+ return [errUnexpectedType(path, input, shape.type)];
250
+
251
+ for (const [k, valueShape] of Object.entries(shape.input)) {
252
+ if (!(k in input)) {
253
+ if (valueShape.anno.optional) continue;
254
+ return [
255
+ { path: [...path, k], message: `Missing required key: '${k}'` },
256
+ ];
257
+ }
258
+ const actualValue = (input as Record<PropertyKey, unknown>)[k];
259
+ const errors = check(valueShape, actualValue, [...path, k]);
260
+ if (errors.length > 0) return errors;
261
+ }
262
+ return [];
263
+ }
264
+ case "record": {
265
+ if (typeof input !== "object")
266
+ return [errUnexpectedType(path, input, shape.type)];
267
+ if (input === null)
268
+ return [errUnexpectedType(path, input, shape.type)];
269
+
270
+ const inputRec = input as Record<PropertyKey, unknown>;
271
+
272
+ const keys = Object.keys(inputRec);
273
+ const keyErrors = keys
274
+ .map((key) => check(shape.input[0], key, path))
275
+ .flat();
276
+ if (keyErrors.length > 0) return keyErrors;
277
+
278
+ const valueErrors = Object.entries(inputRec)
279
+ .map(([k, v]) => check(shape.input[1], v, [...path, k]))
280
+ .flat();
281
+ if (valueErrors.length > 0) return valueErrors;
282
+
283
+ return [];
284
+ }
285
+ case "union": {
286
+ const errors: ValidationError[] = [];
287
+ for (const t of shape.input) {
288
+ const errs = check(t, input, path);
289
+ if (errs.length <= 0) return [];
290
+ errors.push(...errs);
291
+ }
292
+ return errors;
293
+ }
294
+ case "module": {
295
+ if (typeof input !== "object" || input === null)
296
+ return [errUnexpectedType(path, input, shape.type)];
297
+ for (const [k, tableShape] of Object.entries(shape.input)) {
298
+ if (!(k in input)) {
299
+ return [
300
+ {
301
+ path: [...path, k],
302
+ message: `Missing required table: '${k}'`,
303
+ },
304
+ ];
305
+ }
306
+ const actualValue = (input as Record<PropertyKey, unknown>)[k];
307
+ const errors = check(tableShape, actualValue, [...path, k]);
308
+ if (errors.length > 0) return errors;
309
+ }
310
+ return [];
311
+ }
312
+ }
313
+ };
314
+
315
+ return check(shape, input, []);
316
+ }
317
+
318
+ export function is<S extends shapes.Shape>(
319
+ shape: S,
320
+ input: unknown,
321
+ ): input is shapes.InferShapeOut<S> {
322
+ return validate(shape, input).length <= 0;
323
+ }
324
+
325
+ const autoCastFromString = (shape: shapes.Shape, input: string): unknown => {
326
+ switch (shape.type) {
327
+ case "string":
328
+ return input;
329
+ case "number":
330
+ return isNumeric(input) ? Number(input) : input;
331
+ case "boolean": {
332
+ const low = input.toLowerCase().trim();
333
+ return (
334
+ {
335
+ true: true,
336
+ false: false,
337
+ yes: true,
338
+ no: false,
339
+ on: true,
340
+ off: false,
341
+ "1": true,
342
+ "0": false,
343
+ }[low] ?? input
344
+ );
345
+ }
346
+ case "record":
347
+ case "array":
348
+ case "mapping": {
349
+ const clean = input.trim();
350
+ try {
351
+ return JSON.parse(clean);
352
+ } catch {
353
+ return input;
354
+ }
355
+ }
356
+ case "date": {
357
+ const clean = input.trim();
358
+ try {
359
+ const date = new Date(clean);
360
+ const time = date.getTime();
361
+ if (isNaN(time) || !isFinite(time)) return input;
362
+ return date;
363
+ } catch {
364
+ return input;
365
+ }
366
+ }
367
+ default:
368
+ return input;
369
+ }
370
+ };
371
+
372
+ const autoCast = (shape: shapes.Shape, input: unknown): unknown => {
373
+ if (typeof input === "string") input = autoCastFromString(shape, input);
374
+
375
+ switch (shape.type) {
376
+ case "mapping": {
377
+ if (isPlainObject(input)) {
378
+ return remapObjectValues(input, ([k, v]) => {
379
+ if (typeof k !== "string") return v;
380
+ const s = shape.input[k];
381
+ if (!s) return v;
382
+ return autoCast(s, v);
383
+ });
384
+ }
385
+ return input;
386
+ }
387
+ case "record": {
388
+ if (isPlainObject(input)) {
389
+ return remapObjectEntries(input, ([k, v]) => {
390
+ const ks = shape.input[0];
391
+ const vs = shape.input[1];
392
+ const castedK = autoCast(ks, k);
393
+ const castedV = autoCast(vs, v);
394
+ if (typeof castedK !== "string") return { [k]: v };
395
+ return { [castedK]: castedV };
396
+ });
397
+ }
398
+ return input;
399
+ }
400
+ }
401
+
402
+ return input;
403
+ };
404
+
405
+ export function parse<S extends shapes.Shape>(
406
+ shape: S,
407
+ input: unknown,
408
+ ): R.Result<shapes.InferShapeOut<S>, ValidationError[]> {
409
+ input = autoCast(shape, input);
410
+ const errors = validate(shape, input);
411
+ if (errors.length > 0) return R.err(errors);
412
+ return R.ok(input as shapes.InferShapeOut<S>);
413
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/cjs",
5
+ "module": "commonjs",
6
+ "moduleResolution": "node10",
7
+ "ignoreDeprecations": "6.0"
8
+ },
9
+ "include": ["./src/**/*.ts"],
10
+ "exclude": ["./src/**/*.test.ts", "./src/**/*.sample.ts"]
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/esm",
5
+ "module": "esnext",
6
+ "target": "esnext"
7
+ },
8
+ "include": ["./src/**/*.ts"],
9
+ "exclude": ["./src/**/*.test.ts"]
10
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "./src",
4
+ "outDir": "./dist/esm",
5
+ "module": "esnext",
6
+ "target": "esnext",
7
+ "lib": ["esnext"],
8
+ "types": ["node"],
9
+ "sourceMap": true,
10
+ "declaration": true,
11
+ "declarationMap": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "exactOptionalPropertyTypes": true,
14
+ "strict": true,
15
+ "isolatedModules": true,
16
+ "esModuleInterop": true,
17
+ "noUncheckedSideEffectImports": true,
18
+ "moduleDetection": "force",
19
+ "skipLibCheck": true
20
+ },
21
+ "include": ["./src/**/*.ts"],
22
+ "exclude": ["dist", "node_modules"]
23
+ }
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/prettier@3.8.3/node_modules/prettier/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/prettier@3.8.3/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/prettier@3.8.3/node_modules/prettier/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/prettier@3.8.3/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../prettier/bin/prettier.cjs" "$@"
19
+ else
20
+ exec node "$basedir/../prettier/bin/prettier.cjs" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsc" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/tsx@4.22.0/node_modules/tsx/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/tsx@4.22.0/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/tsx@4.22.0/node_modules/tsx/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/tsx@4.22.0/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../tsx/dist/cli.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../tsx/dist/cli.mjs" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/vitest@4.1.6_@types+node@25.8.0_vite@8.0.13_@types+node@25.8.0_esbuild@0.28.0_tsx@4.22.0_/node_modules/vitest/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/vitest@4.1.6_@types+node@25.8.0_vite@8.0.13_@types+node@25.8.0_esbuild@0.28.0_tsx@4.22.0_/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/ianertson/workspace/shapecraft/node_modules/.pnpm/vitest@4.1.6_@types+node@25.8.0_vite@8.0.13_@types+node@25.8.0_esbuild@0.28.0_tsx@4.22.0_/node_modules/vitest/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/vitest@4.1.6_@types+node@25.8.0_vite@8.0.13_@types+node@25.8.0_esbuild@0.28.0_tsx@4.22.0_/node_modules:/home/ianertson/workspace/shapecraft/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../vitest/vitest.mjs" "$@"
21
+ fi
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@shapecraft/samples",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "./src/index.ts",
6
+ "scripts": {
7
+ "test": "vitest --passWithNoTests","check":
8
+ "tsc -p ./tsconfig.json --noEmit",
9
+ "format": "prettier --experimental-ternaries ./src --write",
10
+ "build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "packageManager": "pnpm@10.33.2",
16
+ "dependencies": {
17
+ "@shapecraft/core": "workspace:*"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^25.8.0",
21
+ "prettier": "^3.8.3",
22
+ "tsx": "^4.22.0",
23
+ "typescript": "^6.0.3",
24
+ "vitest": "^4.1.6"
25
+ },
26
+ "publishConfig": {
27
+ "main": "./dist/cjs/index.js",
28
+ "types": "./dist/cjs/index.d.ts",
29
+ "typings": "./dist/cjs/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "import": {
33
+ "types": "./dist/esm/index.d.ts",
34
+ "default": "./dist/esm/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/cjs/index.d.ts",
38
+ "default": "./dist/cjs/index.js"
39
+ },
40
+ "default": {
41
+ "types": "./dist/cjs/index.d.ts",
42
+ "default": "./dist/cjs/index.js"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,49 @@
1
+ import { B } from "@shapecraft/core";
2
+
3
+ export const Author = B.mapping({
4
+ id: B.uint64().primary().autoIncrement(),
5
+ username: B.string().min(3).max(32).unique(),
6
+ displayName: B.string().min(1).max(80),
7
+ bio: B.string().max(280).optional(),
8
+ createdAt: B.date(),
9
+ }).title("Author");
10
+
11
+ export const Tag = B.mapping({
12
+ id: B.uint32().primary().autoIncrement(),
13
+ slug: B.string()
14
+ .regex(/^[a-z0-9-]+$/)
15
+ .unique(),
16
+ label: B.string().min(1).max(40),
17
+ }).title("Tag");
18
+
19
+ export const Post = B.mapping({
20
+ id: B.uint64().primary().autoIncrement(),
21
+ author: B.uint64().references(Author, "id"),
22
+ title: B.string().min(1).max(200),
23
+ slug: B.string()
24
+ .regex(/^[a-z0-9-]+$/)
25
+ .unique(),
26
+ body: B.string(),
27
+ status: B.union(
28
+ B.literal("draft"),
29
+ B.literal("published"),
30
+ B.literal("archived"),
31
+ ),
32
+ publishedAt: B.date().optional(),
33
+ createdAt: B.date(),
34
+ updatedAt: B.date(),
35
+ }).title("Post");
36
+
37
+ export const PostTag = B.mapping({
38
+ post: B.uint64().references(Post, "id"),
39
+ tag: B.uint32().references(Tag, "id"),
40
+ }).title("PostTag");
41
+
42
+ export const Comment = B.mapping({
43
+ id: B.uint64().primary().autoIncrement(),
44
+ post: B.uint64().references(Post, "id"),
45
+ author: B.uint64().references(Author, "id"),
46
+ parent: B.uint64().references(Author, "id").optional(),
47
+ body: B.string().min(1).max(4000),
48
+ createdAt: B.date(),
49
+ }).title("Comment");