@atproto/syntax 0.4.2 → 0.5.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 (77) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/at-identifier.d.ts +1 -0
  4. package/dist/at-identifier.d.ts.map +1 -1
  5. package/dist/at-identifier.js +9 -0
  6. package/dist/at-identifier.js.map +1 -1
  7. package/dist/aturi.d.ts +8 -0
  8. package/dist/aturi.d.ts.map +1 -1
  9. package/dist/aturi.js +31 -40
  10. package/dist/aturi.js.map +1 -1
  11. package/dist/aturi_validation.d.ts +3 -2
  12. package/dist/aturi_validation.d.ts.map +1 -1
  13. package/dist/aturi_validation.js +13 -3
  14. package/dist/aturi_validation.js.map +1 -1
  15. package/dist/datetime.d.ts +128 -11
  16. package/dist/datetime.d.ts.map +1 -1
  17. package/dist/datetime.js +205 -79
  18. package/dist/datetime.js.map +1 -1
  19. package/dist/did.d.ts +3 -2
  20. package/dist/did.d.ts.map +1 -1
  21. package/dist/did.js +18 -13
  22. package/dist/did.js.map +1 -1
  23. package/dist/handle.d.ts +4 -3
  24. package/dist/handle.d.ts.map +1 -1
  25. package/dist/handle.js +9 -9
  26. package/dist/handle.js.map +1 -1
  27. package/dist/index.d.ts +7 -5
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +11 -22
  30. package/dist/index.js.map +1 -1
  31. package/dist/language.d.ts +18 -0
  32. package/dist/language.d.ts.map +1 -0
  33. package/dist/language.js +30 -0
  34. package/dist/language.js.map +1 -0
  35. package/dist/nsid.d.ts +4 -4
  36. package/dist/nsid.d.ts.map +1 -1
  37. package/dist/nsid.js +9 -11
  38. package/dist/nsid.js.map +1 -1
  39. package/dist/recordkey.d.ts +2 -2
  40. package/dist/recordkey.d.ts.map +1 -1
  41. package/dist/recordkey.js +10 -10
  42. package/dist/recordkey.js.map +1 -1
  43. package/dist/tid.d.ts +2 -2
  44. package/dist/tid.d.ts.map +1 -1
  45. package/dist/tid.js +5 -5
  46. package/dist/tid.js.map +1 -1
  47. package/dist/uri.d.ts +3 -0
  48. package/dist/uri.d.ts.map +1 -0
  49. package/dist/uri.js +7 -0
  50. package/dist/uri.js.map +1 -0
  51. package/package.json +7 -4
  52. package/src/at-identifier.ts +12 -1
  53. package/src/aturi.ts +30 -1
  54. package/src/aturi_validation.ts +20 -4
  55. package/src/datetime.ts +271 -92
  56. package/src/did.ts +25 -15
  57. package/src/handle.ts +17 -13
  58. package/src/index.ts +7 -5
  59. package/src/language.ts +39 -0
  60. package/src/nsid.ts +13 -7
  61. package/src/recordkey.ts +14 -12
  62. package/src/tid.ts +7 -5
  63. package/src/uri.ts +5 -0
  64. package/tests/aturi.test.ts +50 -2
  65. package/tests/datetime.test.ts +148 -61
  66. package/tests/did.test.ts +1 -0
  67. package/tests/handle.test.ts +1 -0
  68. package/tests/language.test.ts +88 -0
  69. package/tests/nsid.test.ts +1 -0
  70. package/tests/recordkey.test.ts +1 -0
  71. package/tests/tid.test.ts +1 -0
  72. package/tsconfig.build.json +5 -2
  73. package/tsconfig.build.tsbuildinfo +1 -1
  74. package/tsconfig.tests.json +6 -4
  75. package/vitest.config.ts +5 -0
  76. package/jest.config.js +0 -7
  77. package/tsconfig.tests.tsbuildinfo +0 -1
