typia 13.3.0 → 14.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 (51) hide show
  1. package/lib/internal/_randomPattern.js +20 -3
  2. package/lib/internal/_randomPattern.js.map +1 -1
  3. package/lib/internal/_randomPattern.mjs +5 -1
  4. package/lib/internal/_randomPattern.mjs.map +1 -1
  5. package/lib/internal/_randomStringLength.js +9 -2
  6. package/lib/internal/_randomStringLength.js.map +1 -1
  7. package/lib/internal/_randomStringLength.mjs +3 -1
  8. package/lib/internal/_randomStringLength.mjs.map +1 -1
  9. package/lib/transform.mjs +1 -1
  10. package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +206 -0
  11. package/native/cmd/ttsc-typia/notation_kebab_case_transform_test.go +2 -2
  12. package/native/cmd/ttsc-typia/project_catalog_transform_coverage_test.go +84 -85
  13. package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -1
  14. package/native/cmd/ttsc-typia/transform.go +2 -2
  15. package/native/cmd/ttsc-typia/transform_helpers_test.go +12 -0
  16. package/native/core/factories/MetadataCommentTagFactory.go +96 -27
  17. package/native/core/factories/metadata_comment_tag_factory_coverage_test.go +2 -2
  18. package/native/core/factories/metadata_comment_tag_factory_rejects_out_of_range_integer_test.go +124 -0
  19. package/native/core/programmers/AssertProgrammer.go +20 -6
  20. package/native/core/programmers/RandomProgrammer.go +78 -6
  21. package/native/core/programmers/ValidateProgrammer.go +21 -6
  22. package/native/core/programmers/helpers/RandomJoiner.go +8 -1
  23. package/native/core/programmers/iterate/check_dynamic_properties.go +177 -3
  24. package/native/core/programmers/iterate/check_object.go +12 -1
  25. package/package.json +3 -12
  26. package/src/internal/_randomPattern.ts +18 -3
  27. package/src/internal/_randomStringLength.ts +9 -2
  28. package/lib/executable/FileSystemIdentity.d.ts +0 -44
  29. package/lib/executable/FileSystemIdentity.js +0 -213
  30. package/lib/executable/FileSystemIdentity.js.map +0 -1
  31. package/lib/executable/FileSystemIdentity.mjs +0 -133
  32. package/lib/executable/FileSystemIdentity.mjs.map +0 -1
  33. package/lib/executable/TypiaGenerateWizard.d.ts +0 -9
  34. package/lib/executable/TypiaGenerateWizard.js +0 -852
  35. package/lib/executable/TypiaGenerateWizard.js.map +0 -1
  36. package/lib/executable/TypiaGenerateWizard.mjs +0 -618
  37. package/lib/executable/TypiaGenerateWizard.mjs.map +0 -1
  38. package/lib/executable/generate/ttsc.d.ts +0 -1
  39. package/lib/executable/generate/ttsc.js +0 -53
  40. package/lib/executable/generate/ttsc.js.map +0 -1
  41. package/lib/executable/generate/ttsc.mjs +0 -37
  42. package/lib/executable/generate/ttsc.mjs.map +0 -1
  43. package/lib/executable/typia.d.ts +0 -2
  44. package/lib/executable/typia.js +0 -84
  45. package/lib/executable/typia.js.map +0 -1
  46. package/lib/executable/typia.mjs +0 -60
  47. package/lib/executable/typia.mjs.map +0 -1
  48. package/src/executable/FileSystemIdentity.ts +0 -222
  49. package/src/executable/TypiaGenerateWizard.ts +0 -1133
  50. package/src/executable/generate/ttsc.ts +0 -70
  51. package/src/executable/typia.ts +0 -80
@@ -1,11 +1,14 @@
1
1
  package iterate
2
2
 
