@dzeio/schema 0.11.0 → 0.12.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/dist/Schema.d.mts CHANGED
@@ -396,9 +396,12 @@ declare class SchemaDefault<Child extends SchemaItem, Output = SchemaInfer<Child
396
396
  declare class SchemaFile extends SchemaItem<File> {
397
397
  static readonly id = "file";
398
398
  constructor();
399
- extension(ext: string, error?: string): this;
399
+ extension(ext: string | Array<string>, error?: string): this;
400
+ filenameLimit(limit: number, error?: string): this;
400
401
  maxSize(size: number, error?: string): this;
402
+ filenameTruncate(): this;
401
403
  isOfType(input: unknown): input is File;
404
+ private normalizeFilename;
402
405
  }
403
406
 
404
407
  type TupleToOutputs<Tuple extends Array<SchemaItem>, Result extends Array<any> = []> = number extends Tuple['length'] ? Array<SchemaInfer<Tuple[number]>> : Tuple[Result['length']] extends undefined ? Result : TupleToOutputs<Tuple, [...Result, SchemaInfer<Tuple[Result['length']]>]>;
@@ -451,10 +454,6 @@ declare function parseForm<T extends SchemaItem>(model: T, form: HTMLFormElement
451
454
  */
452
455
  declare function getSchemaItemAtPath(schema: SchemaItem | undefined, path: Array<string | number>): SchemaItem | undefined;
453
456
 
454
- /**
455
- * Decorator for modifier functions on SchemaItems to save them in the "savedCalls" property for the serialization
456
- */
457
- declare function parsable(): (target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => TypedPropertyDescriptor<any>;
458
457
  type SchemaItemStatic = (new (...args: Array<any>) => SchemaItem) & {
459
458
  id: string;
460
459
  };
@@ -512,4 +511,4 @@ declare class Schema<T extends Record<string, SchemaItem> = Record<string, Schem
512
511
  }
513
512
  declare const s: typeof Schema;
514
513
 
515
- export { type Infer, type InputInfer, type Literal, type Model, type ModelInfer$1 as ModelInfer, SchemaAny, SchemaArray, SchemaBoolean, SchemaDate, SchemaDefault, SchemaEnum, SchemaFile, type SchemaInfer, type SchemaInputInfer, SchemaIntersection, SchemaItem, type SchemaItemJSON, type SchemaJSON, SchemaLiteral, SchemaNever, SchemaNull, SchemaNullable, SchemaNullish, SchemaNumber, SchemaObject, SchemaRecord, SchemaString, SchemaTuple, SchemaUndefined, SchemaUnion, SchemaVoid, type ValidationError, type ValidationOptions, type ValidationResult, type ValidationResultOld, Schema as default, getSchemaItemAtPath, isNull, parsable, parseForm, parseFormData, parseQuery, s };
514
+ export { type Infer, type InputInfer, type Literal, type Model, type ModelInfer$1 as ModelInfer, SchemaAny, SchemaArray, SchemaBoolean, SchemaDate, SchemaDefault, SchemaEnum, SchemaFile, type SchemaInfer, type SchemaInputInfer, SchemaIntersection, SchemaItem, type SchemaItemJSON, type SchemaJSON, SchemaLiteral, SchemaNever, SchemaNull, SchemaNullable, SchemaNullish, SchemaNumber, SchemaObject, SchemaRecord, SchemaString, SchemaTuple, SchemaUndefined, SchemaUnion, SchemaVoid, type ValidationError, type ValidationOptions, type ValidationResult, type ValidationResultOld, Schema as default, getSchemaItemAtPath, isNull, parseForm, parseFormData, parseQuery, s };
package/dist/Schema.d.ts CHANGED
@@ -396,9 +396,12 @@ declare class SchemaDefault<Child extends SchemaItem, Output = SchemaInfer<Child
396
396
  declare class SchemaFile extends SchemaItem<File> {
397
397
  static readonly id = "file";
398
398
  constructor();
399
- extension(ext: string, error?: string): this;
399
+ extension(ext: string | Array<string>, error?: string): this;
400
+ filenameLimit(limit: number, error?: string): this;
400
401
  maxSize(size: number, error?: string): this;
402
+ filenameTruncate(): this;
401
403
  isOfType(input: unknown): input is File;
404
+ private normalizeFilename;
402
405
  }
403
406
 
404
407
  type TupleToOutputs<Tuple extends Array<SchemaItem>, Result extends Array<any> = []> = number extends Tuple['length'] ? Array<SchemaInfer<Tuple[number]>> : Tuple[Result['length']] extends undefined ? Result : TupleToOutputs<Tuple, [...Result, SchemaInfer<Tuple[Result['length']]>]>;
@@ -451,10 +454,6 @@ declare function parseForm<T extends SchemaItem>(model: T, form: HTMLFormElement
451
454
  */
452
455
  declare function getSchemaItemAtPath(schema: SchemaItem | undefined, path: Array<string | number>): SchemaItem | undefined;
453
456
 
454
- /**
455
- * Decorator for modifier functions on SchemaItems to save them in the "savedCalls" property for the serialization
456
- */
457
- declare function parsable(): (target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => TypedPropertyDescriptor<any>;
458
457
  type SchemaItemStatic = (new (...args: Array<any>) => SchemaItem) & {
459
458
  id: string;
460
459
  };
@@ -512,4 +511,4 @@ declare class Schema<T extends Record<string, SchemaItem> = Record<string, Schem
512
511
  }
513
512
  declare const s: typeof Schema;
514
513
 
515
- export { type Infer, type InputInfer, type Literal, type Model, type ModelInfer$1 as ModelInfer, SchemaAny, SchemaArray, SchemaBoolean, SchemaDate, SchemaDefault, SchemaEnum, SchemaFile, type SchemaInfer, type SchemaInputInfer, SchemaIntersection, SchemaItem, type SchemaItemJSON, type SchemaJSON, SchemaLiteral, SchemaNever, SchemaNull, SchemaNullable, SchemaNullish, SchemaNumber, SchemaObject, SchemaRecord, SchemaString, SchemaTuple, SchemaUndefined, SchemaUnion, SchemaVoid, type ValidationError, type ValidationOptions, type ValidationResult, type ValidationResultOld, Schema as default, getSchemaItemAtPath, isNull, parsable, parseForm, parseFormData, parseQuery, s };
514
+ export { type Infer, type InputInfer, type Literal, type Model, type ModelInfer$1 as ModelInfer, SchemaAny, SchemaArray, SchemaBoolean, SchemaDate, SchemaDefault, SchemaEnum, SchemaFile, type SchemaInfer, type SchemaInputInfer, SchemaIntersection, SchemaItem, type SchemaItemJSON, type SchemaJSON, SchemaLiteral, SchemaNever, SchemaNull, SchemaNullable, SchemaNullish, SchemaNumber, SchemaObject, SchemaRecord, SchemaString, SchemaTuple, SchemaUndefined, SchemaUnion, SchemaVoid, type ValidationError, type ValidationOptions, type ValidationResult, type ValidationResultOld, Schema as default, getSchemaItemAtPath, isNull, parseForm, parseFormData, parseQuery, s };
package/dist/Schema.js CHANGED
@@ -53,7 +53,6 @@ __export(Schema_exports, {
53
53
  default: () => Schema,
54
54
  getSchemaItemAtPath: () => getSchemaItemAtPath,
55
55
  isNull: () => isNull,
56
- parsable: () => parsable,
57
56
  parseForm: () => parseForm,
58
57
  parseFormData: () => parseFormData,
59
58
  parseQuery: () => parseQuery,
@@ -157,6 +156,24 @@ function getSchemaItemAtPath(schema, path) {
157
156
 
158
157
  // src/SchemaItem.ts
159
158
  var import_object_util2 = require("@dzeio/object-util");
159
+
160
+ // src/parsable.ts
161
+ function parsable() {
162
+ return (target, propertyKey, descriptor) => {
163
+ if (!(propertyKey in target)) {
164
+ throw new Error("property not set in object");
165
+ }
166
+ const original = target[propertyKey];
167
+ descriptor.value = function(...args) {
168
+ this.savedCalls.push({ name: propertyKey, args });
169
+ const res = original.call(this, ...args);
170
+ return res;
171
+ };
172
+ return descriptor;
173
+ };
174
+ }
175
+
176
+ // src/SchemaItem.ts
160
177
  var _SchemaItem = class _SchemaItem {
161
178
  constructor(items) {
162
179
  this.id = this.constructor.id;
@@ -410,7 +427,7 @@ var SchemaDate = class extends SchemaItem {
410
427
  if (splitted.length !== 3) {
411
428
  break;
412
429
  }
413
- date = new Date(splitted[2], splitted[1] - 1, splitted[0]);
430
+ date = /* @__PURE__ */ new Date(`${splitted[0]}-${splitted[1]}-${splitted[2]}`);
414
431
  break;
415
432
  }
416
433
  case "jj/mm/yy": {
@@ -418,7 +435,7 @@ var SchemaDate = class extends SchemaItem {
418
435
  if (splitted.length !== 3) {
419
436
  break;
420
437
  }
421
- date = new Date(splitted[2], splitted[1] - 1, splitted[0]);
438
+ date = /* @__PURE__ */ new Date(`${splitted[2]}-${splitted[1]}-${splitted[0]}`);
422
439
  break;
423
440
  }
424
441
  }
@@ -739,7 +756,8 @@ var SchemaString = class extends SchemaItem {
739
756
  if (typeof input !== "string") {
740
757
  return input;
741
758
  }
742
- return input.trim();
759
+ const invisible = /^\u200B+|\u200B+$/gu;
760
+ return input.trim().replace(invisible, "");
743
761
  });
744
762
  return this;
745
763
  }
@@ -889,24 +907,85 @@ var SchemaFile = class extends SchemaItem {
889
907
  this.addPreProcess((input) => this.isOfType(input) && input.size > 0 ? input : void 0);
890
908
  }
891
909
  extension(ext, error) {
892
- this.addValidation({ fn: (input) => input.name.endsWith(ext), error });
910
+ this.addValidation({ fn: (input) => {
911
+ if (!Array.isArray(ext)) {
912
+ return input.name.endsWith(ext);
913
+ }
914
+ for (const possibility of ext) {
915
+ if (input.name.endsWith(possibility)) {
916
+ return true;
917
+ }
918
+ }
919
+ return false;
920
+ }, error });
921
+ return this;
922
+ }
923
+ filenameLimit(limit, error) {
924
+ if (limit > 255) {
925
+ throw new Error("Filename limit cannot exceed 255 characters");
926
+ }
927
+ if (limit <= 0) {
928
+ throw new Error("Filename limit must be greater than 0");
929
+ }
930
+ this.addValidation({ fn: (file) => file.name.length <= limit, error });
893
931
  return this;
894
932
  }
895
933
  maxSize(size, error) {
896
934
  this.addValidation({ fn: (file) => file.size <= size, error });
897
935
  return this;
898
936
  }
937
+ filenameTruncate() {
938
+ this.addPreProcess((input) => this.isOfType(input) ? this.normalizeFilename(input) : input);
939
+ return this;
940
+ }
899
941
  isOfType(input) {
900
942
  return input instanceof File;
901
943
  }
944
+ normalizeFilename(file) {
945
+ const MAX_FILENAME_BYTES = 255;
946
+ const encoder = new TextEncoder();
947
+ if (encoder.encode(file.name).length <= MAX_FILENAME_BYTES) {
948
+ return file;
949
+ }
950
+ const lastDot = file.name.lastIndexOf(".");
951
+ const hasExtension = lastDot > 0 && lastDot < file.name.length - 1;
952
+ const extension = hasExtension ? file.name.slice(lastDot) : "";
953
+ const basename = hasExtension ? file.name.slice(0, lastDot) : file.name;
954
+ const extensionBytes = encoder.encode(extension).length;
955
+ const maxBaseBytes = MAX_FILENAME_BYTES - extensionBytes;
956
+ let result = "";
957
+ let currentBytes = 0;
958
+ for (const char of basename) {
959
+ const charBytes = encoder.encode(char).length;
960
+ if (currentBytes + charBytes > maxBaseBytes) {
961
+ break;
962
+ }
963
+ result += char;
964
+ currentBytes += charBytes;
965
+ }
966
+ return new File(
967
+ [file],
968
+ result + extension,
969
+ {
970
+ type: file.type,
971
+ lastModified: file.lastModified
972
+ }
973
+ );
974
+ }
902
975
  };
903
976
  SchemaFile.id = "file";
904
977
  __decorateClass([
905
978
  parsable()
906
979
  ], SchemaFile.prototype, "extension", 1);
980
+ __decorateClass([
981
+ parsable()
982
+ ], SchemaFile.prototype, "filenameLimit", 1);
907
983
  __decorateClass([
908
984
  parsable()
909
985
  ], SchemaFile.prototype, "maxSize", 1);
986
+ __decorateClass([
987
+ parsable()
988
+ ], SchemaFile.prototype, "filenameTruncate", 1);
910
989
 
911
990
  // src/items/tuple.ts
912
991
  var SchemaTuple = class extends SchemaItem {
@@ -968,20 +1047,6 @@ SchemaNever.id = "never";
968
1047
 
969
1048
  // src/Schema.ts
970
1049
  var import_object_util6 = require("@dzeio/object-util");
971
- function parsable() {
972
- return (target, propertyKey, descriptor) => {
973
- if (!(propertyKey in target)) {
974
- throw new Error("property not set in object");
975
- }
976
- const original = target[propertyKey];
977
- descriptor.value = function(...args) {
978
- this.savedCalls.push({ name: propertyKey, args });
979
- const res = original.call(this, ...args);
980
- return res;
981
- };
982
- return descriptor;
983
- };
984
- }
985
1050
  var _Schema = class _Schema extends SchemaObject {
986
1051
  static register(module2) {
987
1052
  this.registeredModules.push(module2);
@@ -1171,7 +1236,6 @@ var s = Schema;
1171
1236
  SchemaVoid,
1172
1237
  getSchemaItemAtPath,
1173
1238
  isNull,
1174
- parsable,
1175
1239
  parseForm,
1176
1240
  parseFormData,
1177
1241
  parseQuery,
package/dist/Schema.mjs CHANGED
@@ -105,6 +105,24 @@ function getSchemaItemAtPath(schema, path) {
105
105
 
106
106
  // src/SchemaItem.ts
107
107
  import { isObject, objectClean, objectRemap, objectSet as objectSet2 } from "@dzeio/object-util";
108
+
109
+ // src/parsable.ts
110
+ function parsable() {
111
+ return (target, propertyKey, descriptor) => {
112
+ if (!(propertyKey in target)) {
113
+ throw new Error("property not set in object");
114
+ }
115
+ const original = target[propertyKey];
116
+ descriptor.value = function(...args) {
117
+ this.savedCalls.push({ name: propertyKey, args });
118
+ const res = original.call(this, ...args);
119
+ return res;
120
+ };
121
+ return descriptor;
122
+ };
123
+ }
124
+
125
+ // src/SchemaItem.ts
108
126
  var _SchemaItem = class _SchemaItem {
109
127
  constructor(items) {
110
128
  this.id = this.constructor.id;
@@ -358,7 +376,7 @@ var SchemaDate = class extends SchemaItem {
358
376
  if (splitted.length !== 3) {
359
377
  break;
360
378
  }
361
- date = new Date(splitted[2], splitted[1] - 1, splitted[0]);
379
+ date = /* @__PURE__ */ new Date(`${splitted[0]}-${splitted[1]}-${splitted[2]}`);
362
380
  break;
363
381
  }
364
382
  case "jj/mm/yy": {
@@ -366,7 +384,7 @@ var SchemaDate = class extends SchemaItem {
366
384
  if (splitted.length !== 3) {
367
385
  break;
368
386
  }
369
- date = new Date(splitted[2], splitted[1] - 1, splitted[0]);
387
+ date = /* @__PURE__ */ new Date(`${splitted[2]}-${splitted[1]}-${splitted[0]}`);
370
388
  break;
371
389
  }
372
390
  }
@@ -687,7 +705,8 @@ var SchemaString = class extends SchemaItem {
687
705
  if (typeof input !== "string") {
688
706
  return input;
689
707
  }
690
- return input.trim();
708
+ const invisible = /^\u200B+|\u200B+$/gu;
709
+ return input.trim().replace(invisible, "");
691
710
  });
692
711
  return this;
693
712
  }
@@ -837,24 +856,85 @@ var SchemaFile = class extends SchemaItem {
837
856
  this.addPreProcess((input) => this.isOfType(input) && input.size > 0 ? input : void 0);
838
857
  }
839
858
  extension(ext, error) {
840
- this.addValidation({ fn: (input) => input.name.endsWith(ext), error });
859
+ this.addValidation({ fn: (input) => {
860
+ if (!Array.isArray(ext)) {
861
+ return input.name.endsWith(ext);
862
+ }
863
+ for (const possibility of ext) {
864
+ if (input.name.endsWith(possibility)) {
865
+ return true;
866
+ }
867
+ }
868
+ return false;
869
+ }, error });
870
+ return this;
871
+ }
872
+ filenameLimit(limit, error) {
873
+ if (limit > 255) {
874
+ throw new Error("Filename limit cannot exceed 255 characters");
875
+ }
876
+ if (limit <= 0) {
877
+ throw new Error("Filename limit must be greater than 0");
878
+ }
879
+ this.addValidation({ fn: (file) => file.name.length <= limit, error });
841
880
  return this;
842
881
  }
843
882
  maxSize(size, error) {
844
883
  this.addValidation({ fn: (file) => file.size <= size, error });
845
884
  return this;
846
885
  }
886
+ filenameTruncate() {
887
+ this.addPreProcess((input) => this.isOfType(input) ? this.normalizeFilename(input) : input);
888
+ return this;
889
+ }
847
890
  isOfType(input) {
848
891
  return input instanceof File;
849
892
  }
893
+ normalizeFilename(file) {
894
+ const MAX_FILENAME_BYTES = 255;
895
+ const encoder = new TextEncoder();
896
+ if (encoder.encode(file.name).length <= MAX_FILENAME_BYTES) {
897
+ return file;
898
+ }
899
+ const lastDot = file.name.lastIndexOf(".");
900
+ const hasExtension = lastDot > 0 && lastDot < file.name.length - 1;
901
+ const extension = hasExtension ? file.name.slice(lastDot) : "";
902
+ const basename = hasExtension ? file.name.slice(0, lastDot) : file.name;
903
+ const extensionBytes = encoder.encode(extension).length;
904
+ const maxBaseBytes = MAX_FILENAME_BYTES - extensionBytes;
905
+ let result = "";
906
+ let currentBytes = 0;
907
+ for (const char of basename) {
908
+ const charBytes = encoder.encode(char).length;
909
+ if (currentBytes + charBytes > maxBaseBytes) {
910
+ break;
911
+ }
912
+ result += char;
913
+ currentBytes += charBytes;
914
+ }
915
+ return new File(
916
+ [file],
917
+ result + extension,
918
+ {
919
+ type: file.type,
920
+ lastModified: file.lastModified
921
+ }
922
+ );
923
+ }
850
924
  };
851
925
  SchemaFile.id = "file";
852
926
  __decorateClass([
853
927
  parsable()
854
928
  ], SchemaFile.prototype, "extension", 1);
929
+ __decorateClass([
930
+ parsable()
931
+ ], SchemaFile.prototype, "filenameLimit", 1);
855
932
  __decorateClass([
856
933
  parsable()
857
934
  ], SchemaFile.prototype, "maxSize", 1);
935
+ __decorateClass([
936
+ parsable()
937
+ ], SchemaFile.prototype, "filenameTruncate", 1);
858
938
 
859
939
  // src/items/tuple.ts
860
940
  var SchemaTuple = class extends SchemaItem {
@@ -916,20 +996,6 @@ SchemaNever.id = "never";
916
996
 
917
997
  // src/Schema.ts
918
998
  import { isObject as isObject5, objectRemap as objectRemap2 } from "@dzeio/object-util";
919
- function parsable() {
920
- return (target, propertyKey, descriptor) => {
921
- if (!(propertyKey in target)) {
922
- throw new Error("property not set in object");
923
- }
924
- const original = target[propertyKey];
925
- descriptor.value = function(...args) {
926
- this.savedCalls.push({ name: propertyKey, args });
927
- const res = original.call(this, ...args);
928
- return res;
929
- };
930
- return descriptor;
931
- };
932
- }
933
999
  var _Schema = class _Schema extends SchemaObject {
934
1000
  static register(module) {
935
1001
  this.registeredModules.push(module);
@@ -1119,7 +1185,6 @@ export {
1119
1185
  Schema as default,
1120
1186
  getSchemaItemAtPath,
1121
1187
  isNull,
1122
- parsable,
1123
1188
  parseForm,
1124
1189
  parseFormData,
1125
1190
  parseQuery,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzeio/schema",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "dependencies": {
5
5
  "@dzeio/object-util": "^1.9.2"
6
6
  },
@@ -26,16 +26,21 @@
26
26
  "@eslint/js": "^9.18.0",
27
27
  "@standard-schema/spec": "^1.0.0",
28
28
  "@stylistic/eslint-plugin": "^2.13.0",
29
+ "@swc/core": "^1.15.43",
29
30
  "@types/bun": "^1.1.18",
30
31
  "@typescript-eslint/eslint-plugin": "^8.21.0",
32
+ "@vitest/coverage-v8": "^3.0.4",
31
33
  "globals": "^15.14.0",
32
34
  "tsup": "^8.3.6",
33
35
  "typescript": "^5.7.3",
34
36
  "typescript-eslint": "^8.21.0",
37
+ "unplugin-swc": "^1.5.9",
38
+ "vite-tsconfig-paths": "^6.1.1",
35
39
  "vitest": "^3.0.4"
36
40
  },
37
41
  "scripts": {
38
42
  "test": "bun test --coverage",
43
+ "test:node": "vitest run --coverage",
39
44
  "lint": "eslint",
40
45
  "build": "rm -rf dist && tsup ./src/Schema.ts --format cjs,esm --dts --clean",
41
46
  "prepublishOnly": "rm -rf dist && tsup ./src/Schema.ts --format cjs,esm --dts --clean"