package/dist/index.js CHANGED
@@ -1,25 +1,14 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
2
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./handle.js"), exports);
18
- __exportStar(require("./did.js"), exports);
19
- __exportStar(require("./nsid.js"), exports);
20
- __exportStar(require("./aturi.js"), exports);
21
- __exportStar(require("./at-identifier.js"), exports);
22
- __exportStar(require("./tid.js"), exports);
23
- __exportStar(require("./recordkey.js"), exports);
24
- __exportStar(require("./datetime.js"), exports);
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./at-identifier.js"), exports);
5
+ tslib_1.__exportStar(require("./aturi.js"), exports);
6
+ tslib_1.__exportStar(require("./datetime.js"), exports);
7
+ tslib_1.__exportStar(require("./did.js"), exports);
8
+ tslib_1.__exportStar(require("./handle.js"), exports);
9
+ tslib_1.__exportStar(require("./nsid.js"), exports);
10
+ tslib_1.__exportStar(require("./language.js"), exports);
11
+ tslib_1.__exportStar(require("./recordkey.js"), exports);
12
+ tslib_1.__exportStar(require("./tid.js"), exports);
13
+ tslib_1.__exportStar(require("./uri.js"), exports);
25
14
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B;AAC3B,2CAAwB;AACxB,4CAAyB;AACzB,6CAA0B;AAC1B,qDAAkC;AAClC,2CAAwB;AACxB,iDAA8B;AAC9B,gDAA6B","sourcesContent":["export * from './handle.js'\nexport * from './did.js'\nexport * from './nsid.js'\nexport * from './aturi.js'\nexport * from './at-identifier.js'\nexport * from './tid.js'\nexport * from './recordkey.js'\nexport * from './datetime.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAkC;AAClC,qDAA0B;AAC1B,wDAA6B;AAC7B,mDAAwB;AACxB,sDAA2B;AAC3B,oDAAyB;AACzB,wDAA6B;AAC7B,yDAA8B;AAC9B,mDAAwB;AACxB,mDAAwB","sourcesContent":["export * from './at-identifier.js'\nexport * from './aturi.js'\nexport * from './datetime.js'\nexport * from './did.js'\nexport * from './handle.js'\nexport * from './nsid.js'\nexport * from './language.js'\nexport * from './recordkey.js'\nexport * from './tid.js'\nexport * from './uri.js'\n"]}
@@ -0,0 +1,18 @@
1
+ export type LanguageTag = {
2
+ grandfathered?: string;
3
+ language?: string;
4
+ extlang?: string;
5
+ script?: string;
6
+ region?: string;
7
+ variant?: string;
8
+ extension?: string;
9
+ privateUse?: string;
10
+ };
11
+ export declare function parseLanguageString(input: string): LanguageTag | null;
12
+ /**
13
+ * Validates well-formed BCP 47 syntax
14
+ *
15
+ * @see {@link https://www.rfc-editor.org/rfc/rfc5646.html#section-2.1}
16
+ */
17
+ export declare function isValidLanguage(input: string): boolean;
18
+ //# sourceMappingURL=language.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language.d.ts","sourceRoot":"","sources":["../src/language.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAerE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD"}
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseLanguageString = parseLanguageString;
4
+ exports.isValidLanguage = isValidLanguage;
5
+ const BCP47_REGEXP = /^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUseA>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUseB>x(-[A-Za-z0-9]{1,8})+))$/;
6
+ function parseLanguageString(input) {
7
+ const parsed = input.match(BCP47_REGEXP);
8
+ if (!parsed?.groups)
9
+ return null;
10
+ const { groups } = parsed;
11
+ return {
12
+ grandfathered: groups.grandfathered,
13
+ language: groups.language,
14
+ extlang: groups.extlang,
15
+ script: groups.script,
16
+ region: groups.region,
17
+ variant: groups.variant,
18
+ extension: groups.extension,
19
+ privateUse: groups.privateUseA || groups.privateUseB,
20
+ };
21
+ }
22
+ /**
23
+ * Validates well-formed BCP 47 syntax
24
+ *
25
+ * @see {@link https://www.rfc-editor.org/rfc/rfc5646.html#section-2.1}
26
+ */
27
+ function isValidLanguage(input) {
28
+ return BCP47_REGEXP.test(input);
29
+ }
30
+ //# sourceMappingURL=language.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language.js","sourceRoot":"","sources":["../src/language.ts"],"names":[],"mappings":";;AAcA,kDAeC;AAOD,0CAEC;AAtCD,MAAM,YAAY,GAChB,mlBAAmlB,CAAA;AAarlB,SAAgB,mBAAmB,CAAC,KAAa;IAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,MAAM;QAAE,OAAO,IAAI,CAAA;IAEhC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IACzB,OAAO;QACL,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW;KACrD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,KAAa;IAC3C,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACjC,CAAC","sourcesContent":["const BCP47_REGEXP =\n /^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUseA>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUseB>x(-[A-Za-z0-9]{1,8})+))$/\n\nexport type LanguageTag = {\n grandfathered?: string\n language?: string\n extlang?: string\n script?: string\n region?: string\n variant?: string\n extension?: string\n privateUse?: string\n}\n\nexport function parseLanguageString(input: string): LanguageTag | null {\n const parsed = input.match(BCP47_REGEXP)\n if (!parsed?.groups) return null\n\n const { groups } = parsed\n return {\n grandfathered: groups.grandfathered,\n language: groups.language,\n extlang: groups.extlang,\n script: groups.script,\n region: groups.region,\n variant: groups.variant,\n extension: groups.extension,\n privateUse: groups.privateUseA || groups.privateUseB,\n }\n}\n\n/**\n * Validates well-formed BCP 47 syntax\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc5646.html#section-2.1}\n */\nexport function isValidLanguage(input: string): boolean {\n return BCP47_REGEXP.test(input)\n}\n"]}
package/dist/nsid.d.ts CHANGED
@@ -8,13 +8,13 @@ export declare class NSID {
8
8
  toString: () => string;
9
9
  }): NSID;
10
10
  constructor(nsid: string);
11
- get authority(): string;
11
+ get authority(): `${string}.${string}`;
12
12
  get name(): string | undefined;
13
- toString(): string;
13
+ toString(): NsidString;
14
14
  }
15
- export declare function ensureValidNsid(nsid: string): asserts nsid is NsidString;
15
+ export declare function ensureValidNsid<I extends string>(input: I): asserts input is I & NsidString;
16
16
  export declare function parseNsid(nsid: string): string[];