3
3
  import (
4
+ "strings"
5
+
4
6
  shimast "github.com/microsoft/typescript-go/shim/ast"
5
7
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
6
8
  nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
7
9
  nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
8
10
  nativehelpers "github.com/samchon/typia/packages/typia/native/core/programmers/helpers"
11
+ nativemetadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
9
12
  )
10
13
 
11
14
  type Check_dynamic_propertiesProps struct {
@@ -126,7 +129,11 @@ func check_dynamic_property(props Check_dynamic_propertiesProps) *shimast.Node {
126
129
  nil,
127
130
  ))
128
131
  }
129
- broken := false
132
+ // terminated marks that `statements` already ends in an unconditional return,
133
+ // so nothing after it could run. A signature that accepts every key sets it
134
+ // from the loop; a declared key type that covers every key sets it from the
135
+ // tail below.
136
+ terminated := false
130
137
 
131
138
  if len(props.Regular) != 0 {
132
139
  add(is_regular_property(props.Regular, props.Context.Emit), props.Config.Positive)
@@ -156,13 +163,58 @@ func check_dynamic_property(props Check_dynamic_propertiesProps) *shimast.Node {
156
163
  })
157
164
  if condition.Kind == shimast.KindTrueKeyword {
158
165
  statements = append(statements, f.NewReturnStatement(entry.Expression))
159
- broken = true
166
+ terminated = true
160
167
  break
161
168
  }
162
169
  add(condition, entry.Expression)
163
170
  }
164
171
 
