@atproto/syntax 0.5.4 → 0.6.0-next.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atproto/syntax
2
2
 
3
+ ## 0.6.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#4929](https://github.com/bluesky-social/atproto/pull/4929) [`bb7491c`](https://github.com/bluesky-social/atproto/commit/bb7491c29e06181e1d2f8cf6eb454f9bb8ab961b) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Drop support for Node.js 18 and 20. Node.js 22 is now the minimum supported version. Docker images now use Node.js 24.
8
+
9
+ - [#4943](https://github.com/bluesky-social/atproto/pull/4943) [`07ae5d4`](https://github.com/bluesky-social/atproto/commit/07ae5d4452df51e045e0239da7a04cf0bc154028) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Convert to pure ESM. All packages now ship `"type": "module"` with ES module output and Node16 module resolution.
10
+
11
+ Node.js 22's `require()` compatibility layer can still load these packages in CommonJS code.
12
+
13
+ - [#4930](https://github.com/bluesky-social/atproto/pull/4930) [`042df15`](https://github.com/bluesky-social/atproto/commit/042df15087c0e62cd1e715fcbf58852fab875af9) Thanks [@devinivy](https://github.com/devinivy)! - Build with TypeScript 6.0. Emitted `.d.ts` files now use TypeScript 6's stricter `Uint8Array<ArrayBuffer>` typing in places where Web/Node APIs require buffer-backed (not shared-memory) byte arrays. Consumers compiling against these types on older TypeScript should see no runtime impact, but may need to widen or cast in spots that previously relied on `Uint8Array` defaulting to `<ArrayBufferLike>`.
14
+
15
+ Internal: tsconfig `moduleResolution: "node"` is silenced via `ignoreDeprecations: "6.0"` for now; the proper migration to `node16`/`bundler` resolution is deferred.
16
+
3
17
  ## 0.5.4
4
18
 
5
19
  ### Patch Changes
@@ -1,21 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isHandleIdentifier = isHandleIdentifier;
4
- exports.isDidIdentifier = isDidIdentifier;
5
- exports.assertAtIdentifierString = assertAtIdentifierString;
6
- exports.ensureValidAtIdentifier = assertAtIdentifierString;
7
- exports.asAtIdentifierString = asAtIdentifierString;
8
- exports.isAtIdentifierString = isAtIdentifierString;
9
- exports.isValidAtIdentifier = isAtIdentifierString;
10
- exports.ifAtIdentifierString = ifAtIdentifierString;
11
- const did_js_1 = require("./did.js");
12
- const handle_js_1 = require("./handle.js");
1
+ import { ensureValidDidRegex, isValidDid } from './did.js';
2
+ import { InvalidHandleError, ensureValidHandleRegex, isValidHandle, } from './handle.js';
13
3
  /**
14
4
  * Discriminates {@link HandleString} from a valid {@link AtIdentifierString}.
15
5
  *
16
6
  * @return `true` if the identifier is a handle, `false` otherwise
17
7
  */
18
- function isHandleIdentifier(id) {
8
+ export function isHandleIdentifier(id) {
19
9
  return !isDidIdentifier(id);
20
10
  }
21
11
  /**
@@ -23,7 +13,7 @@ function isHandleIdentifier(id) {
23
13
  *
24
14
  * @return `true` if the identifier is a DID, `false` otherwise
25
15
  */
26
- function isDidIdentifier(id) {
16
+ export function isDidIdentifier(id) {
27
17
  return id.startsWith('did:');
28
18
  }
29
19
  /**
@@ -33,20 +23,20 @@ function isDidIdentifier(id) {
33
23
  * @throws InvalidHandleError if the input string does not meet the atproto 'datetime' format requirements.
34
24
  * @see {@link AtIdentifierString}
35
25
  */
36
- function assertAtIdentifierString(input) {
26
+ export function assertAtIdentifierString(input) {
37
27
  try {
38
28
  if (!input || typeof input !== 'string') {
39
29
  throw new TypeError('Identifier must be a non-empty string');
40
30
  }
41
31
  else if (input.startsWith('did:')) {
42
- (0, did_js_1.ensureValidDidRegex)(input);
32
+ ensureValidDidRegex(input);
43
33
  }
44
34
  else {
45
- (0, handle_js_1.ensureValidHandleRegex)(input);
35
+ ensureValidHandleRegex(input);
46
36
  }
47
37
  }
48
38
  catch (cause) {
49
- throw new handle_js_1.InvalidHandleError('Invalid DID or handle', { cause });
39
+ throw new InvalidHandleError('Invalid DID or handle', { cause });
50
40
  }
51
41
  }
52
42
  /**
@@ -56,7 +46,7 @@ function assertAtIdentifierString(input) {
56
46
  * @throws InvalidHandleError if the input string does not meet the atproto 'at-identifier' format requirements.
57
47
  * @see {@link AtIdentifierString}
58
48
  */
59
- function asAtIdentifierString(input) {
49
+ export function asAtIdentifierString(input) {
60
50
  assertAtIdentifierString(input);
61
51
  return input;
62
52
  }
@@ -67,15 +57,15 @@ function asAtIdentifierString(input) {
67
57
  * @returns `true` if the value is a valid AT identifier
68
58
  * @see {@link AtIdentifierString}
69
59
  */
70
- function isAtIdentifierString(input) {
60
+ export function isAtIdentifierString(input) {
71
61
  if (!input || typeof input !== 'string') {
72
62
  return false;
73
63
  }
74
64
  else if (input.startsWith('did:')) {
75
- return (0, did_js_1.isValidDid)(input);
65
+ return isValidDid(input);
76
66
  }
77
67
  else {
78
- return (0, handle_js_1.isValidHandle)(input);
68
+ return isValidHandle(input);
79
69
  }
80
70
  }
81
71
  /**
@@ -84,7 +74,9 @@ function isAtIdentifierString(input) {
84
74
  *
85
75
  * @see {@link AtIdentifierString}
86
76
  */
87
- function ifAtIdentifierString(input) {
77
+ export function ifAtIdentifierString(input) {
88
78
  return isAtIdentifierString(input) ? input : undefined;
89
79
  }
80
+ // Legacy exports (should we deprecate these ?)
81
+ export { assertAtIdentifierString as ensureValidAtIdentifier, isAtIdentifierString as isValidAtIdentifier, };
90
82
  //# sourceMappingURL=at-identifier.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"at-identifier.js","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":";;AAoBA,gDAEC;AAOD,0CAEC;AASD,4DAcC;AA+C6B,2DAAuB;AAtCrD,oDAGC;AASD,oDAUC;AAiByB,mDAAmB;AAT7C,oDAIC;AAjGD,qCAAqE;AACrE,2CAKoB;AASpB;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,EAAsB;IACvD,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC,CAAA;AAC7B,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,EAAsB;IACpD,OAAO,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,wBAAwB,CACtC,KAAQ;IAER,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAA;QAC9D,CAAC;aAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,IAAA,4BAAmB,EAAC,KAAK,CAAC,CAAA;QAC5B,CAAC;aAAM,CAAC;YACN,IAAA,kCAAsB,EAAC,KAAK,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,8BAAkB,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IAClE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAAI,KAAQ;IAC9C,wBAAwB,CAAC,KAAK,CAAC,CAAA;IAC/B,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAClC,KAAQ;IAER,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAA;IACd,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,OAAO,IAAA,mBAAU,EAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;SAAM,CAAC;QACN,OAAO,IAAA,yBAAa,EAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAClC,KAAQ;IAER,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACxD,CAAC","sourcesContent":["import { DidString, ensureValidDidRegex, isValidDid } from './did.js'\nimport {\n HandleString,\n InvalidHandleError,\n ensureValidHandleRegex,\n isValidHandle,\n} from './handle.js'\n\n/**\n * An \"at-identifier\" string - either a {@link DidString} or a {@link HandleString}\n *\n * @example `\"did:plc:1234...\"`, `\"did:web:example.com\"` or `\"alice.bsky.social\"`\n */\nexport type AtIdentifierString = DidString | HandleString\n\n/**\n * Discriminates {@link HandleString} from a valid {@link AtIdentifierString}.\n *\n * @return `true` if the identifier is a handle, `false` otherwise\n */\nexport function isHandleIdentifier(id: AtIdentifierString): id is HandleString {\n return !isDidIdentifier(id)\n}\n\n/**\n * Discriminates {@link DidString} from a valid {@link AtIdentifierString}.\n *\n * @return `true` if the identifier is a DID, `false` otherwise\n */\nexport function isDidIdentifier(id: AtIdentifierString): id is DidString {\n return id.startsWith('did:')\n}\n\n/**\n * Validates that a string is a valid {@link AtIdentifierString} format string,\n * throwing an error if it is not.\n *\n * @throws InvalidHandleError if the input string does not meet the atproto 'datetime' format requirements.\n * @see {@link AtIdentifierString}\n */\nexport function assertAtIdentifierString<I>(\n input: I,\n): asserts input is I & AtIdentifierString {\n try {\n if (!input || typeof input !== 'string') {\n throw new TypeError('Identifier must be a non-empty string')\n } else if (input.startsWith('did:')) {\n ensureValidDidRegex(input)\n } else {\n ensureValidHandleRegex(input)\n }\n } catch (cause) {\n throw new InvalidHandleError('Invalid DID or handle', { cause })\n }\n}\n\n/**\n * Casts a string to a {@link AtIdentifierString} if it is a valid at-identifier\n * string, throwing an error if it is not.\n *\n * @throws InvalidHandleError if the input string does not meet the atproto 'at-identifier' format requirements.\n * @see {@link AtIdentifierString}\n */\nexport function asAtIdentifierString<I>(input: I): I & AtIdentifierString {\n assertAtIdentifierString(input)\n return input\n}\n\n/**\n * Type guard that checks if a value is a valid AT identifier (DID or handle).\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid AT identifier\n * @see {@link AtIdentifierString}\n */\nexport function isAtIdentifierString<I>(\n input: I,\n): input is I & AtIdentifierString {\n if (!input || typeof input !== 'string') {\n return false\n } else if (input.startsWith('did:')) {\n return isValidDid(input)\n } else {\n return isValidHandle(input)\n }\n}\n\n/**\n * Returns the input if it is a valid {@link AtIdentifierString} format string, or\n * `undefined` if it is not.\n *\n * @see {@link AtIdentifierString}\n */\nexport function ifAtIdentifierString<I>(\n input: I,\n): undefined | (I & AtIdentifierString) {\n return isAtIdentifierString(input) ? input : undefined\n}\n\n// Legacy exports (should we deprecate these ?)\nexport {\n assertAtIdentifierString as ensureValidAtIdentifier,\n isAtIdentifierString as isValidAtIdentifier,\n}\n"]}
1
+ {"version":3,"file":"at-identifier.js","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,mBAAmB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrE,OAAO,EAEL,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,GACd,MAAM,aAAa,CAAA;AASpB;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAsB;IACvD,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC,CAAA;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,EAAsB;IACpD,OAAO,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAQ;IAER,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAA;QAC9D,CAAC;aAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,mBAAmB,CAAC,KAAK,CAAC,CAAA;QAC5B,CAAC;aAAM,CAAC;YACN,sBAAsB,CAAC,KAAK,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IAClE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAI,KAAQ;IAC9C,wBAAwB,CAAC,KAAK,CAAC,CAAA;IAC/B,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAQ;IAER,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAA;IACd,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;SAAM,CAAC;QACN,OAAO,aAAa,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAQ;IAER,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACxD,CAAC;AAED,+CAA+C;AAC/C,OAAO,EACL,wBAAwB,IAAI,uBAAuB,EACnD,oBAAoB,IAAI,mBAAmB,GAC5C,CAAA","sourcesContent":["import { DidString, ensureValidDidRegex, isValidDid } from './did.js'\nimport {\n HandleString,\n InvalidHandleError,\n ensureValidHandleRegex,\n isValidHandle,\n} from './handle.js'\n\n/**\n * An \"at-identifier\" string - either a {@link DidString} or a {@link HandleString}\n *\n * @example `\"did:plc:1234...\"`, `\"did:web:example.com\"` or `\"alice.bsky.social\"`\n */\nexport type AtIdentifierString = DidString | HandleString\n\n/**\n * Discriminates {@link HandleString} from a valid {@link AtIdentifierString}.\n *\n * @return `true` if the identifier is a handle, `false` otherwise\n */\nexport function isHandleIdentifier(id: AtIdentifierString): id is HandleString {\n return !isDidIdentifier(id)\n}\n\n/**\n * Discriminates {@link DidString} from a valid {@link AtIdentifierString}.\n *\n * @return `true` if the identifier is a DID, `false` otherwise\n */\nexport function isDidIdentifier(id: AtIdentifierString): id is DidString {\n return id.startsWith('did:')\n}\n\n/**\n * Validates that a string is a valid {@link AtIdentifierString} format string,\n * throwing an error if it is not.\n *\n * @throws InvalidHandleError if the input string does not meet the atproto 'datetime' format requirements.\n * @see {@link AtIdentifierString}\n */\nexport function assertAtIdentifierString<I>(\n input: I,\n): asserts input is I & AtIdentifierString {\n try {\n if (!input || typeof input !== 'string') {\n throw new TypeError('Identifier must be a non-empty string')\n } else if (input.startsWith('did:')) {\n ensureValidDidRegex(input)\n } else {\n ensureValidHandleRegex(input)\n }\n } catch (cause) {\n throw new InvalidHandleError('Invalid DID or handle', { cause })\n }\n}\n\n/**\n * Casts a string to a {@link AtIdentifierString} if it is a valid at-identifier\n * string, throwing an error if it is not.\n *\n * @throws InvalidHandleError if the input string does not meet the atproto 'at-identifier' format requirements.\n * @see {@link AtIdentifierString}\n */\nexport function asAtIdentifierString<I>(input: I): I & AtIdentifierString {\n assertAtIdentifierString(input)\n return input\n}\n\n/**\n * Type guard that checks if a value is a valid AT identifier (DID or handle).\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid AT identifier\n * @see {@link AtIdentifierString}\n */\nexport function isAtIdentifierString<I>(\n input: I,\n): input is I & AtIdentifierString {\n if (!input || typeof input !== 'string') {\n return false\n } else if (input.startsWith('did:')) {\n return isValidDid(input)\n } else {\n return isValidHandle(input)\n }\n}\n\n/**\n * Returns the input if it is a valid {@link AtIdentifierString} format string, or\n * `undefined` if it is not.\n *\n * @see {@link AtIdentifierString}\n */\nexport function ifAtIdentifierString<I>(\n input: I,\n): undefined | (I & AtIdentifierString) {\n return isAtIdentifierString(input) ? input : undefined\n}\n\n// Legacy exports (should we deprecate these ?)\nexport {\n assertAtIdentifierString as ensureValidAtIdentifier,\n isAtIdentifierString as isValidAtIdentifier,\n}\n"]}
package/dist/aturi.js CHANGED
@@ -1,29 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AtUri = exports.ATP_URI_REGEX = void 0;
4
- const tslib_1 = require("tslib");
5
- const at_identifier_js_1 = require("./at-identifier.js");
6
- const did_js_1 = require("./did.js");
7
- const nsid_js_1 = require("./nsid.js");
8
- const recordkey_js_1 = require("./recordkey.js");
9
- tslib_1.__exportStar(require("./aturi_validation.js"), exports);
10
- exports.ATP_URI_REGEX =
1
+ import { ensureValidAtIdentifier, isDidIdentifier, } from './at-identifier.js';
2
+ import { InvalidDidError } from './did.js';
3
+ import { ensureValidNsid } from './nsid.js';
4
+ import { ensureValidRecordKey } from './recordkey.js';
5
+ export * from './aturi_validation.js';
6
+ export const ATP_URI_REGEX =
11
7
  // proto- --did-------------- --name---------------- --path---- --query-- --hash--
12
8
  /^(at:\/\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\/[^?#\s]*)?(\?[^#\s]+)?(#[^\s]+)?$/i;
13
9
  // --path----- --query-- --hash--
14
10
  const RELATIVE_REGEX = /^(\/[^?#\s]*)?(\?[^#\s]+)?(#[^\s]+)?$/i;
15
- class AtUri {
16
- hash;
17
- host;
18
- pathname;
19
- searchParams;
11
+ export class AtUri {
20
12
  constructor(uri, base) {
21
13
  const parsed = base !== undefined
22
14
  ? typeof base === 'string'
23
15
  ? Object.assign(parse(base), parseRelative(uri))
24
16
  : Object.assign({ host: base.host }, parseRelative(uri))
25
17
  : parse(uri);
26
- (0, at_identifier_js_1.ensureValidAtIdentifier)(parsed.host);
18
+ ensureValidAtIdentifier(parsed.host);
27
19
  this.hash = parsed.hash ?? '';
28
20
  this.host = parsed.host;
29
21
  this.pathname = parsed.pathname ?? '';
@@ -45,15 +37,15 @@ class AtUri {
45
37
  }
46
38
  get did() {
47
39
  const { host } = this;
48
- if ((0, at_identifier_js_1.isDidIdentifier)(host))
40
+ if (isDidIdentifier(host))
49
41
  return host;
50
- throw new did_js_1.InvalidDidError(`AtUri "${this}" does not have a DID hostname`);
42
+ throw new InvalidDidError(`AtUri "${this}" does not have a DID hostname`);
51
43
  }
52
44
  get hostname() {
53
45
  return this.host;
54
46
  }
55
47
  set hostname(v) {
56
- (0, at_identifier_js_1.ensureValidAtIdentifier)(v);
48
+ ensureValidAtIdentifier(v);
57
49
  this.host = v;
58
50
  }
59
51
  get search() {
@@ -67,11 +59,11 @@ class AtUri {
67
59
  }
68
60
  get collectionSafe() {
69
61
  const { collection } = this;
70
- (0, nsid_js_1.ensureValidNsid)(collection);
62
+ ensureValidNsid(collection);
71
63
  return collection;
72
64
  }
73
65
  set collection(v) {
74
- (0, nsid_js_1.ensureValidNsid)(v);
66
+ ensureValidNsid(v);
75
67
  this.unsafelySetCollection(v);
76
68
  }
77
69
  unsafelySetCollection(v) {
@@ -84,11 +76,11 @@ class AtUri {
84
76
  }
85
77
  get rkeySafe() {
86
78
  const { rkey } = this;
87
- (0, recordkey_js_1.ensureValidRecordKey)(rkey);
79
+ ensureValidRecordKey(rkey);
88
80
  return rkey;
89
81
  }
90
82
  set rkey(v) {
91
- (0, recordkey_js_1.ensureValidRecordKey)(v);
83
+ ensureValidRecordKey(v);
92
84
  this.unsafelySetRkey(v);
93
85
  }
94
86
  unsafelySetRkey(v) {
@@ -123,9 +115,8 @@ class AtUri {
123
115
  return `at://${this.host}${pathname}${qs}${fragment}`;
124
116
  }
125
117
  }
126
- exports.AtUri = AtUri;
127
118
  function parse(str) {
128
- const match = str.match(exports.ATP_URI_REGEX);
119
+ const match = str.match(ATP_URI_REGEX);
129
120
  if (!match) {
130
121
  throw new Error(`Invalid AT uri: ${str}`);
131
122
  }
package/dist/aturi.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"aturi.js","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AtIdentifierString,\n ensureValidAtIdentifier,\n isDidIdentifier,\n} from './at-identifier.js'\nimport { AtUriString } from './aturi_validation.js'\nimport { DidString, InvalidDidError } from './did.js'\nimport { NsidString, ensureValidNsid } from './nsid.js'\nimport { RecordKeyString, ensureValidRecordKey } from './recordkey.js'\n\nexport * from './aturi_validation.js'\n\n// Re-export types used in public interface\nexport type {\n AtIdentifierString,\n AtUriString,\n DidString,\n NsidString,\n RecordKeyString,\n}\n\nexport const ATP_URI_REGEX =\n // proto- --did-------------- --name---------------- --path---- --query-- --hash--\n /^(at:\\/\\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\\/[^?#\\s]*)?(\\?[^#\\s]+)?(#[^\\s]+)?$/i\n// --path----- --query-- --hash--\nconst RELATIVE_REGEX = /^(\\/[^?#\\s]*)?(\\?[^#\\s]+)?(#[^\\s]+)?$/i\n\nexport class AtUri {\n hash: string\n host: AtIdentifierString\n pathname: string\n searchParams: URLSearchParams\n\n constructor(uri: string, base?: string | AtUri) {\n const parsed =\n base !== undefined\n ? typeof base === 'string'\n ? Object.assign(parse(base), parseRelative(uri))\n : Object.assign({ host: base.host }, parseRelative(uri))\n : parse(uri)\n\n ensureValidAtIdentifier(parsed.host)\n\n this.hash = parsed.hash ?? ''\n this.host = parsed.host\n this.pathname = parsed.pathname ?? ''\n this.searchParams = parsed.searchParams\n }\n\n static make(handleOrDid: string, collection?: string, rkey?: string) {\n let str = handleOrDid\n if (collection) str += '/' + collection\n if (rkey) str += '/' + rkey\n return new AtUri(str)\n }\n\n get protocol() {\n return 'at:'\n }\n\n get origin() {\n return `at://${this.host}` as const\n }\n\n get did(): DidString {\n const { host } = this\n if (isDidIdentifier(host)) return host\n throw new InvalidDidError(`AtUri \"${this}\" does not have a DID hostname`)\n }\n\n get hostname(): AtIdentifierString {\n return this.host\n }\n\n set hostname(v: string) {\n ensureValidAtIdentifier(v)\n this.host = v\n }\n\n get search() {\n return this.searchParams.toString()\n }\n\n set search(v: string) {\n this.searchParams = new URLSearchParams(v)\n }\n\n get collection() {\n return this.pathname.split('/').filter(Boolean)[0] || ''\n }\n\n get collectionSafe(): NsidString {\n const { collection } = this\n ensureValidNsid(collection)\n return collection\n }\n\n set collection(v: string) {\n ensureValidNsid(v)\n this.unsafelySetCollection(v)\n }\n\n unsafelySetCollection(v: string) {\n const parts = this.pathname.split('/').filter(Boolean)\n parts[0] = v\n this.pathname = parts.join('/')\n }\n\n get rkey() {\n return this.pathname.split('/').filter(Boolean)[1] || ''\n }\n\n get rkeySafe(): RecordKeyString {\n const { rkey } = this\n ensureValidRecordKey(rkey)\n return rkey\n }\n\n set rkey(v: string) {\n ensureValidRecordKey(v)\n this.unsafelySetRkey(v)\n }\n\n unsafelySetRkey(v: string) {\n const parts = this.pathname.split('/').filter(Boolean)\n parts[0] ||= 'undefined'\n parts[1] = v\n this.pathname = parts.join('/')\n }\n\n get href() {\n return this.toString()\n }\n\n toString(): AtUriString {\n let pathname = this.pathname\n if (pathname && !pathname.startsWith('/')) {\n pathname = `/${pathname}`\n }\n while (pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1)\n }\n let qs = ''\n if (this.searchParams.size) {\n qs = `?${this.searchParams.toString()}`\n }\n // @NOTE We keep the hash as-is, even if it doesn't start with a '/'.\n let fragment = this.hash\n if (fragment === '#') {\n fragment = ''\n } else if (fragment && !fragment.startsWith('#')) {\n fragment = `#${fragment}`\n }\n return `at://${this.host}${pathname}${qs}${fragment}` as AtUriString\n }\n}\n\nfunction parse(str: string) {\n const match = str.match(ATP_URI_REGEX) as null | {\n 0: string\n 1: string | undefined // proto\n 2: string // host\n 3: string | undefined // path\n 4: string | undefined // query\n 5: string | undefined // hash\n }\n\n if (!match) {\n throw new Error(`Invalid AT uri: ${str}`)\n }\n\n return {\n host: match[2],\n hash: match[5],\n pathname: match[3],\n searchParams: new URLSearchParams(match[4]),\n }\n}\n\nfunction parseRelative(str: string) {\n const match = str.match(RELATIVE_REGEX) as null | {\n 0: string\n 1: string | undefined // path\n 2: string | undefined // query\n 3: string | undefined // hash\n }\n\n if (!match) {\n throw new Error(`Invalid path: ${str}`)\n }\n\n return {\n hash: match[3],\n pathname: match[1],\n searchParams: new URLSearchParams(match[2]),\n }\n}\n"]}
1
+ {"version":3,"file":"aturi.js","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AtIdentifierString,\n ensureValidAtIdentifier,\n isDidIdentifier,\n} from './at-identifier.js'\nimport { AtUriString } from './aturi_validation.js'\nimport { DidString, InvalidDidError } from './did.js'\nimport { NsidString, ensureValidNsid } from './nsid.js'\nimport { RecordKeyString, ensureValidRecordKey } from './recordkey.js'\n\nexport * from './aturi_validation.js'\n\n// Re-export types used in public interface\nexport type {\n AtIdentifierString,\n AtUriString,\n DidString,\n NsidString,\n RecordKeyString,\n}\n\nexport const ATP_URI_REGEX =\n // proto- --did-------------- --name---------------- --path---- --query-- --hash--\n /^(at:\\/\\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\\/[^?#\\s]*)?(\\?[^#\\s]+)?(#[^\\s]+)?$/i\n// --path----- --query-- --hash--\nconst RELATIVE_REGEX = /^(\\/[^?#\\s]*)?(\\?[^#\\s]+)?(#[^\\s]+)?$/i\n\nexport class AtUri {\n hash: string\n host: AtIdentifierString\n pathname: string\n searchParams: URLSearchParams\n\n constructor(uri: string, base?: string | AtUri) {\n const parsed =\n base !== undefined\n ? typeof base === 'string'\n ? Object.assign(parse(base), parseRelative(uri))\n : Object.assign({ host: base.host }, parseRelative(uri))\n : parse(uri)\n\n ensureValidAtIdentifier(parsed.host)\n\n this.hash = parsed.hash ?? ''\n this.host = parsed.host\n this.pathname = parsed.pathname ?? ''\n this.searchParams = parsed.searchParams\n }\n\n static make(handleOrDid: string, collection?: string, rkey?: string) {\n let str = handleOrDid\n if (collection) str += '/' + collection\n if (rkey) str += '/' + rkey\n return new AtUri(str)\n }\n\n get protocol() {\n return 'at:'\n }\n\n get origin() {\n return `at://${this.host}` as const\n }\n\n get did(): DidString {\n const { host } = this\n if (isDidIdentifier(host)) return host\n throw new InvalidDidError(`AtUri \"${this}\" does not have a DID hostname`)\n }\n\n get hostname(): AtIdentifierString {\n return this.host\n }\n\n set hostname(v: string) {\n ensureValidAtIdentifier(v)\n this.host = v\n }\n\n get search() {\n return this.searchParams.toString()\n }\n\n set search(v: string) {\n this.searchParams = new URLSearchParams(v)\n }\n\n get collection() {\n return this.pathname.split('/').filter(Boolean)[0] || ''\n }\n\n get collectionSafe(): NsidString {\n const { collection } = this\n ensureValidNsid(collection)\n return collection\n }\n\n set collection(v: string) {\n ensureValidNsid(v)\n this.unsafelySetCollection(v)\n }\n\n unsafelySetCollection(v: string) {\n const parts = this.pathname.split('/').filter(Boolean)\n parts[0] = v\n this.pathname = parts.join('/')\n }\n\n get rkey() {\n return this.pathname.split('/').filter(Boolean)[1] || ''\n }\n\n get rkeySafe(): RecordKeyString {\n const { rkey } = this\n ensureValidRecordKey(rkey)\n return rkey\n }\n\n set rkey(v: string) {\n ensureValidRecordKey(v)\n this.unsafelySetRkey(v)\n }\n\n unsafelySetRkey(v: string) {\n const parts = this.pathname.split('/').filter(Boolean)\n parts[0] ||= 'undefined'\n parts[1] = v\n this.pathname = parts.join('/')\n }\n\n get href() {\n return this.toString()\n }\n\n toString(): AtUriString {\n let pathname = this.pathname\n if (pathname && !pathname.startsWith('/')) {\n pathname = `/${pathname}`\n }\n while (pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1)\n }\n let qs = ''\n if (this.searchParams.size) {\n qs = `?${this.searchParams.toString()}`\n }\n // @NOTE We keep the hash as-is, even if it doesn't start with a '/'.\n let fragment = this.hash\n if (fragment === '#') {\n fragment = ''\n } else if (fragment && !fragment.startsWith('#')) {\n fragment = `#${fragment}`\n }\n return `at://${this.host}${pathname}${qs}${fragment}` as AtUriString\n }\n}\n\nfunction parse(str: string) {\n const match = str.match(ATP_URI_REGEX) as null | {\n 0: string\n 1: string | undefined // proto\n 2: string // host\n 3: string | undefined // path\n 4: string | undefined // query\n 5: string | undefined // hash\n }\n\n if (!match) {\n throw new Error(`Invalid AT uri: ${str}`)\n }\n\n return {\n host: match[2],\n hash: match[5],\n pathname: match[3],\n searchParams: new URLSearchParams(match[4]),\n }\n}\n\nfunction parseRelative(str: string) {\n const match = str.match(RELATIVE_REGEX) as null | {\n 0: string\n 1: string | undefined // path\n 2: string | undefined // query\n 3: string | undefined // hash\n }\n\n if (!match) {\n throw new Error(`Invalid path: ${str}`)\n }\n\n return {\n hash: match[3],\n pathname: match[1],\n searchParams: new URLSearchParams(match[2]),\n }\n}\n"]}
@@ -1,24 +1,13 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InvalidAtUriError = void 0;
4
- exports.isAtUriString = isAtUriString;
5
- exports.ifAtUriString = ifAtUriString;
6
- exports.asAtUriString = asAtUriString;
7
- exports.assertAtUriString = assertAtUriString;
8
- exports.ensureValidAtUri = ensureValidAtUri;
9
- exports.ensureValidAtUriRegex = ensureValidAtUriRegex;
10
- exports.isValidAtUri = isValidAtUri;
11
- exports.parseAtUriString = parseAtUriString;
12
- const at_identifier_js_1 = require("./at-identifier.js");
13
- const result_js_1 = require("./lib/result.js");
14
- const nsid_js_1 = require("./nsid.js");
15
- const recordkey_js_1 = require("./recordkey.js");
1
+ import { isAtIdentifierString } from './at-identifier.js';
2
+ import { failure, success } from './lib/result.js';
3
+ import { isValidNsid } from './nsid.js';
4
+ import { isValidRecordKey } from './recordkey.js';
16
5
  /**
17
6
  * Type guard that checks if a value is a valid {@link AtUriString}
18
7
  *
19
8
  * @see {@link AtUriString}
20
9
  */
21
- function isAtUriString(input, options) {
10
+ export function isAtUriString(input, options) {
22
11
  return parseAtUriString(input, options).success;
23
12
  }
24
13
  /**
@@ -27,7 +16,7 @@ function isAtUriString(input, options) {
27
16
  *
28
17
  * @see {@link AtUriString}
29
18
  */
30
- function ifAtUriString(input, options) {
19
+ export function ifAtUriString(input, options) {
31
20
  return isAtUriString(input, options) ? input : undefined;
32
21
  }
33
22
  /**
@@ -37,7 +26,7 @@ function ifAtUriString(input, options) {
37
26
  * @throws InvalidAtUriError if the input string does not meet the atproto AT URI format requirements.
38
27
  * @see {@link AtUriString}
39
28
  */
40
- function asAtUriString(input, options) {
29
+ export function asAtUriString(input, options) {
41
30
  assertAtUriString(input, options);
42
31
  return input;
43
32
  }
@@ -47,7 +36,7 @@ function asAtUriString(input, options) {
47
36
  *
48
37
  * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}
49
38
  */
50
- function assertAtUriString(input, options) {
39
+ export function assertAtUriString(input, options) {
51
40
  // Optimistically use faster isAtUriString(), throwing a detailed error only
52
41
  // in case of failure. This check, and the fact that the code after it always
53
42
  // throws, also ensures that isAtUriString() and assertAtUriString()'s
@@ -64,7 +53,7 @@ function assertAtUriString(input, options) {
64
53
  * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}
65
54
  * @deprecated use {@link assertAtUriString} with `{ strict: false }` option instead
66
55
  */
67
- function ensureValidAtUri(input) {
56
+ export function ensureValidAtUri(input) {
68
57
  assertAtUriString(input, { strict: false, detailed: true });
69
58
  }
70
59
  /**
@@ -74,7 +63,7 @@ function ensureValidAtUri(input) {
74
63
  * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}
75
64
  * @deprecated use {@link assertAtUriString} with `{ strict: false }` option instead
76
65
  */
77
- function ensureValidAtUriRegex(input) {
66
+ export function ensureValidAtUriRegex(input) {
78
67
  assertAtUriString(input, { strict: false, detailed: false });
79
68
  }
80
69
  /**
@@ -85,12 +74,11 @@ function ensureValidAtUriRegex(input) {
85
74
  *
86
75
  * @deprecated use {@link isAtUriString} with `{ strict: false }` option instead
87
76
  */
88
- function isValidAtUri(input) {
77
+ export function isValidAtUri(input) {
89
78
  return isAtUriString(input, { strict: false });
90
79
  }
91
- class InvalidAtUriError extends Error {
80
+ export class InvalidAtUriError extends Error {
92
81
  }
93
- exports.InvalidAtUriError = InvalidAtUriError;
94
82
  const INVALID_CHAR_REGEXP = /[^a-zA-Z0-9._~:@!$&'()*+,;=%/\\[\]#?-]/;
95
83
  const AT_URI_REGEXP = /^(?<uri>at:\/\/(?<authority>[^/?#\s]+)(?:\/(?<collection>[^/?#\s]+)(?:\/(?<rkey>[^/?#\s]+))?)?(?<trailingSlash>\/)?)(?:\?(?<query>[^#\s]*))?(?:#(?<hash>[^\s]*))?$/;
96
84
  /**
@@ -98,16 +86,16 @@ const AT_URI_REGEXP = /^(?<uri>at:\/\/(?<authority>[^/?#\s]+)(?:\/(?<collection>
98
86
  * returns a failure with a detailed error message if the string is not a valid
99
87
  * {@link AtUriString}.
100
88
  */
101
- function parseAtUriString(input, options) {
89
+ export function parseAtUriString(input, options) {
102
90
  if (typeof input !== 'string') {
103
- return (0, result_js_1.failure)('ATURI must be a string');
91
+ return failure('ATURI must be a string');
104
92
  }
105
93
  if (input.length > 8192) {
106
- return (0, result_js_1.failure)('ATURI exceeds maximum length');
94
+ return failure('ATURI exceeds maximum length');
107
95
  }
108
96
  const invalidChar = input.match(INVALID_CHAR_REGEXP);
109
97
  if (invalidChar) {
110
- return (0, result_js_1.failure)('Disallowed characters in ATURI (ASCII)');
98
+ return failure('Disallowed characters in ATURI (ASCII)');
111
99
  }
112
100
  const match = input.match(AT_URI_REGEXP);
113
101
  const groups = match?.groups;
@@ -117,13 +105,13 @@ function parseAtUriString(input, options) {
117
105
  // a generic error.
118
106
  if (options?.detailed) {
119
107
  if (!input.startsWith('at://')) {
120
- return (0, result_js_1.failure)('ATURI must start with "at://"');
108
+ return failure('ATURI must start with "at://"');
121
109
  }
122
110
  if (input.includes(' ')) {
123
- return (0, result_js_1.failure)('ATURI can not contain spaces');
111
+ return failure('ATURI can not contain spaces');
124
112
  }
125
113
  if (input.includes('//', 5)) {
126
- return (0, result_js_1.failure)('ATURI can not have empty path segments');
114
+ return failure('ATURI can not have empty path segments');
127
115
  }
128
116
  const pathStart = input.indexOf('/', 5); // after "at://"
129
117
  if (pathStart !== -1) {
@@ -131,11 +119,11 @@ function parseAtUriString(input, options) {
131
119
  const pathEnd = fragmentIndex !== -1 ? fragmentIndex : input.length;
132
120
  const secondSlash = input.indexOf('/', pathStart + 1);
133
121
  if (secondSlash !== -1 && secondSlash !== pathEnd - 1) {
134
- return (0, result_js_1.failure)('ATURI can not have more than two path segments');
122
+ return failure('ATURI can not have more than two path segments');
135
123
  }
136
124
  }
137
125
  }
138
- return (0, result_js_1.failure)('ATURI does not match expected format');
126
+ return failure('ATURI does not match expected format');
139
127
  }
140
128
  // @NOTE Percent-encoding is allowed by the AT URI specification, but any
141
129
  // percent-encoded characters appearing in the collection NSID or record key
@@ -143,11 +131,11 @@ function parseAtUriString(input, options) {
143
131
  // validators. Since these values are defined to be plain ASCII identifiers,
144
132
  // this legacy behavior is beneficial: it ensures that normalized
145
133
  // (non-percent-encoded) values are always used, as prescribed by the spec.
146
- if (!(0, at_identifier_js_1.isAtIdentifierString)(groups.authority)) {
147
- return (0, result_js_1.failure)('ATURI has invalid authority');
134
+ if (!isAtIdentifierString(groups.authority)) {
135
+ return failure('ATURI has invalid authority');
148
136
  }
149
- if (groups.collection != null && !(0, nsid_js_1.isValidNsid)(groups.collection)) {
150
- return (0, result_js_1.failure)('ATURI has invalid collection');
137
+ if (groups.collection != null && !isValidNsid(groups.collection)) {
138
+ return failure('ATURI has invalid collection');
151
139
  }
152
140
  if (groups.hash != null) {
153
141
  const result = parseJsonPointer(groups.hash, options);
@@ -155,21 +143,21 @@ function parseAtUriString(input, options) {
155
143
  groups.hash = result.value;
156
144
  }
157
145
  else {
158
- return (0, result_js_1.failure)(`ATURI has invalid fragment (${result.message})`);
146
+ return failure(`ATURI has invalid fragment (${result.message})`);
159
147
  }
160
148
  }
161
149
  if (options?.strict !== false) {
162
150
  if (groups.trailingSlash != null) {
163
- return (0, result_js_1.failure)('ATURI can not have a trailing slash');
151
+ return failure('ATURI can not have a trailing slash');
164
152
  }
165
153
  if (groups.query != null) {
166
- return (0, result_js_1.failure)('ATURI query part is not allowed');
154
+ return failure('ATURI query part is not allowed');
167
155
  }
168
- if (groups.rkey != null && !(0, recordkey_js_1.isValidRecordKey)(groups.rkey)) {
169
- return (0, result_js_1.failure)('ATURI has invalid record key');
156
+ if (groups.rkey != null && !isValidRecordKey(groups.rkey)) {
157
+ return failure('ATURI has invalid record key');
170
158
  }
171
159
  }
172
- return (0, result_js_1.success)(groups);
160
+ return success(groups);
173
161
  }
174
162
  const BASIC_JSON_POINTER_REGEXP = /^\/[a-zA-Z0-9._~:@!$&')(*+,;=%[\]/-]*$/;
175
163
  /**
@@ -179,22 +167,22 @@ const BASIC_JSON_POINTER_REGEXP = /^\/[a-zA-Z0-9._~:@!$&')(*+,;=%[\]/-]*$/;
179
167
  */
180
168
  function parseJsonPointer(value, options) {
181
169
  if (!BASIC_JSON_POINTER_REGEXP.test(value)) {
182
- return (0, result_js_1.failure)('Invalid JSON pointer');
170
+ return failure('Invalid JSON pointer');
183
171
  }
184
172
  const result = parsePercentEncoding(value);
185
173
  // In non-strict mode, we allow invalid percent-encoding in the fragment
186
174
  if (!result.success && options?.strict === false) {
187
- return (0, result_js_1.success)(value);
175
+ return success(value);
188
176
  }
189
177
  return result;
190
178
  }
191
179
  function parsePercentEncoding(value) {
192
180
  try {
193
- return (0, result_js_1.success)(decodeURIComponent(value));
181
+ return success(decodeURIComponent(value));
194
182
  }
195
183
  catch {
196
184
  // decodeURIComponent throws if the percent-encoding is invalid (e.g. "%FF")
197
- return (0, result_js_1.failure)('Invalid percent-encoding');
185
+ return failure('Invalid percent-encoding');
198
186
  }
199
187
  }
200
188
  //# sourceMappingURL=aturi_validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"aturi_validation.js","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtIdentifierString, isAtIdentifierString } from './at-identifier.js'\nimport { Result, failure, success } from './lib/result.js'\nimport { NsidString, isValidNsid } from './nsid.js'\nimport { isValidRecordKey } from './recordkey.js'\n\nexport type AtUriStringBase =\n | `at://${AtIdentifierString}`\n | `at://${AtIdentifierString}/${NsidString}`\n | `at://${AtIdentifierString}/${NsidString}/${string}`\n\nexport type AtUriStringFragment = `#/${string}`\n\n/**\n * A URI string as used to point at resources in the AT Protocol\n *\n * The full, general structure of an AT URI is:\n *\n * ```bnf\n * AT-URI = \"at://\" AUTHORITY [ PATH ] [ \"?\" QUERY ] [ \"#\" FRAGMENT ]\n * ```\n *\n * The authority part of the URI can be either a handle or a DID, indicating the\n * identity associated with the repository. In current atproto Lexicon use, the\n * query and fragment parts are not yet supported, and only a fixed pattern of\n * paths are allowed:\n *\n * ```bnf\n * AT-URI = \"at://\" AUTHORITY [ \"/\" COLLECTION [ \"/\" RKEY ] ]\n *\n * AUTHORITY = HANDLE | DID\n * COLLECTION = NSID\n * RKEY = RECORD-KEY\n * ```\n *\n * The authority section is required, and should be normalized.\n *\n * AT URI strings must respect the following syntax (as prescribed by the AT\n * protocol specification):\n *\n * - The overall URI is restricted to a subset of ASCII characters\n * - For reference below, the set of unreserved characters, as defined in [RFC-3986](https://www.rfc-editor.org/rfc/rfc3986), includes alphanumeric (`A-Za-z0-9`), period, hyphen, underscore, and tilde (`.-_~`)\n * - Maximum overall length is 8 kilobytes (which may be shortened in the future)\n * - Hex-encoding of characters is permitted (but in practice not necessary and should be avoided to keep the URI normalized and human-readable)\n * - The URI scheme is `at`, and an authority part preceded with double slashes is always required. AT URIs always start with `at://`.\n * - An authority section is required and must be non-empty. the authority can be either an atproto Handle, or a DID meeting the restrictions for use with atproto. The authority part can *not* be interpreted as a host:port pair, because of the use of colon characters (`:`) in DIDs. Colons and unreserved characters should not be escaped in DIDs, but other reserved characters (including `#`, `/`, `$`, `&`, `@`) must be escaped.\n * - Note that none of the current \"blessed\" DID methods for atproto allow these characters in DID identifiers\n * - An optional path section may follow the authority. The path may contain multiple segments separated by a single slash (`/`). Generic URI path normalization rules may be used.\n * - An optional query part is allowed, following generic URI syntax restrictions\n * - An optional fragment part is allowed, using JSON Path syntax\n *\n * @example \"at://did:plc:ewvi7nxzyoun6zhxrhs64oiz/app.bsky.actor.profile/self\"\n *\n * @see {@link https://atproto.com/specs/at-uri-scheme AT protocol - AT URI Scheme}\n */\nexport type AtUriString =\n | AtUriStringBase\n | `${AtUriStringBase}${AtUriStringFragment}`\n\n/**\n * Type guard that checks if a value is a valid {@link AtUriString}\n *\n * @see {@link AtUriString}\n */\nexport function isAtUriString<I>(\n input: I,\n options?: Omit<ParseAtUriStringOptions, 'detailed'>,\n): input is I & AtUriString {\n return parseAtUriString(input, options).success\n}\n\n/**\n * Returns the input if it is a valid {@link AtUriString} format string, or\n * `undefined` if it is not.\n *\n * @see {@link AtUriString}\n */\nexport function ifAtUriString<I>(\n input: I,\n options?: Omit<ParseAtUriStringOptions, 'detailed'>,\n): undefined | (I & AtUriString) {\n return isAtUriString(input, options) ? input : undefined\n}\n\n/**\n * Casts a string to an {@link AtUriString} if it is a valid AT URI format\n * string, throwing an error if it is not.\n *\n * @throws InvalidAtUriError if the input string does not meet the atproto AT URI format requirements.\n * @see {@link AtUriString}\n */\nexport function asAtUriString<I>(\n input: I,\n options?: ParseAtUriStringOptions,\n): I & AtUriString {\n assertAtUriString(input, options)\n return input\n}\n\n/**\n * Assert the validity of an {@link AtUriString}, throwing an error if the\n * {@link input} is not a valid AT URI.\n *\n * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}\n */\nexport function assertAtUriString<I>(\n input: I,\n options?: ParseAtUriStringOptions,\n): asserts input is I & AtUriString {\n // Optimistically use faster isAtUriString(), throwing a detailed error only\n // in case of failure. This check, and the fact that the code after it always\n // throws, also ensures that isAtUriString() and assertAtUriString()'s\n // behavior are always consistent.\n const result = parseAtUriString(input, options)\n if (!result.success) {\n throw new InvalidAtUriError(result.message)\n }\n}\n\n/**\n * Assert the **non-strict** validity of an {@link AtUriString}, throwing a\n * detailed error if the {@link input} is not a valid AT URI.\n *\n * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}\n * @deprecated use {@link assertAtUriString} with `{ strict: false }` option instead\n */\nexport function ensureValidAtUri<I>(\n input: I,\n): asserts input is I & AtUriString {\n assertAtUriString(input, { strict: false, detailed: true })\n}\n\n/**\n * Assert the (non-strict!) validity of an {@link AtUriString}, throwing an\n * error if the {@link input} is not a valid AT URI.\n *\n * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}\n * @deprecated use {@link assertAtUriString} with `{ strict: false }` option instead\n */\nexport function ensureValidAtUriRegex<I>(\n input: I,\n): asserts input is I & AtUriString {\n assertAtUriString(input, { strict: false, detailed: false })\n}\n\n/**\n * Type guard that checks if a value is a valid {@link AtUriString} format\n * string, without enforcing strict record key validation. This is useful for\n * cases where you want to allow a wider range of valid ATURIs, such as when\n * validating user input or when the record key is not relevant.\n *\n * @deprecated use {@link isAtUriString} with `{ strict: false }` option instead\n */\nexport function isValidAtUri<I>(input: I): input is I & AtUriString {\n return isAtUriString(input, { strict: false })\n}\n\nexport class InvalidAtUriError extends Error {}\n\nexport type ParseAtUriStringOptions = {\n /**\n * If true, the parser will enforce that the record key (rkey) part of the URI\n * is a valid record key (validated by {@link isValidRecordKey}). If false,\n * any non-empty string of allowed chars will be accepted as a record key.\n *\n * @default true\n */\n strict?: boolean\n\n /**\n * If true, the parser will return detailed error messages for why a string is\n * not a valid AT URI. This option has no effect on the behavior of\n * {@link isAtUriString}, which will always return false for invalid strings\n * regardless of this option.\n *\n * @default false\n */\n detailed?: boolean\n}\n\nexport type AtUriParts = {\n authority: AtIdentifierString\n query?: string\n hash?: string\n} & (\n | { collection?: NsidString; rkey?: undefined }\n | { collection: NsidString; rkey?: string }\n)\n\nconst INVALID_CHAR_REGEXP = /[^a-zA-Z0-9._~:@!$&'()*+,;=%/\\\\[\\]#?-]/\nconst AT_URI_REGEXP =\n /^(?<uri>at:\\/\\/(?<authority>[^/?#\\s]+)(?:\\/(?<collection>[^/?#\\s]+)(?:\\/(?<rkey>[^/?#\\s]+))?)?(?<trailingSlash>\\/)?)(?:\\?(?<query>[^#\\s]*))?(?:#(?<hash>[^\\s]*))?$/\n\n/**\n * Parses a valid {@link AtUriString} into a {@link AtUriParts} object, or\n * returns a failure with a detailed error message if the string is not a valid\n * {@link AtUriString}.\n */\nexport function parseAtUriString(\n input: unknown,\n options?: ParseAtUriStringOptions,\n): Result<AtUriParts> {\n if (typeof input !== 'string') {\n return failure('ATURI must be a string')\n }\n\n if (input.length > 8192) {\n return failure('ATURI exceeds maximum length')\n }\n\n const invalidChar = input.match(INVALID_CHAR_REGEXP)\n if (invalidChar) {\n return failure('Disallowed characters in ATURI (ASCII)')\n }\n\n const match = input.match(AT_URI_REGEXP)\n const groups = match?.groups\n if (!groups) {\n // Regex validation failed, but we don't know exactly why. Provide more\n // detailed error messages if the \"detailed\" option is set, falling back to\n // a generic error.\n if (options?.detailed) {\n if (!input.startsWith('at://')) {\n return failure('ATURI must start with \"at://\"')\n }\n\n if (input.includes(' ')) {\n return failure('ATURI can not contain spaces')\n }\n\n if (input.includes('//', 5)) {\n return failure('ATURI can not have empty path segments')\n }\n\n const pathStart = input.indexOf('/', 5) // after \"at://\"\n if (pathStart !== -1) {\n const fragmentIndex = input.indexOf('#')\n const pathEnd = fragmentIndex !== -1 ? fragmentIndex : input.length\n const secondSlash = input.indexOf('/', pathStart + 1)\n if (secondSlash !== -1 && secondSlash !== pathEnd - 1) {\n return failure('ATURI can not have more than two path segments')\n }\n }\n }\n\n return failure('ATURI does not match expected format')\n }\n\n // @NOTE Percent-encoding is allowed by the AT URI specification, but any\n // percent-encoded characters appearing in the collection NSID or record key\n // will effectively be rejected by the isValidNsid and isValidRecordKey\n // validators. Since these values are defined to be plain ASCII identifiers,\n // this legacy behavior is beneficial: it ensures that normalized\n // (non-percent-encoded) values are always used, as prescribed by the spec.\n\n if (!isAtIdentifierString(groups.authority)) {\n return failure('ATURI has invalid authority')\n }\n\n if (groups.collection != null && !isValidNsid(groups.collection)) {\n return failure('ATURI has invalid collection')\n }\n\n if (groups.hash != null) {\n const result = parseJsonPointer(groups.hash, options)\n if (result.success) {\n groups.hash = result.value\n } else {\n return failure(`ATURI has invalid fragment (${result.message})`)\n }\n }\n\n if (options?.strict !== false) {\n if (groups.trailingSlash != null) {\n return failure('ATURI can not have a trailing slash')\n }\n\n if (groups.query != null) {\n return failure('ATURI query part is not allowed')\n }\n\n if (groups.rkey != null && !isValidRecordKey(groups.rkey)) {\n return failure('ATURI has invalid record key')\n }\n }\n\n return success(groups as AtUriParts)\n}\n\nconst BASIC_JSON_POINTER_REGEXP = /^\\/[a-zA-Z0-9._~:@!$&')(*+,;=%[\\]/-]*$/\n\n/**\n * Checks if a string is a valid JSON pointer (RFC-6901) with the allowed chars\n * for ATURI fragments. This is a very loose validation that only checks the\n * basic syntax and charset.\n */\nfunction parseJsonPointer(\n value: string,\n options?: { strict?: boolean },\n): Result<string> {\n if (!BASIC_JSON_POINTER_REGEXP.test(value)) {\n return failure('Invalid JSON pointer')\n }\n\n const result = parsePercentEncoding(value)\n\n // In non-strict mode, we allow invalid percent-encoding in the fragment\n if (!result.success && options?.strict === false) {\n return success(value)\n }\n\n return result\n}\n\nfunction parsePercentEncoding(value: string): Result<string> {\n try {\n return success(decodeURIComponent(value))\n } catch {\n // decodeURIComponent throws if the percent-encoding is invalid (e.g. \"%FF\")\n return failure('Invalid percent-encoding')\n }\n}\n"]}
1
+ {"version":3,"file":"aturi_validation.js","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,EAAU,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAc,WAAW,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAuDjD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAQ,EACR,OAAmD;IAEnD,OAAO,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,OAAO,CAAA;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAQ,EACR,OAAmD;IAEnD,OAAO,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAQ,EACR,OAAiC;IAEjC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACjC,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAQ,EACR,OAAiC;IAEjC,4EAA4E;IAC5E,6EAA6E;IAC7E,sEAAsE;IACtE,kCAAkC;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAQ;IAER,iBAAiB,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAQ;IAER,iBAAiB,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAI,KAAQ;IACtC,OAAO,aAAa,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAgC/C,MAAM,mBAAmB,GAAG,wCAAwC,CAAA;AACpE,MAAM,aAAa,GACjB,oKAAoK,CAAA;AAEtK;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAc,EACd,OAAiC;IAEjC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,wBAAwB,CAAC,CAAA;IAC1C,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,8BAA8B,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;IACpD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC,wCAAwC,CAAC,CAAA;IAC1D,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;IACxC,MAAM,MAAM,GAAG,KAAK,EAAE,MAAM,CAAA;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,uEAAuE;QACvE,2EAA2E;QAC3E,mBAAmB;QACnB,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/B,OAAO,OAAO,CAAC,+BAA+B,CAAC,CAAA;YACjD,CAAC;YAED,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,OAAO,OAAO,CAAC,8BAA8B,CAAC,CAAA;YAChD,CAAC;YAED,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBAC5B,OAAO,OAAO,CAAC,wCAAwC,CAAC,CAAA;YAC1D,CAAC;YAED,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA,CAAC,gBAAgB;YACxD,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;gBACrB,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;gBACxC,MAAM,OAAO,GAAG,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAA;gBACnE,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,CAAC,CAAC,CAAA;gBACrD,IAAI,WAAW,KAAK,CAAC,CAAC,IAAI,WAAW,KAAK,OAAO,GAAG,CAAC,EAAE,CAAC;oBACtD,OAAO,OAAO,CAAC,gDAAgD,CAAC,CAAA;gBAClE,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC,sCAAsC,CAAC,CAAA;IACxD,CAAC;IAED,yEAAyE;IACzE,4EAA4E;IAC5E,uEAAuE;IACvE,4EAA4E;IAC5E,iEAAiE;IACjE,2EAA2E;IAE3E,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5C,OAAO,OAAO,CAAC,6BAA6B,CAAC,CAAA;IAC/C,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACjE,OAAO,OAAO,CAAC,8BAA8B,CAAC,CAAA;IAChD,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACrD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAA;QAC5B,CAAC;aAAM,CAAC;YACN,OAAO,OAAO,CAAC,+BAA+B,MAAM,CAAC,OAAO,GAAG,CAAC,CAAA;QAClE,CAAC;IACH,CAAC;IAED,IAAI,OAAO,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YACjC,OAAO,OAAO,CAAC,qCAAqC,CAAC,CAAA;QACvD,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,iCAAiC,CAAC,CAAA;QACnD,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,OAAO,OAAO,CAAC,8BAA8B,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,MAAoB,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,yBAAyB,GAAG,wCAAwC,CAAA;AAE1E;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,KAAa,EACb,OAA8B;IAE9B,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,OAAO,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;IAE1C,wEAAwE;IACxE,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;QACjD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;QAC5E,OAAO,OAAO,CAAC,0BAA0B,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC","sourcesContent":["import { AtIdentifierString, isAtIdentifierString } from './at-identifier.js'\nimport { Result, failure, success } from './lib/result.js'\nimport { NsidString, isValidNsid } from './nsid.js'\nimport { isValidRecordKey } from './recordkey.js'\n\nexport type AtUriStringBase =\n | `at://${AtIdentifierString}`\n | `at://${AtIdentifierString}/${NsidString}`\n | `at://${AtIdentifierString}/${NsidString}/${string}`\n\nexport type AtUriStringFragment = `#/${string}`\n\n/**\n * A URI string as used to point at resources in the AT Protocol\n *\n * The full, general structure of an AT URI is:\n *\n * ```bnf\n * AT-URI = \"at://\" AUTHORITY [ PATH ] [ \"?\" QUERY ] [ \"#\" FRAGMENT ]\n * ```\n *\n * The authority part of the URI can be either a handle or a DID, indicating the\n * identity associated with the repository. In current atproto Lexicon use, the\n * query and fragment parts are not yet supported, and only a fixed pattern of\n * paths are allowed:\n *\n * ```bnf\n * AT-URI = \"at://\" AUTHORITY [ \"/\" COLLECTION [ \"/\" RKEY ] ]\n *\n * AUTHORITY = HANDLE | DID\n * COLLECTION = NSID\n * RKEY = RECORD-KEY\n * ```\n *\n * The authority section is required, and should be normalized.\n *\n * AT URI strings must respect the following syntax (as prescribed by the AT\n * protocol specification):\n *\n * - The overall URI is restricted to a subset of ASCII characters\n * - For reference below, the set of unreserved characters, as defined in [RFC-3986](https://www.rfc-editor.org/rfc/rfc3986), includes alphanumeric (`A-Za-z0-9`), period, hyphen, underscore, and tilde (`.-_~`)\n * - Maximum overall length is 8 kilobytes (which may be shortened in the future)\n * - Hex-encoding of characters is permitted (but in practice not necessary and should be avoided to keep the URI normalized and human-readable)\n * - The URI scheme is `at`, and an authority part preceded with double slashes is always required. AT URIs always start with `at://`.\n * - An authority section is required and must be non-empty. the authority can be either an atproto Handle, or a DID meeting the restrictions for use with atproto. The authority part can *not* be interpreted as a host:port pair, because of the use of colon characters (`:`) in DIDs. Colons and unreserved characters should not be escaped in DIDs, but other reserved characters (including `#`, `/`, `$`, `&`, `@`) must be escaped.\n * - Note that none of the current \"blessed\" DID methods for atproto allow these characters in DID identifiers\n * - An optional path section may follow the authority. The path may contain multiple segments separated by a single slash (`/`). Generic URI path normalization rules may be used.\n * - An optional query part is allowed, following generic URI syntax restrictions\n * - An optional fragment part is allowed, using JSON Path syntax\n *\n * @example \"at://did:plc:ewvi7nxzyoun6zhxrhs64oiz/app.bsky.actor.profile/self\"\n *\n * @see {@link https://atproto.com/specs/at-uri-scheme AT protocol - AT URI Scheme}\n */\nexport type AtUriString =\n | AtUriStringBase\n | `${AtUriStringBase}${AtUriStringFragment}`\n\n/**\n * Type guard that checks if a value is a valid {@link AtUriString}\n *\n * @see {@link AtUriString}\n */\nexport function isAtUriString<I>(\n input: I,\n options?: Omit<ParseAtUriStringOptions, 'detailed'>,\n): input is I & AtUriString {\n return parseAtUriString(input, options).success\n}\n\n/**\n * Returns the input if it is a valid {@link AtUriString} format string, or\n * `undefined` if it is not.\n *\n * @see {@link AtUriString}\n */\nexport function ifAtUriString<I>(\n input: I,\n options?: Omit<ParseAtUriStringOptions, 'detailed'>,\n): undefined | (I & AtUriString) {\n return isAtUriString(input, options) ? input : undefined\n}\n\n/**\n * Casts a string to an {@link AtUriString} if it is a valid AT URI format\n * string, throwing an error if it is not.\n *\n * @throws InvalidAtUriError if the input string does not meet the atproto AT URI format requirements.\n * @see {@link AtUriString}\n */\nexport function asAtUriString<I>(\n input: I,\n options?: ParseAtUriStringOptions,\n): I & AtUriString {\n assertAtUriString(input, options)\n return input\n}\n\n/**\n * Assert the validity of an {@link AtUriString}, throwing an error if the\n * {@link input} is not a valid AT URI.\n *\n * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}\n */\nexport function assertAtUriString<I>(\n input: I,\n options?: ParseAtUriStringOptions,\n): asserts input is I & AtUriString {\n // Optimistically use faster isAtUriString(), throwing a detailed error only\n // in case of failure. This check, and the fact that the code after it always\n // throws, also ensures that isAtUriString() and assertAtUriString()'s\n // behavior are always consistent.\n const result = parseAtUriString(input, options)\n if (!result.success) {\n throw new InvalidAtUriError(result.message)\n }\n}\n\n/**\n * Assert the **non-strict** validity of an {@link AtUriString}, throwing a\n * detailed error if the {@link input} is not a valid AT URI.\n *\n * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}\n * @deprecated use {@link assertAtUriString} with `{ strict: false }` option instead\n */\nexport function ensureValidAtUri<I>(\n input: I,\n): asserts input is I & AtUriString {\n assertAtUriString(input, { strict: false, detailed: true })\n}\n\n/**\n * Assert the (non-strict!) validity of an {@link AtUriString}, throwing an\n * error if the {@link input} is not a valid AT URI.\n *\n * @throws InvalidAtUriError if the {@link input} is not a valid {@link AtUriString}\n * @deprecated use {@link assertAtUriString} with `{ strict: false }` option instead\n */\nexport function ensureValidAtUriRegex<I>(\n input: I,\n): asserts input is I & AtUriString {\n assertAtUriString(input, { strict: false, detailed: false })\n}\n\n/**\n * Type guard that checks if a value is a valid {@link AtUriString} format\n * string, without enforcing strict record key validation. This is useful for\n * cases where you want to allow a wider range of valid ATURIs, such as when\n * validating user input or when the record key is not relevant.\n *\n * @deprecated use {@link isAtUriString} with `{ strict: false }` option instead\n */\nexport function isValidAtUri<I>(input: I): input is I & AtUriString {\n return isAtUriString(input, { strict: false })\n}\n\nexport class InvalidAtUriError extends Error {}\n\nexport type ParseAtUriStringOptions = {\n /**\n * If true, the parser will enforce that the record key (rkey) part of the URI\n * is a valid record key (validated by {@link isValidRecordKey}). If false,\n * any non-empty string of allowed chars will be accepted as a record key.\n *\n * @default true\n */\n strict?: boolean\n\n /**\n * If true, the parser will return detailed error messages for why a string is\n * not a valid AT URI. This option has no effect on the behavior of\n * {@link isAtUriString}, which will always return false for invalid strings\n * regardless of this option.\n *\n * @default false\n */\n detailed?: boolean\n}\n\nexport type AtUriParts = {\n authority: AtIdentifierString\n query?: string\n hash?: string\n} & (\n | { collection?: NsidString; rkey?: undefined }\n | { collection: NsidString; rkey?: string }\n)\n\nconst INVALID_CHAR_REGEXP = /[^a-zA-Z0-9._~:@!$&'()*+,;=%/\\\\[\\]#?-]/\nconst AT_URI_REGEXP =\n /^(?<uri>at:\\/\\/(?<authority>[^/?#\\s]+)(?:\\/(?<collection>[^/?#\\s]+)(?:\\/(?<rkey>[^/?#\\s]+))?)?(?<trailingSlash>\\/)?)(?:\\?(?<query>[^#\\s]*))?(?:#(?<hash>[^\\s]*))?$/\n\n/**\n * Parses a valid {@link AtUriString} into a {@link AtUriParts} object, or\n * returns a failure with a detailed error message if the string is not a valid\n * {@link AtUriString}.\n */\nexport function parseAtUriString(\n input: unknown,\n options?: ParseAtUriStringOptions,\n): Result<AtUriParts> {\n if (typeof input !== 'string') {\n return failure('ATURI must be a string')\n }\n\n if (input.length > 8192) {\n return failure('ATURI exceeds maximum length')\n }\n\n const invalidChar = input.match(INVALID_CHAR_REGEXP)\n if (invalidChar) {\n return failure('Disallowed characters in ATURI (ASCII)')\n }\n\n const match = input.match(AT_URI_REGEXP)\n const groups = match?.groups\n if (!groups) {\n // Regex validation failed, but we don't know exactly why. Provide more\n // detailed error messages if the \"detailed\" option is set, falling back to\n // a generic error.\n if (options?.detailed) {\n if (!input.startsWith('at://')) {\n return failure('ATURI must start with \"at://\"')\n }\n\n if (input.includes(' ')) {\n return failure('ATURI can not contain spaces')\n }\n\n if (input.includes('//', 5)) {\n return failure('ATURI can not have empty path segments')\n }\n\n const pathStart = input.indexOf('/', 5) // after \"at://\"\n if (pathStart !== -1) {\n const fragmentIndex = input.indexOf('#')\n const pathEnd = fragmentIndex !== -1 ? fragmentIndex : input.length\n const secondSlash = input.indexOf('/', pathStart + 1)\n if (secondSlash !== -1 && secondSlash !== pathEnd - 1) {\n return failure('ATURI can not have more than two path segments')\n }\n }\n }\n\n return failure('ATURI does not match expected format')\n }\n\n // @NOTE Percent-encoding is allowed by the AT URI specification, but any\n // percent-encoded characters appearing in the collection NSID or record key\n // will effectively be rejected by the isValidNsid and isValidRecordKey\n // validators. Since these values are defined to be plain ASCII identifiers,\n // this legacy behavior is beneficial: it ensures that normalized\n // (non-percent-encoded) values are always used, as prescribed by the spec.\n\n if (!isAtIdentifierString(groups.authority)) {\n return failure('ATURI has invalid authority')\n }\n\n if (groups.collection != null && !isValidNsid(groups.collection)) {\n return failure('ATURI has invalid collection')\n }\n\n if (groups.hash != null) {\n const result = parseJsonPointer(groups.hash, options)\n if (result.success) {\n groups.hash = result.value\n } else {\n return failure(`ATURI has invalid fragment (${result.message})`)\n }\n }\n\n if (options?.strict !== false) {\n if (groups.trailingSlash != null) {\n return failure('ATURI can not have a trailing slash')\n }\n\n if (groups.query != null) {\n return failure('ATURI query part is not allowed')\n }\n\n if (groups.rkey != null && !isValidRecordKey(groups.rkey)) {\n return failure('ATURI has invalid record key')\n }\n }\n\n return success(groups as AtUriParts)\n}\n\nconst BASIC_JSON_POINTER_REGEXP = /^\\/[a-zA-Z0-9._~:@!$&')(*+,;=%[\\]/-]*$/\n\n/**\n * Checks if a string is a valid JSON pointer (RFC-6901) with the allowed chars\n * for ATURI fragments. This is a very loose validation that only checks the\n * basic syntax and charset.\n */\nfunction parseJsonPointer(\n value: string,\n options?: { strict?: boolean },\n): Result<string> {\n if (!BASIC_JSON_POINTER_REGEXP.test(value)) {\n return failure('Invalid JSON pointer')\n }\n\n const result = parsePercentEncoding(value)\n\n // In non-strict mode, we allow invalid percent-encoding in the fragment\n if (!result.success && options?.strict === false) {\n return success(value)\n }\n\n return result\n}\n\nfunction parsePercentEncoding(value: string): Result<string> {\n try {\n return success(decodeURIComponent(value))\n } catch {\n // decodeURIComponent throws if the percent-encoding is invalid (e.g. \"%FF\")\n return failure('Invalid percent-encoding')\n }\n}\n"]}