17
- export declare function isValidNsid(nsid: string): nsid is NsidString;
17
+ export declare function isValidNsid<I extends string>(input: I): input is I & NsidString;
18
18
  type ValidateResult<T> = {
19
19
  success: true;
20
20
  value: T;
@@ -1 +1 @@
1
- {"version":3,"file":"nsid.d.ts","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,UAAU,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,CAAA;AAExD,qBAAa,IAAI;IACf,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAEpC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpD,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM;IAI3B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;KAAE,GAAG,IAAI;gBAWxC,IAAI,EAAE,MAAM;IAIxB,IAAI,SAAS,WAKZ;IAED,IAAI,IAAI,uBAEP;IAED,QAAQ;CAGT;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,UAAU,CAGxE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,UAAU,CAI5D;AAED,KAAK,cAAc,CAAC,CAAC,IACjB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAKvC,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,CA0DpE;AA4BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,UAAU,CAG7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAuB3E;AAED,qBAAa,gBAAiB,SAAQ,KAAK;CAAG"}
1
+ {"version":3,"file":"nsid.d.ts","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,UAAU,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,CAAA;AAExD,qBAAa,IAAI;IACf,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAEpC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpD,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM;IAI3B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;KAAE,GAAG,IAAI;gBAWxC,IAAI,EAAE,MAAM;IAIxB,IAAI,SAAS,IAIK,GAAG,MAAM,IAAI,MAAM,EAAE,CACtC;IAED,IAAI,IAAI,uBAEP;IAED,QAAQ,IAAI,UAAU;CAGvB;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC9C,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,UAAU,CAGjC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,CAAC,GAAG,UAAU,CAIzB;AAED,KAAK,cAAc,CAAC,CAAC,IACjB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAKvC,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,CA0DpE;AA4BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,UAAU,CAG7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAyB3E;AAED,qBAAa,gBAAiB,SAAQ,KAAK;CAAG"}
package/dist/nsid.js CHANGED
@@ -20,6 +20,7 @@ exports.validateNsid = validateNsid;
20
20
  exports.ensureValidNsidRegex = ensureValidNsidRegex;
21
21
  exports.validateNsidRegex = validateNsidRegex;
22
22
  class NSID {
23
+ segments;
23
24
  static parse(input) {
24
25
  return new NSID(input);
25
26
  }
@@ -41,12 +42,6 @@ class NSID {
41
42
  return new NSID(String(input));
42
43
  }
43
44
  constructor(nsid) {
44
- Object.defineProperty(this, "segments", {
45
- enumerable: true,
46
- configurable: true,
47
- writable: true,
48
- value: void 0
49
- });
50
45
  this.segments = parseNsid(nsid);
51
46
  }
52
47
  get authority() {
@@ -63,8 +58,8 @@ class NSID {
63
58
  }
64
59
  }
65
60
  exports.NSID = NSID;
66
- function ensureValidNsid(nsid) {
67
- const result = validateNsid(nsid);
61
+ function ensureValidNsid(input) {
62
+ const result = validateNsid(input);
68
63
  if (!result.success)
69
64
  throw new InvalidNsidError(result.message);
70
65
  }
@@ -74,10 +69,10 @@ function parseNsid(nsid) {
74
69
  throw new InvalidNsidError(result.message);
75
70
  return result.value;
76
71
  }
77
- function isValidNsid(nsid) {
72
+ function isValidNsid(input) {
78
73
  // Since the regex version is more performant for valid NSIDs, we use it when
79
74
  // we don't care about error details.
80
- return validateNsidRegex(nsid).success;
75
+ return validateNsidRegex(input).success;
81
76
  }
82
77
  // Human readable constraints on NSID:
83
78
  // - a valid domain in reversed notation
@@ -180,7 +175,10 @@ function validateNsidRegex(value) {
180
175
  message: 'NSID is too long (317 chars max)',
181
176
  };
182
177
  }
183
- if (!/^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/.test(value)) {
178
+ if (
179
+ // Fast check for small values
180
+ value.length < 5 ||
181
+ !/^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/.test(value)) {
184
182
  return {
185
183
  success: false,
186
184
  message: "NSID didn't validate via regex",
package/dist/nsid.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"nsid.js","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"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","sourcesContent":["/*\nGrammar:\n\nalpha = \"a\" / \"b\" / \"c\" / \"d\" / \"e\" / \"f\" / \"g\" / \"h\" / \"i\" / \"j\" / \"k\" / \"l\" / \"m\" / \"n\" / \"o\" / \"p\" / \"q\" / \"r\" / \"s\" / \"t\" / \"u\" / \"v\" / \"w\" / \"x\" / \"y\" / \"z\" / \"A\" / \"B\" / \"C\" / \"D\" / \"E\" / \"F\" / \"G\" / \"H\" / \"I\" / \"J\" / \"K\" / \"L\" / \"M\" / \"N\" / \"O\" / \"P\" / \"Q\" / \"R\" / \"S\" / \"T\" / \"U\" / \"V\" / \"W\" / \"X\" / \"Y\" / \"Z\"\nnumber = \"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\" / \"7\" / \"8\" / \"9\" / \"0\"\ndelim = \".\"\nsegment = alpha *( alpha / number / \"-\" )\nauthority = segment *( delim segment )\nname = alpha *( alpha / number )\nnsid = authority delim name\n\n*/\n\nexport type NsidString = `${string}.${string}.${string}`\n\nexport class NSID {\n readonly segments: readonly string[]\n\n static parse(input: string): NSID {\n return new NSID(input)\n }\n\n static create(authority: string, name: string): NSID {\n const input = [...authority.split('.').reverse(), name].join('.')\n return new NSID(input)\n }\n\n static isValid(nsid: string) {\n return isValidNsid(nsid)\n }\n\n static from(input: { toString: () => string }): NSID {\n if (input instanceof NSID) {\n // No need to clone, NSID is immutable\n return input\n }\n if (Array.isArray(input)) {\n return new NSID((input as string[]).join('.'))\n }\n return new NSID(String(input))\n }\n\n constructor(nsid: string) {\n this.segments = parseNsid(nsid)\n }\n\n get authority() {\n return this.segments\n .slice(0, this.segments.length - 1)\n .reverse()\n .join('.')\n }\n\n get name() {\n return this.segments.at(this.segments.length - 1)\n }\n\n toString() {\n return this.segments.join('.')\n }\n}\n\nexport function ensureValidNsid(nsid: string): asserts nsid is NsidString {\n const result = validateNsid(nsid)\n if (!result.success) throw new InvalidNsidError(result.message)\n}\n\nexport function parseNsid(nsid: string): string[] {\n const result = validateNsid(nsid)\n if (!result.success) throw new InvalidNsidError(result.message)\n return result.value\n}\n\nexport function isValidNsid(nsid: string): nsid is NsidString {\n // Since the regex version is more performant for valid NSIDs, we use it when\n // we don't care about error details.\n return validateNsidRegex(nsid).success\n}\n\ntype ValidateResult<T> =\n | { success: true; value: T }\n | { success: false; message: string }\n\n// Human readable constraints on NSID:\n// - a valid domain in reversed notation\n// - followed by an additional period-separated name, which is camel-case letters\nexport function validateNsid(input: string): ValidateResult<string[]> {\n if (input.length > 253 + 1 + 63) {\n return {\n success: false,\n message: 'NSID is too long (317 chars max)',\n }\n }\n if (hasDisallowedCharacters(input)) {\n return {\n success: false,\n message:\n 'Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)',\n }\n }\n const segments = input.split('.')\n if (segments.length < 3) {\n return {\n success: false,\n message: 'NSID needs at least three parts',\n }\n }\n for (const l of segments) {\n if (l.length < 1) {\n return {\n success: false,\n message: 'NSID parts can not be empty',\n }\n }\n if (l.length > 63) {\n return {\n success: false,\n message: 'NSID part too long (max 63 chars)',\n }\n }\n if (startsWithHyphen(l) || endsWithHyphen(l)) {\n return {\n success: false,\n message: 'NSID parts can not start or end with hyphen',\n }\n }\n }\n if (startsWithNumber(segments[0])) {\n return {\n success: false,\n message: 'NSID first part may not start with a digit',\n }\n }\n if (!isValidIdentifier(segments[segments.length - 1])) {\n return {\n success: false,\n message:\n 'NSID name part must be only letters and digits (and no leading digit)',\n }\n }\n return {\n success: true,\n value: segments,\n }\n}\n\nfunction hasDisallowedCharacters(v: string) {\n return !/^[a-zA-Z0-9.-]*$/.test(v)\n}\n\nfunction startsWithNumber(v: string) {\n const charCode = v.charCodeAt(0)\n return charCode >= 48 && charCode <= 57\n}\n\nfunction startsWithHyphen(v: string) {\n return v.charCodeAt(0) === 45 /* - */\n}\n\nfunction endsWithHyphen(v: string) {\n return v.charCodeAt(v.length - 1) === 45 /* - */\n}\n\nfunction isValidIdentifier(v: string) {\n // Note, since we already know that \"v\" only contains [a-zA-Z0-9-], we can\n // simplify the following regex by checking only the first char and presence\n // of \"-\".\n\n // return /^[a-zA-Z][a-zA-Z0-9]*$/.test(v)\n return !startsWithNumber(v) && !v.includes('-')\n}\n\n/**\n * @deprecated Use {@link ensureValidNsid} if you care about error details,\n * {@link parseNsid}/{@link NSID.parse} if you need the parsed segments, or\n * {@link isValidNsid} if you just want a boolean.\n */\nexport function ensureValidNsidRegex(nsid: string): asserts nsid is NsidString {\n const result = validateNsidRegex(nsid)\n if (!result.success) throw new InvalidNsidError(result.message)\n}\n\n/**\n * Regexp based validation that behaves identically to the previous code but\n * provides less detailed error messages (while being 20% to 50% faster).\n */\nexport function validateNsidRegex(value: string): ValidateResult<NsidString> {\n if (value.length > 253 + 1 + 63) {\n return {\n success: false,\n message: 'NSID is too long (317 chars max)',\n }\n }\n\n if (\n !/^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/.test(\n value,\n )\n ) {\n return {\n success: false,\n message: \"NSID didn't validate via regex\",\n }\n }\n\n return {\n success: true,\n value: value as NsidString,\n }\n}\n\nexport class InvalidNsidError extends Error {}\n"]}
1
+ {"version":3,"file":"nsid.js","sourceRoot":"","sources":["../src/nsid.ts"],"names":[],"mappings":"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","sourcesContent":["/*\nGrammar:\n\nalpha = \"a\" / \"b\" / \"c\" / \"d\" / \"e\" / \"f\" / \"g\" / \"h\" / \"i\" / \"j\" / \"k\" / \"l\" / \"m\" / \"n\" / \"o\" / \"p\" / \"q\" / \"r\" / \"s\" / \"t\" / \"u\" / \"v\" / \"w\" / \"x\" / \"y\" / \"z\" / \"A\" / \"B\" / \"C\" / \"D\" / \"E\" / \"F\" / \"G\" / \"H\" / \"I\" / \"J\" / \"K\" / \"L\" / \"M\" / \"N\" / \"O\" / \"P\" / \"Q\" / \"R\" / \"S\" / \"T\" / \"U\" / \"V\" / \"W\" / \"X\" / \"Y\" / \"Z\"\nnumber = \"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\" / \"7\" / \"8\" / \"9\" / \"0\"\ndelim = \".\"\nsegment = alpha *( alpha / number / \"-\" )\nauthority = segment *( delim segment )\nname = alpha *( alpha / number )\nnsid = authority delim name\n\n*/\n\nexport type NsidString = `${string}.${string}.${string}`\n\nexport class NSID {\n readonly segments: readonly string[]\n\n static parse(input: string): NSID {\n return new NSID(input)\n }\n\n static create(authority: string, name: string): NSID {\n const input = [...authority.split('.').reverse(), name].join('.')\n return new NSID(input)\n }\n\n static isValid(nsid: string) {\n return isValidNsid(nsid)\n }\n\n static from(input: { toString: () => string }): NSID {\n if (input instanceof NSID) {\n // No need to clone, NSID is immutable\n return input\n }\n if (Array.isArray(input)) {\n return new NSID((input as string[]).join('.'))\n }\n return new NSID(String(input))\n }\n\n constructor(nsid: string) {\n this.segments = parseNsid(nsid)\n }\n\n get authority() {\n return this.segments\n .slice(0, this.segments.length - 1)\n .reverse()\n .join('.') as `${string}.${string}`\n }\n\n get name() {\n return this.segments.at(this.segments.length - 1)\n }\n\n toString(): NsidString {\n return this.segments.join('.') as NsidString\n }\n}\n\nexport function ensureValidNsid<I extends string>(\n input: I,\n): asserts input is I & NsidString {\n const result = validateNsid(input)\n if (!result.success) throw new InvalidNsidError(result.message)\n}\n\nexport function parseNsid(nsid: string): string[] {\n const result = validateNsid(nsid)\n if (!result.success) throw new InvalidNsidError(result.message)\n return result.value\n}\n\nexport function isValidNsid<I extends string>(\n input: I,\n): input is I & NsidString {\n // Since the regex version is more performant for valid NSIDs, we use it when\n // we don't care about error details.\n return validateNsidRegex(input).success\n}\n\ntype ValidateResult<T> =\n | { success: true; value: T }\n | { success: false; message: string }\n\n// Human readable constraints on NSID:\n// - a valid domain in reversed notation\n// - followed by an additional period-separated name, which is camel-case letters\nexport function validateNsid(input: string): ValidateResult<string[]> {\n if (input.length > 253 + 1 + 63) {\n return {\n success: false,\n message: 'NSID is too long (317 chars max)',\n }\n }\n if (hasDisallowedCharacters(input)) {\n return {\n success: false,\n message:\n 'Disallowed characters in NSID (ASCII letters, digits, dashes, periods only)',\n }\n }\n const segments = input.split('.')\n if (segments.length < 3) {\n return {\n success: false,\n message: 'NSID needs at least three parts',\n }\n }\n for (const l of segments) {\n if (l.length < 1) {\n return {\n success: false,\n message: 'NSID parts can not be empty',\n }\n }\n if (l.length > 63) {\n return {\n success: false,\n message: 'NSID part too long (max 63 chars)',\n }\n }\n if (startsWithHyphen(l) || endsWithHyphen(l)) {\n return {\n success: false,\n message: 'NSID parts can not start or end with hyphen',\n }\n }\n }\n if (startsWithNumber(segments[0])) {\n return {\n success: false,\n message: 'NSID first part may not start with a digit',\n }\n }\n if (!isValidIdentifier(segments[segments.length - 1])) {\n return {\n success: false,\n message:\n 'NSID name part must be only letters and digits (and no leading digit)',\n }\n }\n return {\n success: true,\n value: segments,\n }\n}\n\nfunction hasDisallowedCharacters(v: string) {\n return !/^[a-zA-Z0-9.-]*$/.test(v)\n}\n\nfunction startsWithNumber(v: string) {\n const charCode = v.charCodeAt(0)\n return charCode >= 48 && charCode <= 57\n}\n\nfunction startsWithHyphen(v: string) {\n return v.charCodeAt(0) === 45 /* - */\n}\n\nfunction endsWithHyphen(v: string) {\n return v.charCodeAt(v.length - 1) === 45 /* - */\n}\n\nfunction isValidIdentifier(v: string) {\n // Note, since we already know that \"v\" only contains [a-zA-Z0-9-], we can\n // simplify the following regex by checking only the first char and presence\n // of \"-\".\n\n // return /^[a-zA-Z][a-zA-Z0-9]*$/.test(v)\n return !startsWithNumber(v) && !v.includes('-')\n}\n\n/**\n * @deprecated Use {@link ensureValidNsid} if you care about error details,\n * {@link parseNsid}/{@link NSID.parse} if you need the parsed segments, or\n * {@link isValidNsid} if you just want a boolean.\n */\nexport function ensureValidNsidRegex(nsid: string): asserts nsid is NsidString {\n const result = validateNsidRegex(nsid)\n if (!result.success) throw new InvalidNsidError(result.message)\n}\n\n/**\n * Regexp based validation that behaves identically to the previous code but\n * provides less detailed error messages (while being 20% to 50% faster).\n */\nexport function validateNsidRegex(value: string): ValidateResult<NsidString> {\n if (value.length > 253 + 1 + 63) {\n return {\n success: false,\n message: 'NSID is too long (317 chars max)',\n }\n }\n\n if (\n // Fast check for small values\n value.length < 5 ||\n !/^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/.test(\n value,\n )\n ) {\n return {\n success: false,\n message: \"NSID didn't validate via regex\",\n }\n }\n\n return {\n success: true,\n value: value as NsidString,\n }\n}\n\nexport class InvalidNsidError extends Error {}\n"]}
@@ -1,6 +1,6 @@
1
1
  export type RecordKeyString = string;
2
- export declare function ensureValidRecordKey(rkey: string): asserts rkey is RecordKeyString;
3
- export declare function isValidRecordKey(rkey: string): rkey is RecordKeyString;
2
+ export declare function ensureValidRecordKey<I extends string>(input: I): asserts input is I & RecordKeyString;
3
+ export declare function isValidRecordKey<I extends string>(input: I): input is I & RecordKeyString;
4
4
  export declare class InvalidRecordKeyError extends Error {
5
5
  }
6
6
  //# sourceMappingURL=recordkey.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recordkey.d.ts","sourceRoot":"","sources":["../src/recordkey.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,MAAM,CAAA;AAmBpC,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,IAAI,eAAe,CAgBjC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,eAAe,CAOtE;AAED,qBAAa,qBAAsB,SAAQ,KAAK;CAAG"}
1
+ {"version":3,"file":"recordkey.d.ts","sourceRoot":"","sources":["../src/recordkey.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,MAAM,CAAA;AAmBpC,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,EACnD,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,eAAe,CAgBtC;AAED,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAC/C,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,CAAC,GAAG,eAAe,CAO9B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;CAAG"}
package/dist/recordkey.js CHANGED
@@ -18,24 +18,24 @@ const RECORD_KEY_REGEX = /^[a-zA-Z0-9_~.:-]{1,512}$/;
18
18
  // - must be permissible to include in a path component of a URI (following
19
19
  // RFC-3986, section 3.3). The above constraints satisfy this condition, by
20
20
  // matching the "unreserved" characters allowed in generic URI paths.
21
- function ensureValidRecordKey(rkey) {
22
- if (rkey.length > RECORD_KEY_MAX_LENGTH ||
23
- rkey.length < RECORD_KEY_MIN_LENGTH) {
21
+ function ensureValidRecordKey(input) {
22
+ if (input.length > RECORD_KEY_MAX_LENGTH ||
23
+ input.length < RECORD_KEY_MIN_LENGTH) {
24
24
  throw new InvalidRecordKeyError(`record key must be ${RECORD_KEY_MIN_LENGTH} to ${RECORD_KEY_MAX_LENGTH} characters`);
25
25
  }
26
- if (RECORD_KEY_INVALID_VALUES.has(rkey)) {
26
+ if (RECORD_KEY_INVALID_VALUES.has(input)) {
27
27
  throw new InvalidRecordKeyError('record key can not be "." or ".."');
28
28
  }
29
29
  // simple regex to enforce most constraints via just regex and length.
30
- if (!RECORD_KEY_REGEX.test(rkey)) {
30
+ if (!RECORD_KEY_REGEX.test(input)) {
31
31
  throw new InvalidRecordKeyError('record key syntax not valid (regex)');
32
32
  }
33
33
  }
34
- function isValidRecordKey(rkey) {
35
- return (rkey.length >= RECORD_KEY_MIN_LENGTH &&
36
- rkey.length <= RECORD_KEY_MAX_LENGTH &&
37
- RECORD_KEY_REGEX.test(rkey) &&
38
- !RECORD_KEY_INVALID_VALUES.has(rkey));
34
+ function isValidRecordKey(input) {
35
+ return (input.length >= RECORD_KEY_MIN_LENGTH &&
36
+ input.length <= RECORD_KEY_MAX_LENGTH &&
37
+ RECORD_KEY_REGEX.test(input) &&
38
+ !RECORD_KEY_INVALID_VALUES.has(input));
39
39
  }
40
40
  class InvalidRecordKeyError extends Error {
41
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"recordkey.js","sourceRoot":"","sources":["../src/recordkey.ts"],"names":[],"mappings":";;;AAmBA,oDAkBC;AAED,4CAOC;AA5CD,MAAM,qBAAqB,GAAG,GAAG,CAAA;AACjC,MAAM,qBAAqB,GAAG,CAAC,CAAA;AAC/B,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;AACtD,MAAM,gBAAgB,GAAG,2BAA2B,CAAA;AAEpD,yDAAyD;AACzD,qFAAqF;AACrF,6EAA6E;AAC7E,gFAAgF;AAChF,oDAAoD;AACpD,4DAA4D;AAC5D,wEAAwE;AACxE,wCAAwC;AACxC,2EAA2E;AAC3E,6EAA6E;AAC7E,uEAAuE;AAEvE,SAAgB,oBAAoB,CAClC,IAAY;IAEZ,IACE,IAAI,CAAC,MAAM,GAAG,qBAAqB;QACnC,IAAI,CAAC,MAAM,GAAG,qBAAqB,EACnC,CAAC;QACD,MAAM,IAAI,qBAAqB,CAC7B,sBAAsB,qBAAqB,OAAO,qBAAqB,aAAa,CACrF,CAAA;IACH,CAAC;IACD,IAAI,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,qBAAqB,CAAC,mCAAmC,CAAC,CAAA;IACtE,CAAC;IACD,sEAAsE;IACtE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,qBAAqB,CAAC,qCAAqC,CAAC,CAAA;IACxE,CAAC;AACH,CAAC;AAED,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,OAAO,CACL,IAAI,CAAC,MAAM,IAAI,qBAAqB;QACpC,IAAI,CAAC,MAAM,IAAI,qBAAqB;QACpC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3B,CAAC,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,CACrC,CAAA;AACH,CAAC;AAED,MAAa,qBAAsB,SAAQ,KAAK;CAAG;AAAnD,sDAAmD","sourcesContent":["export type RecordKeyString = string\n\nconst RECORD_KEY_MAX_LENGTH = 512\nconst RECORD_KEY_MIN_LENGTH = 1\nconst RECORD_KEY_INVALID_VALUES = new Set(['.', '..'])\nconst RECORD_KEY_REGEX = /^[a-zA-Z0-9_~.:-]{1,512}$/\n\n// https://atproto.com/specs/record-key#record-key-syntax\n// Regardless of the type, Record Keys must fulfill some baseline syntax constraints:\n// - restricted to a subset of ASCII characters -- the allowed characters are\n// alphanumeric (A-Za-z0-9), period, dash, underscore, colon, or tilde (.-_:~)\n// - must have at least 1 and at most 512 characters\n// - the specific record key values . and .. are not allowed\n// - must be a permissible part of repository MST path string (the above\n// constraints satisfy this condition)\n// - must be permissible to include in a path component of a URI (following\n// RFC-3986, section 3.3). The above constraints satisfy this condition, by\n// matching the \"unreserved\" characters allowed in generic URI paths.\n\nexport function ensureValidRecordKey(\n rkey: string,\n): asserts rkey is RecordKeyString {\n if (\n rkey.length > RECORD_KEY_MAX_LENGTH ||\n rkey.length < RECORD_KEY_MIN_LENGTH\n ) {\n throw new InvalidRecordKeyError(\n `record key must be ${RECORD_KEY_MIN_LENGTH} to ${RECORD_KEY_MAX_LENGTH} characters`,\n )\n }\n if (RECORD_KEY_INVALID_VALUES.has(rkey)) {\n throw new InvalidRecordKeyError('record key can not be \".\" or \"..\"')\n }\n // simple regex to enforce most constraints via just regex and length.\n if (!RECORD_KEY_REGEX.test(rkey)) {\n throw new InvalidRecordKeyError('record key syntax not valid (regex)')\n }\n}\n\nexport function isValidRecordKey(rkey: string): rkey is RecordKeyString {\n return (\n rkey.length >= RECORD_KEY_MIN_LENGTH &&\n rkey.length <= RECORD_KEY_MAX_LENGTH &&\n RECORD_KEY_REGEX.test(rkey) &&\n !RECORD_KEY_INVALID_VALUES.has(rkey)\n )\n}\n\nexport class InvalidRecordKeyError extends Error {}\n"]}
1
+ {"version":3,"file":"recordkey.js","sourceRoot":"","sources":["../src/recordkey.ts"],"names":[],"mappings":";;;AAmBA,oDAkBC;AAED,4CASC;AA9CD,MAAM,qBAAqB,GAAG,GAAG,CAAA;AACjC,MAAM,qBAAqB,GAAG,CAAC,CAAA;AAC/B,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;AACtD,MAAM,gBAAgB,GAAG,2BAA2B,CAAA;AAEpD,yDAAyD;AACzD,qFAAqF;AACrF,6EAA6E;AAC7E,gFAAgF;AAChF,oDAAoD;AACpD,4DAA4D;AAC5D,wEAAwE;AACxE,wCAAwC;AACxC,2EAA2E;AAC3E,6EAA6E;AAC7E,uEAAuE;AAEvE,SAAgB,oBAAoB,CAClC,KAAQ;IAER,IACE,KAAK,CAAC,MAAM,GAAG,qBAAqB;QACpC,KAAK,CAAC,MAAM,GAAG,qBAAqB,EACpC,CAAC;QACD,MAAM,IAAI,qBAAqB,CAC7B,sBAAsB,qBAAqB,OAAO,qBAAqB,aAAa,CACrF,CAAA;IACH,CAAC;IACD,IAAI,yBAAyB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,qBAAqB,CAAC,mCAAmC,CAAC,CAAA;IACtE,CAAC;IACD,sEAAsE;IACtE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,qBAAqB,CAAC,qCAAqC,CAAC,CAAA;IACxE,CAAC;AACH,CAAC;AAED,SAAgB,gBAAgB,CAC9B,KAAQ;IAER,OAAO,CACL,KAAK,CAAC,MAAM,IAAI,qBAAqB;QACrC,KAAK,CAAC,MAAM,IAAI,qBAAqB;QACrC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;QAC5B,CAAC,yBAAyB,CAAC,GAAG,CAAC,KAAK,CAAC,CACtC,CAAA;AACH,CAAC;AAED,MAAa,qBAAsB,SAAQ,KAAK;CAAG;AAAnD,sDAAmD","sourcesContent":["export type RecordKeyString = string\n\nconst RECORD_KEY_MAX_LENGTH = 512\nconst RECORD_KEY_MIN_LENGTH = 1\nconst RECORD_KEY_INVALID_VALUES = new Set(['.', '..'])\nconst RECORD_KEY_REGEX = /^[a-zA-Z0-9_~.:-]{1,512}$/\n\n// https://atproto.com/specs/record-key#record-key-syntax\n// Regardless of the type, Record Keys must fulfill some baseline syntax constraints:\n// - restricted to a subset of ASCII characters -- the allowed characters are\n// alphanumeric (A-Za-z0-9), period, dash, underscore, colon, or tilde (.-_:~)\n// - must have at least 1 and at most 512 characters\n// - the specific record key values . and .. are not allowed\n// - must be a permissible part of repository MST path string (the above\n// constraints satisfy this condition)\n// - must be permissible to include in a path component of a URI (following\n// RFC-3986, section 3.3). The above constraints satisfy this condition, by\n// matching the \"unreserved\" characters allowed in generic URI paths.\n\nexport function ensureValidRecordKey<I extends string>(\n input: I,\n): asserts input is I & RecordKeyString {\n if (\n input.length > RECORD_KEY_MAX_LENGTH ||\n input.length < RECORD_KEY_MIN_LENGTH\n ) {\n throw new InvalidRecordKeyError(\n `record key must be ${RECORD_KEY_MIN_LENGTH} to ${RECORD_KEY_MAX_LENGTH} characters`,\n )\n }\n if (RECORD_KEY_INVALID_VALUES.has(input)) {\n throw new InvalidRecordKeyError('record key can not be \".\" or \"..\"')\n }\n // simple regex to enforce most constraints via just regex and length.\n if (!RECORD_KEY_REGEX.test(input)) {\n throw new InvalidRecordKeyError('record key syntax not valid (regex)')\n }\n}\n\nexport function isValidRecordKey<I extends string>(\n input: I,\n): input is I & RecordKeyString {\n return (\n input.length >= RECORD_KEY_MIN_LENGTH &&\n input.length <= RECORD_KEY_MAX_LENGTH &&\n RECORD_KEY_REGEX.test(input) &&\n !RECORD_KEY_INVALID_VALUES.has(input)\n )\n}\n\nexport class InvalidRecordKeyError extends Error {}\n"]}
package/dist/tid.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type TidString = string;
2
- export declare function ensureValidTid(tid: string): asserts tid is TidString;
3
- export declare function isValidTid(tid: string): tid is TidString;
2
+ export declare function ensureValidTid<I extends string>(input: I): asserts input is I & TidString;
3
+ export declare function isValidTid<I extends string>(input: I): input is I & TidString;
4
4
  export declare class InvalidTidError extends Error {
5
5
  }
6
6
  //# sourceMappingURL=tid.d.ts.map
package/dist/tid.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tid.d.ts","sourceRoot":"","sources":["../src/tid.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAK9B,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,SAAS,CAQpE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,SAAS,CAExD;AAED,qBAAa,eAAgB,SAAQ,KAAK;CAAG"}
1
+ {"version":3,"file":"tid.d.ts","sourceRoot":"","sources":["../src/tid.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAK9B,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAQhC;AAED,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,SAAS,CAE7E;AAED,qBAAa,eAAgB,SAAQ,KAAK;CAAG"}
package/dist/tid.js CHANGED
@@ -5,17 +5,17 @@ exports.ensureValidTid = ensureValidTid;
5
5
  exports.isValidTid = isValidTid;
6
6
  const TID_LENGTH = 13;
7
7
  const TID_REGEX = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/;
8
- function ensureValidTid(tid) {
9
- if (tid.length !== TID_LENGTH) {
8
+ function ensureValidTid(input) {
9
+ if (input.length !== TID_LENGTH) {
10
10
  throw new InvalidTidError(`TID must be ${TID_LENGTH} characters`);
11
11
  }
12
12
  // simple regex to enforce most constraints via just regex and length.
13
- if (!TID_REGEX.test(tid)) {
13
+ if (!TID_REGEX.test(input)) {
14
14
  throw new InvalidTidError('TID syntax not valid (regex)');
15
15
  }
16
16
  }
17
- function isValidTid(tid) {
18
- return tid.length === TID_LENGTH && TID_REGEX.test(tid);
17
+ function isValidTid(input) {
18
+ return input.length === TID_LENGTH && TID_REGEX.test(input);
19
19
  }
20
20
  class InvalidTidError extends Error {
21
21
  }
package/dist/tid.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tid.js","sourceRoot":"","sources":["../src/tid.ts"],"names":[],"mappings":";;;AAKA,wCAQC;AAED,gCAEC;AAfD,MAAM,UAAU,GAAG,EAAE,CAAA;AACrB,MAAM,SAAS,GAAG,4DAA4D,CAAA;AAE9E,SAAgB,cAAc,CAAC,GAAW;IACxC,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC9B,MAAM,IAAI,eAAe,CAAC,eAAe,UAAU,aAAa,CAAC,CAAA;IACnE,CAAC;IACD,sEAAsE;IACtE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,eAAe,CAAC,8BAA8B,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC;AAED,SAAgB,UAAU,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACzD,CAAC;AAED,MAAa,eAAgB,SAAQ,KAAK;CAAG;AAA7C,0CAA6C","sourcesContent":["export type TidString = string\n\nconst TID_LENGTH = 13\nconst TID_REGEX = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/\n\nexport function ensureValidTid(tid: string): asserts tid is TidString {\n if (tid.length !== TID_LENGTH) {\n throw new InvalidTidError(`TID must be ${TID_LENGTH} characters`)\n }\n // simple regex to enforce most constraints via just regex and length.\n if (!TID_REGEX.test(tid)) {\n throw new InvalidTidError('TID syntax not valid (regex)')\n }\n}\n\nexport function isValidTid(tid: string): tid is TidString {\n return tid.length === TID_LENGTH && TID_REGEX.test(tid)\n}\n\nexport class InvalidTidError extends Error {}\n"]}
1
+ {"version":3,"file":"tid.js","sourceRoot":"","sources":["../src/tid.ts"],"names":[],"mappings":";;;AAKA,wCAUC;AAED,gCAEC;AAjBD,MAAM,UAAU,GAAG,EAAE,CAAA;AACrB,MAAM,SAAS,GAAG,4DAA4D,CAAA;AAE9E,SAAgB,cAAc,CAC5B,KAAQ;IAER,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,eAAe,CAAC,eAAe,UAAU,aAAa,CAAC,CAAA;IACnE,CAAC;IACD,sEAAsE;IACtE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,eAAe,CAAC,8BAA8B,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC;AAED,SAAgB,UAAU,CAAmB,KAAQ;IACnD,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC7D,CAAC;AAED,MAAa,eAAgB,SAAQ,KAAK;CAAG;AAA7C,0CAA6C","sourcesContent":["export type TidString = string\n\nconst TID_LENGTH = 13\nconst TID_REGEX = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/\n\nexport function ensureValidTid<I extends string>(\n input: I,\n): asserts input is I & TidString {\n if (input.length !== TID_LENGTH) {\n throw new InvalidTidError(`TID must be ${TID_LENGTH} characters`)\n }\n // simple regex to enforce most constraints via just regex and length.\n if (!TID_REGEX.test(input)) {\n throw new InvalidTidError('TID syntax not valid (regex)')\n }\n}\n\nexport function isValidTid<I extends string>(input: I): input is I & TidString {\n return input.length === TID_LENGTH && TID_REGEX.test(input)\n}\n\nexport class InvalidTidError extends Error {}\n"]}
package/dist/uri.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type UriString = `${string}:${string}`;
2
+ export declare function isValidUri<I extends string>(input: I): input is I & UriString;
3
+ //# sourceMappingURL=uri.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uri.d.ts","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAA;AAE7C,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,SAAS,CAE7E"}
package/dist/uri.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isValidUri = isValidUri;
4
+ function isValidUri(input) {
5
+ return /^\w+:(?:\/\/)?[^\s/][^\s]*$/.test(input);
6
+ }
7
+ //# sourceMappingURL=uri.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uri.js","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":";;AAEA,gCAEC;AAFD,SAAgB,UAAU,CAAmB,KAAQ;IACnD,OAAO,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAClD,CAAC","sourcesContent":["export type UriString = `${string}:${string}`\n\nexport function isValidUri<I extends string>(input: I): input is I & UriString {\n return /^\\w+:(?:\\/\\/)?[^\\s/][^\\s]*$/.test(input)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/syntax",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "description": "Validation for atproto identifiers and formats: DID, handle, NSID, AT URI, etc",
6
6
  "keywords": [
@@ -17,15 +17,18 @@
17
17
  },
18
18
  "main": "dist/index.js",
19
19
  "types": "dist/index.d.ts",
20
+ "dependencies": {
21
+ "tslib": "^2.8.1"
22
+ },
20
23
  "devDependencies": {
21
- "jest": "^28.1.2",
22
- "typescript": "^5.6.3"
24
+ "typescript": "^5.6.3",
25
+ "vitest": "^4.0.16"
23
26
  },
24
27
  "browser": {
25
28
  "dns/promises": false
26
29
  },
27
30
  "scripts": {
28
- "test": "jest",
31
+ "test": "vitest run",
29
32
  "build": "tsc --build tsconfig.build.json"
30
33
  }
31
34
  }
@@ -1,8 +1,9 @@
1
- import { DidString, ensureValidDidRegex } from './did.js'
1
+ import { DidString, ensureValidDidRegex, isValidDid } from './did.js'
2
2
  import {
3
3
  HandleString,
4
4
  InvalidHandleError,
5
5
  ensureValidHandleRegex,
6
+ isValidHandle,
6
7
  } from './handle.js'
7
8
 
8
9
  export type AtIdentifierString = DidString | HandleString
@@ -20,3 +21,13 @@ export function ensureValidAtIdentifier(
20
21
  throw new InvalidHandleError('Invalid DID or handle', { cause })
21
22
  }
22
23
  }
24
+
25
+ export function isValidAtIdentifier<I extends string>(
26
+ input: I,
27
+ ): input is I & AtIdentifierString {
28
+ if (input.startsWith('did:')) {
29
+ return isValidDid(input)
30
+ } else {
31
+ return isValidHandle(input)
32
+ }
33
+ }
package/src/aturi.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { AtIdentifierString, ensureValidAtIdentifier } from './at-identifier.js'
2
2
  import { AtUriString } from './aturi_validation.js'
3
- import { ensureValidNsid } from './nsid.js'
3
+ import { DidString, ensureValidDid } from './did.js'
4
+ import { NsidString, ensureValidNsid } from './nsid.js'
5
+ import { RecordKeyString, ensureValidRecordKey } from './recordkey.js'
4
6
 
5
7
  export * from './aturi_validation.js'
6
8
 
@@ -47,6 +49,12 @@ export class AtUri {
47
49
  return `at://${this.host}` as const
48
50
  }
49
51
 
52
+ get did(): DidString {
53
+ const { hostname } = this
54
+ ensureValidDid(hostname)
55
+ return hostname
56
+ }
57
+
50
58
  get hostname() {
51
59
  return this.host
52
60
  }
@@ -68,8 +76,18 @@ export class AtUri {
68
76
  return this.pathname.split('/').filter(Boolean)[0] || ''
69
77
  }
70
78
 
79
+ get collectionSafe(): NsidString {
80
+ const { collection } = this
81
+ ensureValidNsid(collection)
82
+ return collection
83
+ }
84
+
71
85
  set collection(v: string) {
72
86
  ensureValidNsid(v)
87
+ this.unsafelySetCollection(v)
88
+ }
89
+
90
+ unsafelySetCollection(v: string) {
73
91
  const parts = this.pathname.split('/').filter(Boolean)
74
92
  parts[0] = v
75
93
  this.pathname = parts.join('/')
@@ -79,7 +97,18 @@ export class AtUri {
79
97
  return this.pathname.split('/').filter(Boolean)[1] || ''
80
98
  }
81
99
 
100
+ get rkeySafe(): RecordKeyString {
101
+ const { rkey } = this
102
+ ensureValidRecordKey(rkey)
103
+ return rkey
104
+ }
105
+
82
106
  set rkey(v: string) {
107
+ ensureValidRecordKey(v)
108
+ this.unsafelySetRkey(v)
109
+ }
110
+
111
+ unsafelySetRkey(v: string) {
83
112
  const parts = this.pathname.split('/').filter(Boolean)
84
113
  parts[0] ||= 'undefined'
85
114
  parts[1] = v
@@ -22,7 +22,9 @@ export type AtUriString =
22
22
  // - rkey must have at least one char
23
23
  // - regardless of path component, a fragment can follow as "#" and then a JSON pointer (RFC-6901)
24
24
 
25
- export function ensureValidAtUri(input: string): asserts input is AtUriString {
25
+ export function ensureValidAtUri<I extends string>(
26
+ input: I,
27
+ ): asserts input is I & AtUriString {
26
28
  const fragmentIndex = input.indexOf('#')
27
29
  if (fragmentIndex !== -1) {
28
30
  if (input.charCodeAt(fragmentIndex + 1) !== 47) {
@@ -105,12 +107,14 @@ export function ensureValidAtUri(input: string): asserts input is AtUriString {
105
107
  }
106
108
  }
107
109
 
108
- export function ensureValidAtUriRegex(uri: string): asserts uri is AtUriString {
110
+ export function ensureValidAtUriRegex<I extends string>(
111
+ input: I,
112
+ ): asserts input is I & AtUriString {
109
113
  // simple regex to enforce most constraints via just regex and length.
110
114
  // hand wrote this regex based on above constraints. whew!
111
115
  const aturiRegex =
112
116
  /^at:\/\/(?<authority>[a-zA-Z0-9._:%-]+)(\/(?<collection>[a-zA-Z0-9-.]+)(\/(?<rkey>[a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(#(?<fragment>\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]/\\]*))?$/
113
- const rm = uri.match(aturiRegex)
117
+ const rm = input.match(aturiRegex)
114
118
  if (!rm || !rm.groups) {
115
119
  throw new Error("ATURI didn't validate via regex")
116
120
  }
@@ -130,7 +134,19 @@ export function ensureValidAtUriRegex(uri: string): asserts uri is AtUriString {
130
134
  throw new Error('ATURI collection path segment must be a valid NSID')
131
135
  }
132
136
 
133
- if (uri.length > 8 * 1024) {
137
+ if (input.length > 8 * 1024) {
134
138
  throw new Error('ATURI is far too long')
135
139
  }
136
140
  }
141
+
142
+ export function isValidAtUri<I extends string>(
143
+ input: I,
144
+ ): input is I & AtUriString {
145
+ try {
146
+ ensureValidAtUriRegex(input)
147
+ } catch {
148
+ return false
149
+ }
150
+
151
+ return true
152
+ }