165
- if !broken {
172
+ if !terminated {
173
+ // Reaching here means the key satisfied no dynamic signature. Two different
174
+ // things bring a key here, and they are not the same failure.
175
+ //
176
+ // A key whose *declared type* no signature covers -- `wrong` against
177
+ // `[key: `prefix_${string}`]` -- is an extra property. Nothing declares it,
178
+ // so `is` accepts it as it accepts any surplus property, and `equals`
179
+ // reports it through `Superfluous`.
180
+ //
181
+ // A key the declaration does cover but whose *type tag* it breaks -- `ab`
182
+ // against `[key: string & tags.MinLength<3>]` -- is a declared property
183
+ // with an invalid key. It used to reach the same accepting tail, which made
184
+ // every key tag inert: `[key: string & tags.Format<"uuid">]` accepted any
185
+ // key at all, while the emitted check sat right above, used only to decide
186
+ // *which* signature's value type to apply (#2347).
187
+ //
188
+ // So the tag failure is separated out first, by re-asking the same key
189
+ // question with every type tag removed. What is left is exactly the
190
+ // declared key type, and a key that answers it is a member of the object
191
+ // whose key broke a constraint -- never a surplus property.
192
+ shape := check_dynamic_properties_key_shape(props, key)
193
+ if shape != nil {
194
+ var invalid *shimast.Node
195
+ if props.Config.InvalidKey != nil {
196
+ invalid = props.Config.InvalidKey(
197
+ value,
198
+ check_dynamic_properties_key_expected(props.Dynamic),
199
+ check_dynamic_properties_invalid_key_description(props.Context.Emit),
200
+ )
201
+ } else {
202
+ // A programmer with no better report reduces both to the same answer,
203
+ // as `is` does with `false`.
204
+ invalid = props.Config.Superfluous(value, check_dynamic_properties_superfluous_description(props.Context.Emit))
205
+ }
206
+ if shape.Kind == shimast.KindTrueKeyword {
207
+ // Every key the signature accepts is covered -- a string index
208
+ // signature covers every key `Object.keys` can yield -- so the surplus
209
+ // tail below is unreachable and is not emitted.
210
+ statements = append(statements, f.NewReturnStatement(invalid))
211
+ terminated = true
212
+ } else {
213
+ add(shape, invalid)
214
+ }
215
+ }
216
+ }
217
+ if !terminated {
166
218
  output := props.Config.Positive
167
219
  if props.Config.Equals {
168
220
  output = props.Config.Superfluous(value, check_dynamic_properties_superfluous_description(props.Context.Emit))
@@ -257,6 +309,128 @@ func check_dynamic_properties_internal(context nativecontext.ITypiaContext, name
257
309
  return f.NewIdentifier(name)
258
310
  }
259
311
 
312
+ // The condition that the key belongs to some index signature's declared key
313
+ // type while breaking that signature's type tags -- the question the tail has
314
+ // to answer to tell an invalid key from a surplus property.
315
+ //
316
+ // It is the same `Check_dynamic_key` question asked of every signature with its
317
+ // type tags removed, joined with `||`. The tail runs only after each full
318
+ // condition returned false, so a signature whose declared type still answers
319
+ // yes is one whose tags are what the key broke.
320
+ //
321
+ // Nil when no signature carries a key tag. Then each stripped condition equals
322
+ // the full one the tail already refuted, so the check could never fire, and an
323
+ // object with untagged index signatures emits exactly what it emitted before.
324
+ func check_dynamic_properties_key_shape(props Check_dynamic_propertiesProps, key *shimast.Expression) *shimast.Node {
325
+ tagged := false
326
+ for _, entry := range props.Dynamic {
327
+ if check_dynamic_properties_key_tagged(entry.Key) {
328
+ tagged = true
329
+ break
330
+ }
331
+ }
332
+ if !tagged {
333
+ return nil
334
+ }
335
+ conditions := make([]*shimast.Node, 0, len(props.Dynamic))
336
+ for _, entry := range props.Dynamic {
337
+ condition := Check_dynamic_key(Check_dynamic_keyProps{
338
+ Context: props.Context,
339
+ Metadata: check_dynamic_properties_key_untagged(entry.Key),
340
+ Input: key,
341
+ })
342
+ if condition.Kind == shimast.KindTrueKeyword {
343
+ return condition
344
+ }
345
+ conditions = append(conditions, condition)
346
+ }
347
+ return check_dynamic_key_reduce(conditions, shimast.KindBarBarToken, props.Context.Emit)
348
+ }
349
+
350
+ // Whether the key declaration constrains more than its own type, which is what
351
+ // makes the stripped question differ from the full one.
352
+ //
353
+ // One tag carrying a runtime check is enough, and the row it sits in need not
354
+ // carry them all. `Check_string` and `Check_number` filter per tag, so
355
+ // `[key: number & Minimum<0> & <a tag with no runtime check>]` still emits the
356
+ // minimum -- reading the row as all-or-nothing here would have declared that
357
+ // key untagged and left its minimum inert, which is the defect #2347 closed.
358
+ //
359
+ // Answering yes where the stripped question turns out identical costs an
360
+ // unreachable branch, never a wrong answer: the tail is reached only after
361
+ // every full condition refused the key, so a stripped condition equal to one of
362
+ // them refuses it too.
363
+ func check_dynamic_properties_key_tagged(metadata *nativemetadata.MetadataSchema) bool {
364
+ for _, atomic := range metadata.Atomics {
365
+ for _, row := range atomic.Tags {
366
+ for _, tag := range row {
367
+ if tag.Validate != "" {
368
+ return true
369
+ }
370
+ }
371
+ }
372
+ }
373
+ return false
374
+ }
375
+
376
+ // The key declaration with its type tags removed, leaving the declared type
377
+ // alone. Templates, constants, and natives carry no tags, so only the atomics
378
+ // are rebuilt; the clone keeps the original reportable name untouched.
379
+ func check_dynamic_properties_key_untagged(metadata *nativemetadata.MetadataSchema) *nativemetadata.MetadataSchema {
380
+ clone := *metadata
381
+ clone.Atomics = make([]*nativemetadata.MetadataAtomic, 0, len(metadata.Atomics))
382
+ for _, atomic := range metadata.Atomics {
383
+ clone.Atomics = append(clone.Atomics, nativemetadata.MetadataAtomic_create(nativemetadata.MetadataAtomic{
384
+ Type: atomic.Type,
385
+ }))
386
+ }
387
+ return &clone
388
+ }
389
+
390
+ // The type every declared key would have satisfied, for the report. Several
391
+ // signatures join with `|`, which is how the declaration reads.
392
+ func check_dynamic_properties_key_expected(dynamic []nativehelpers.IExpressionEntry) string {
393
+ names := make([]string, 0, len(dynamic))
394
+ for _, entry := range dynamic {
395
+ names = append(names, entry.Key.GetName())
396
+ }
397
+ return strings.Join(names, " | ")
398
+ }
399
+
400
+ // The advice `Superfluous` gives -- remove the property -- is wrong here. The
401
+ // property is declared; its key is what broke.
402
+ func check_dynamic_properties_invalid_key_description(emit ...*shimprinter.EmitContext) *shimast.Node {
403
+ f := nativecontext.EmitFactoryOf(check_dynamic_properties_factory, emit...)
404
+ return f.NewCallExpression(
405
+ nativefactories.IdentifierFactory.Access(
406
+ nil,
407
+ f.NewArrayLiteralExpression(
408
+ f.NewNodeList([]*shimast.Node{
409
+ f.NewTemplateExpression(
410
+ f.NewTemplateHead("The key `", "The key \\`", shimast.TokenFlagsNone),
411
+ f.NewNodeList([]*shimast.Node{
412
+ f.NewTemplateSpan(
413
+ f.NewIdentifier("key"),
414
+ f.NewTemplateTail("` does not satisfy the type its index signature declares.", "\\` does not satisfy the type its index signature declares.", shimast.TokenFlagsNone),
415
+ ),
416
+ }),
417
+ ),
418
+ f.NewStringLiteral("", shimast.TokenFlagsNone),
419
+ f.NewStringLiteral("Please correct the key next time.", shimast.TokenFlagsNone),
420
+ }),
421
+ true,
422
+ ),
423
+ "join",
424
+ ),
425
+ nil,
426
+ nil,
427
+ f.NewNodeList([]*shimast.Node{
428
+ f.NewStringLiteral("\n", shimast.TokenFlagsNone),
429
+ }),
430
+ shimast.NodeFlagsNone,
431
+ )
432
+ }
433
+
260
434
  func check_dynamic_properties_superfluous_description(emit ...*shimprinter.EmitContext) *shimast.Node {
261
435
  f := nativecontext.EmitFactoryOf(check_dynamic_properties_factory, emit...)
262
436
  return f.NewCallExpression(
@@ -22,7 +22,18 @@ type Check_object_IConfig struct {
22
22
  Reduce func(a *shimast.Expression, b *shimast.Expression) *shimast.Node
23
23
  Positive *shimast.Expression
24
24
  Superfluous func(value *shimast.Expression, description *shimast.Expression) *shimast.Node
25
- Entries *shimast.Expression
25
+ // InvalidKey reports a dynamic key an index signature declares but whose type
26
+ // tag it breaks. A key no signature declares at all is a surplus property and
27
+ // goes to `Superfluous` instead.
28
+ //
29
+ // `Superfluous` is the wrong report for this one: it says the property is not
30
+ // defined in the object type and advises removing it, which is false and
31
+ // unhelpful when the property *is* declared and only its key broke a
32
+ // constraint. A programmer that has no better answer may leave this nil, and
33
+ // `Superfluous` is used instead -- the outcome is identical, only the message
34
+ // differs.
35
+ InvalidKey func(value *shimast.Expression, expected string, description *shimast.Expression) *shimast.Node
36
+ Entries *shimast.Expression
26
37
  }
27
38
 
28
39
  func Check_object(props Check_objectProps) *shimast.Node {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typia",
3
- "version": "13.3.0",
3
+ "version": "14.0.0",
4
4
  "description": "Superfast runtime validators with only one line",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -26,9 +26,6 @@
26
26
  },
27
27
  "./package.json": "./package.json"
28
28
  },
29
- "bin": {
30
- "typia": "./lib/executable/typia.js"
31
- },
32
29
  "ttsc": {
33
30
  "plugin": {
34
31
  "transform": "typia/lib/transform"
@@ -46,12 +43,9 @@
46
43
  "homepage": "https://typia.io",
47
44
  "dependencies": {
48
45
  "@standard-schema/spec": "^1.0.0",
49
- "commander": "^10.0.0",
50
- "inquirer": "^8.2.5",
51
46
  "randexp": "^0.5.3",
52
- "tinyglobby": "^0.2.12",
53
- "@typia/interface": "^13.3.0",
54
- "@typia/utils": "^13.3.0"
47
+ "@typia/interface": "^14.0.0",
48
+ "@typia/utils": "^14.0.0"
55
49
  },
56
50
  "peerDependencies": {
57
51
  "ttsc": ">=0.19.2"
@@ -62,12 +56,9 @@
62
56
  }
63
57
  },
64
58
  "devDependencies": {
65
- "@types/inquirer": "^8.2.5",
66
59
  "@types/node": "^25.3.0",
67
- "chalk": "^4.0.0",
68
60
  "rimraf": "^6.1.2",
69
61
  "rolldown": "^1.1.5",
70
- "suppress-warnings": "^1.0.2",
71
62
  "ttsc": "^0.19.2",
72
63
  "typescript": "^7.0.2"
73
64
  },
@@ -1,6 +1,7 @@
1
1
  import RandExp from "randexp";
2
2
 
3
3
  import { _ILengthProps } from "./_randomStringLength";
4
+ import { _stringLength } from "./_stringLength";
4
5
 
5
6
  export const _randomPattern = (
6
7
  regex: RegExp,
@@ -16,14 +17,28 @@ export const _randomPattern = (
16
17
  if (max !== undefined) r.max = max;
17
18
  else if (min !== undefined && min * 2 > r.max) r.max = min * 2;
18
19
 
20
+ // The verification counts characters, not UTF-16 code units, because that is
21
+ // what `MinLength` and `MaxLength` compare against. `Pattern` compiles to a
22
+ // flagless `RegExp`, so a quantifier binds one code unit wherever its atom is
23
+ // a bare astral character: under `^😀+$` the `+` repeats the trailing low
24
+ // surrogate alone, and RandExp draws `\ud83d` + k * `\ude00` -- k characters
25
+ // in k + 1 code units. Measuring in code units accepted a two-character draw
26
+ // for `MinLength<3>` and handed back a value the generated validator rejects.
27
+ // Group the atom (`^(?:😀)+$`) and the arithmetic changes to 2k units for k
28
+ // characters; the two counts still differ, which is the point.
29
+ //
30
+ // The count is taken only after the pattern matches, and only when a bound
31
+ // exists, so an unbounded draw still walks nothing.
19
32
  const bounded: boolean = min !== undefined || max !== undefined;
20
33
  const limit: number = bounded ? 1024 : 10;
21
34
  for (let i: number = 0; i < limit; ++i) {
22
35
  const str: string = r.gen();
36
+ if (regex.test(str) === false) continue;
37
+ if (bounded === false) return str;
38
+ const length: number = _stringLength(str);
23
39
  if (
24
- regex.test(str) &&
25
- (min === undefined || str.length >= min) &&
26
- (max === undefined || str.length <= max)
40
+ (min === undefined || length >= min) &&
41
+ (max === undefined || length <= max)
27
42
  )
28
43
  return str;
29
44
  }
@@ -1,5 +1,6 @@
1
1
  import { _randomInteger } from "./_randomInteger";
2
2
  import { _randomString } from "./_randomString";
3
+ import { _stringLength } from "./_stringLength";
3
4
 
4
5
  export const _RANDOM_LENGTH_ERROR =
5
6
  "unable to generate a random string satisfying both the format and the length constraints.";
@@ -106,9 +107,15 @@ export const _randomFormatLength = (
106
107
  return generate();
107
108
  for (let i: number = 0; i < 256; ++i) {
108
109
  const value: string = generate();
110
+ // Characters, not UTF-16 code units: `minLength` and `maxLength` are the
111
+ // bounds the generated validator compares in characters. `uuid` and `date`,
112
+ // the only two shapes routed through here, are ASCII, so the two counts
113
+ // agree for them today -- this keeps one measure so a later non-ASCII
114
+ // format cannot reintroduce the divergence unnoticed.
115
+ const length: number = _stringLength(value);
109
116
  if (
110
- (props.minLength === undefined || value.length >= props.minLength) &&
111
- (props.maxLength === undefined || value.length <= props.maxLength)
117
+ (props.minLength === undefined || length >= props.minLength) &&
118
+ (props.maxLength === undefined || length <= props.maxLength)
112
119
  )
113
120
  return value;
114
121
  }
@@ -1,44 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- export declare namespace FileSystemIdentity {
4
- interface IIdentity {
5
- readonly caseSensitive: boolean;
6
- contains(file: string, directory: string): boolean;
7
- filesystemKey(file: string): string;
8
- isDeclarationFile(file: string): boolean;
9
- isSamePath(x: string, y: string): boolean;
10
- isSupportedExtension(file: string): boolean;
11
- projectFileKey(file: string): string;
12
- }
13
- function create(caseSensitive: boolean, pathApi?: typeof path.posix): IIdentity;
14
- class Policy {
15
- private caseSensitive_;
16
- private location_;
17
- observe(caseSensitive: boolean | undefined, location: string): void;
18
- get(): IIdentity;
19
- }
20
- function inspectDirectory(directory: string): Promise<boolean | undefined>;
21
- function probeDirectory(directory: string): Promise<boolean>;
22
- /**
23
- * Builds the key that decides whether two paths are the same filesystem
24
- * object.
25
- *
26
- * The identity is read from `fs.BigIntStats` rather than `fs.Stats`, because
27
- * `fs.Stats.ino` is a JavaScript number. An NTFS file ID is `(sequenceNumber
28
- * << 48) | mftRecordIndex` and routinely exceeds `Number.MAX_SAFE_INTEGER` —
29
- * in a 4000-directory probe, 1879 of them did — where the spacing between
30
- * representable doubles is 8 or more. Two entries sharing a sequence number
31
- * with MFT record indices within that spacing round to one double. The
32
- * traversal callers would then treat a distinct directory or file as already
33
- * visited and skip it with no diagnostic, and the overwrite guard would
34
- * refuse a legitimate output (samchon/typia#2269).
35
- *
36
- * A filesystem that reports no inode at all keeps the canonical path
37
- * fallback, which is the only identity available there.
38
- *
39
- * @param stat Stats read with `{ bigint: true }`.
40
- * @param realpath Canonical path of the same object.
41
- * @returns Key that is equal for two paths only when they are one object.
42
- */
43
- function identityKey(stat: Pick<fs.BigIntStats, "dev" | "ino">, realpath: string): string;
44
- }
@@ -1,213 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.FileSystemIdentity = void 0;
16
- const fs_1 = __importDefault(require("fs"));
17
- const path_1 = __importDefault(require("path"));
18
- var FileSystemIdentity;
19
- (function (FileSystemIdentity) {
20
- function create(caseSensitive, pathApi = path_1.default) {
21
- const fold = (value) => caseSensitive ? value : value.toLowerCase();
22
- const filesystemKey = (file) => fold(pathApi.normalize(file));
23
- return {
24
- caseSensitive,
25
- contains: (file, directory) => {
26
- const fileKey = filesystemKey(pathApi.resolve(file));
27
- const directoryKey = filesystemKey(pathApi.resolve(directory));
28
- const prefix = directoryKey.endsWith(pathApi.sep)
29
- ? directoryKey
30
- : `${directoryKey}${pathApi.sep}`;
31
- return fileKey === directoryKey || fileKey.startsWith(prefix);
32
- },
33
- filesystemKey,
34
- isDeclarationFile: (file) => DTS_PATTERN.test(fold(pathApi.basename(file))),
35
- isSamePath: (x, y) => filesystemKey(x) === filesystemKey(y),
36
- isSupportedExtension: (file) => {
37
- const filename = fold(pathApi.basename(file));
38
- return (TS_PATTERN.test(filename) && DTS_PATTERN.test(filename) === false);
39
- },
40
- projectFileKey: fold,
41
- };
42
- }
43
- FileSystemIdentity.create = create;
44
- class Policy {
45
- observe(caseSensitive, location) {
46
- if (caseSensitive === undefined)
47
- return;
48
- if (this.caseSensitive_ === undefined) {
49
- this.caseSensitive_ = caseSensitive;
50
- this.location_ = location;
51
- }
52
- else if (this.caseSensitive_ !== caseSensitive) {
53
- throw new URIError(`Error on TypiaGenerateWizard.generate(): inconsistent filesystem case behavior between ${this.location_} and ${location}.`);
54
- }
55
- }
56
- get() {
57
- if (this.caseSensitive_ === undefined) {
58
- throw new URIError("Error on TypiaGenerateWizard.generate(): unable to determine filesystem case behavior for this run.");
59
- }
60
- return create(this.caseSensitive_);
61
- }
62
- }
63
- FileSystemIdentity.Policy = Policy;
64
- function inspectDirectory(directory) {
65
- return __awaiter(this, void 0, void 0, function* () {
66
- const names = yield fs_1.default.promises.readdir(directory);
67
- const entries = new Set(names);
68
- for (const name of names) {
69
- const alternate = alternateCase(name);
70
- if (alternate === undefined)
71
- continue;
72
- if (entries.has(alternate))
73
- return true;
74
- try {
75
- yield fs_1.default.promises.lstat(path_1.default.join(directory, alternate));
76
- return false;
77
- }
78
- catch (error) {
79
- if (isMissingFileError(error))
80
- return true;
81
- throw error;
82
- }
83
- }
84
- return undefined;
85
- });
86
- }
87
- FileSystemIdentity.inspectDirectory = inspectDirectory;
88
- function probeDirectory(directory) {
89
- return __awaiter(this, void 0, void 0, function* () {
90
- const inspected = yield inspectDirectory(directory);
91
- if (inspected !== undefined)
92
- return inspected;
93
- for (let attempt = 0; attempt < 10; ++attempt) {
94
- const name = `.typia-case-probe-${process.pid}-${Date.now()}-${Math.random()
95
- .toString(36)
96
- .slice(2)}`.toLowerCase();
97
- const original = path_1.default.join(directory, name);
98
- const alternate = path_1.default.join(directory, name.toUpperCase());
99
- let handle;
100
- try {
101
- handle = yield fs_1.default.promises.open(original, "wx");
102
- }
103
- catch (error) {
104
- if (isAlreadyExistsError(error))
105
- continue;
106
- throw new URIError(`Error on TypiaGenerateWizard.generate(): unable to probe filesystem case behavior at ${directory}: ${formatUnknownError(error)}`);
107
- }
108
- let closed = false;
109
- try {
110
- yield handle.close();
111
- closed = true;
112
- try {
113
- yield fs_1.default.promises.lstat(alternate);
114
- return false;
115
- }
116
- catch (error) {
117
- if (isMissingFileError(error))
118
- return true;
119
- throw error;
120
- }
121
- }
122
- finally {
123
- let cleanupError;
124
- if (closed === false) {
125
- try {
126
- yield handle.close();
127
- }
128
- catch (error) {
129
- cleanupError = error;
130
- }
131
- }
132
- try {
133
- yield fs_1.default.promises.unlink(original);
134
- }
135
- catch (error) {
136
- if (isMissingFileError(error) === false)
137
- cleanupError !== null && cleanupError !== void 0 ? cleanupError : (cleanupError = error);
138
- }
139
- if (cleanupError !== undefined) {
140
- throw new URIError(`Error on TypiaGenerateWizard.generate(): unable to clean filesystem case probe ${original}: ${formatUnknownError(cleanupError)}`);
141
- }
142
- }
143
- }
144
- throw new URIError(`Error on TypiaGenerateWizard.generate(): unable to reserve a filesystem case probe at ${directory}.`);
145
- });
146
- }
147
- FileSystemIdentity.probeDirectory = probeDirectory;
148
- /**
149
- * Builds the key that decides whether two paths are the same filesystem
150
- * object.
151
- *
152
- * The identity is read from `fs.BigIntStats` rather than `fs.Stats`, because
153
- * `fs.Stats.ino` is a JavaScript number. An NTFS file ID is `(sequenceNumber
154
- * << 48) | mftRecordIndex` and routinely exceeds `Number.MAX_SAFE_INTEGER` —
155
- * in a 4000-directory probe, 1879 of them did — where the spacing between
156
- * representable doubles is 8 or more. Two entries sharing a sequence number
157
- * with MFT record indices within that spacing round to one double. The
158
- * traversal callers would then treat a distinct directory or file as already
159
- * visited and skip it with no diagnostic, and the overwrite guard would
160
- * refuse a legitimate output (samchon/typia#2269).
161
- *
162
- * A filesystem that reports no inode at all keeps the canonical path
163
- * fallback, which is the only identity available there.
164
- *
165
- * @param stat Stats read with `{ bigint: true }`.
166
- * @param realpath Canonical path of the same object.
167
- * @returns Key that is equal for two paths only when they are one object.
168
- */
169
- function identityKey(stat, realpath) {
170
- // `BigInt(0)` rather than `0n`: this package compiles at ES2016, where a
171
- // BigInt literal is a syntax error, while the constructor and the `bigint`
172
- // type are fine.
173
- return stat.ino === BigInt(0)
174
- ? `path:${path_1.default.normalize(realpath)}`
175
- : `inode:${stat.dev}:${stat.ino}`;
176
- }
177
- FileSystemIdentity.identityKey = identityKey;
178
- function alternateCase(name) {
179
- let changed = false;
180
- let output = "";
181
- for (const character of name) {
182
- if (character >= "a" && character <= "z") {
183
- output += character.toUpperCase();
184
- changed = true;
185
- }
186
- else if (character >= "A" && character <= "Z") {
187
- output += character.toLowerCase();
188
- changed = true;
189
- }
190
- else
191
- output += character;
192
- }
193
- return changed ? output : undefined;
194
- }
195
- function isAlreadyExistsError(error) {
196
- return (typeof error === "object" &&
197
- error !== null &&
198
- "code" in error &&
199
- error.code === "EEXIST");
200
- }
201
- function isMissingFileError(error) {
202
- return (typeof error === "object" &&
203
- error !== null &&
204
- "code" in error &&
205
- error.code === "ENOENT");
206
- }
207
- function formatUnknownError(error) {
208
- return error instanceof Error ? error.message : String(error);
209
- }
210
- })(FileSystemIdentity || (exports.FileSystemIdentity = FileSystemIdentity = {}));
211
- const TS_PATTERN = /\.[cm]?tsx?$/;
212
- const DTS_PATTERN = /\.d\.[cm]?tsx?$/;
213
- //# sourceMappingURL=FileSystemIdentity.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FileSystemIdentity.js","sourceRoot":"","sources":["../../src/executable/FileSystemIdentity.ts"],"names":[],"mappings":"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"}