@atproto/syntax 0.5.0 → 0.5.2
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 +16 -0
- package/dist/at-identifier.d.ts +49 -2
- package/dist/at-identifier.d.ts.map +1 -1
- package/dist/at-identifier.js +68 -6
- package/dist/at-identifier.js.map +1 -1
- package/dist/aturi.d.ts +2 -1
- package/dist/aturi.d.ts.map +1 -1
- package/dist/aturi.js +4 -3
- package/dist/aturi.js.map +1 -1
- package/dist/aturi_validation.js +1 -1
- package/dist/aturi_validation.js.map +1 -1
- package/dist/datetime.js +0 -3
- package/dist/datetime.js.map +1 -1
- package/package.json +1 -1
- package/src/at-identifier.ts +77 -6
- package/src/aturi.ts +19 -6
- package/src/aturi_validation.ts +1 -1
- package/src/datetime.ts +0 -3
- package/tests/datetime.test.ts +31 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @atproto/syntax
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4792](https://github.com/bluesky-social/atproto/pull/4792) [`0dbea15`](https://github.com/bluesky-social/atproto/commit/0dbea15da48a6ca913cc3a3a2d8c0ffe64d7c69a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Make AT-uri validation faster
|
|
8
|
+
|
|
9
|
+
- [#4408](https://github.com/bluesky-social/atproto/pull/4408) [`d0c136c`](https://github.com/bluesky-social/atproto/commit/d0c136cba2ec8fa97017849b1023d5af5d2cc60c) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `isHandleIdentifier` and `isDidIdentifier` utilities to discriminate `AtIdentifierString` variants
|
|
10
|
+
|
|
11
|
+
- [#4779](https://github.com/bluesky-social/atproto/pull/4779) [`527f5d4`](https://github.com/bluesky-social/atproto/commit/527f5d4c5d0c9264c2ff6f23ad06a41163fc6809) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove year 10 limit on `DatetimeString` and `AtprotoDate`
|
|
12
|
+
|
|
13
|
+
## 0.5.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`67eb0c1`](https://github.com/bluesky-social/atproto/commit/67eb0c19ac415e762e221b2ccda9f0bcf7b3dd6f) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Improve efficiency of `AtUri` `did` getter and typing of `hostname` property
|
|
18
|
+
|
|
3
19
|
## 0.5.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/dist/at-identifier.d.ts
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
import { DidString } from './did.js';
|
|
2
2
|
import { HandleString } from './handle.js';
|
|
3
|
+
/**
|
|
4
|
+
* An "at-identifier" string - either a {@link DidString} or a {@link HandleString}
|
|
5
|
+
*
|
|
6
|
+
* @example `"did:plc:1234..."`, `"did:web:example.com"` or `"alice.bsky.social"`
|
|
7
|
+
*/
|
|
3
8
|
export type AtIdentifierString = DidString | HandleString;
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Discriminates {@link HandleString} from a valid {@link AtIdentifierString}.
|
|
11
|
+
*
|
|
12
|
+
* @return `true` if the identifier is a handle, `false` otherwise
|
|
13
|
+
*/
|
|
14
|
+
export declare function isHandleIdentifier(id: AtIdentifierString): id is HandleString;
|
|
15
|
+
/**
|
|
16
|
+
* Discriminates {@link DidString} from a valid {@link AtIdentifierString}.
|
|
17
|
+
*
|
|
18
|
+
* @return `true` if the identifier is a DID, `false` otherwise
|
|
19
|
+
*/
|
|
20
|
+
export declare function isDidIdentifier(id: AtIdentifierString): id is DidString;
|
|
21
|
+
/**
|
|
22
|
+
* Validates that a string is a valid {@link AtIdentifierString} format string,
|
|
23
|
+
* throwing an error if it is not.
|
|
24
|
+
*
|
|
25
|
+
* @throws InvalidHandleError if the input string does not meet the atproto 'datetime' format requirements.
|
|
26
|
+
* @see {@link AtIdentifierString}
|
|
27
|
+
*/
|
|
28
|
+
export declare function assertAtIdentifierString<I>(input: I): asserts input is I & AtIdentifierString;
|
|
29
|
+
/**
|
|
30
|
+
* Casts a string to a {@link AtIdentifierString} if it is a valid at-identifier
|
|
31
|
+
* string, throwing an error if it is not.
|
|
32
|
+
*
|
|
33
|
+
* @throws InvalidHandleError if the input string does not meet the atproto 'at-identifier' format requirements.
|
|
34
|
+
* @see {@link AtIdentifierString}
|
|
35
|
+
*/
|
|
36
|
+
export declare function asAtIdentifierString<I>(input: I): I & AtIdentifierString;
|
|
37
|
+
/**
|
|
38
|
+
* Type guard that checks if a value is a valid AT identifier (DID or handle).
|
|
39
|
+
*
|
|
40
|
+
* @param value - The value to check
|
|
41
|
+
* @returns `true` if the value is a valid AT identifier
|
|
42
|
+
* @see {@link AtIdentifierString}
|
|
43
|
+
*/
|
|
44
|
+
export declare function isAtIdentifierString<I>(input: I): input is I & AtIdentifierString;
|
|
45
|
+
/**
|
|
46
|
+
* Returns the input if it is a valid {@link AtIdentifierString} format string, or
|
|
47
|
+
* `undefined` if it is not.
|
|
48
|
+
*
|
|
49
|
+
* @see {@link AtIdentifierString}
|
|
50
|
+
*/
|
|
51
|
+
export declare function ifAtIdentifierString<I>(input: I): undefined | (I & AtIdentifierString);
|
|
52
|
+
export { assertAtIdentifierString as ensureValidAtIdentifier, isAtIdentifierString as isValidAtIdentifier, };
|
|
6
53
|
//# sourceMappingURL=at-identifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"at-identifier.d.ts","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmC,MAAM,UAAU,CAAA;AACrE,OAAO,EACL,YAAY,EAIb,MAAM,aAAa,CAAA;AAEpB,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,YAAY,CAAA;AAEzD,wBAAgB,
|
|
1
|
+
{"version":3,"file":"at-identifier.d.ts","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmC,MAAM,UAAU,CAAA;AACrE,OAAO,EACL,YAAY,EAIb,MAAM,aAAa,CAAA;AAEpB;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,YAAY,CAAA;AAEzD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,kBAAkB,GAAG,EAAE,IAAI,YAAY,CAE7E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,kBAAkB,GAAG,EAAE,IAAI,SAAS,CAEvE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,kBAAkB,CAYzC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAGxE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,CAAC,GAAG,kBAAkB,CAQjC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,KAAK,EAAE,CAAC,GACP,SAAS,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAEtC;AAGD,OAAO,EACL,wBAAwB,IAAI,uBAAuB,EACnD,oBAAoB,IAAI,mBAAmB,GAC5C,CAAA"}
|
package/dist/at-identifier.js
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
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;
|
|
5
11
|
const did_js_1 = require("./did.js");
|
|
6
12
|
const handle_js_1 = require("./handle.js");
|
|
7
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Discriminates {@link HandleString} from a valid {@link AtIdentifierString}.
|
|
15
|
+
*
|
|
16
|
+
* @return `true` if the identifier is a handle, `false` otherwise
|
|
17
|
+
*/
|
|
18
|
+
function isHandleIdentifier(id) {
|
|
19
|
+
return !isDidIdentifier(id);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Discriminates {@link DidString} from a valid {@link AtIdentifierString}.
|
|
23
|
+
*
|
|
24
|
+
* @return `true` if the identifier is a DID, `false` otherwise
|
|
25
|
+
*/
|
|
26
|
+
function isDidIdentifier(id) {
|
|
27
|
+
return id.startsWith('did:');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validates that a string is a valid {@link AtIdentifierString} format string,
|
|
31
|
+
* throwing an error if it is not.
|
|
32
|
+
*
|
|
33
|
+
* @throws InvalidHandleError if the input string does not meet the atproto 'datetime' format requirements.
|
|
34
|
+
* @see {@link AtIdentifierString}
|
|
35
|
+
*/
|
|
36
|
+
function assertAtIdentifierString(input) {
|
|
8
37
|
try {
|
|
9
|
-
if (input
|
|
38
|
+
if (!input || typeof input !== 'string') {
|
|
39
|
+
throw new TypeError('Identifier must be a non-empty string');
|
|
40
|
+
}
|
|
41
|
+
else if (input.startsWith('did:')) {
|
|
10
42
|
(0, did_js_1.ensureValidDidRegex)(input);
|
|
11
43
|
}
|
|
12
44
|
else {
|
|
@@ -17,12 +49,42 @@ function ensureValidAtIdentifier(input) {
|
|
|
17
49
|
throw new handle_js_1.InvalidHandleError('Invalid DID or handle', { cause });
|
|
18
50
|
}
|
|
19
51
|
}
|
|
20
|
-
|
|
21
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Casts a string to a {@link AtIdentifierString} if it is a valid at-identifier
|
|
54
|
+
* string, throwing an error if it is not.
|
|
55
|
+
*
|
|
56
|
+
* @throws InvalidHandleError if the input string does not meet the atproto 'at-identifier' format requirements.
|
|
57
|
+
* @see {@link AtIdentifierString}
|
|
58
|
+
*/
|
|
59
|
+
function asAtIdentifierString(input) {
|
|
60
|
+
assertAtIdentifierString(input);
|
|
61
|
+
return input;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Type guard that checks if a value is a valid AT identifier (DID or handle).
|
|
65
|
+
*
|
|
66
|
+
* @param value - The value to check
|
|
67
|
+
* @returns `true` if the value is a valid AT identifier
|
|
68
|
+
* @see {@link AtIdentifierString}
|
|
69
|
+
*/
|
|
70
|
+
function isAtIdentifierString(input) {
|
|
71
|
+
if (!input || typeof input !== 'string') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
else if (input.startsWith('did:')) {
|
|
22
75
|
return (0, did_js_1.isValidDid)(input);
|
|
23
76
|
}
|
|
24
77
|
else {
|
|
25
78
|
return (0, handle_js_1.isValidHandle)(input);
|
|
26
79
|
}
|
|
27
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns the input if it is a valid {@link AtIdentifierString} format string, or
|
|
83
|
+
* `undefined` if it is not.
|
|
84
|
+
*
|
|
85
|
+
* @see {@link AtIdentifierString}
|
|
86
|
+
*/
|
|
87
|
+
function ifAtIdentifierString(input) {
|
|
88
|
+
return isAtIdentifierString(input) ? input : undefined;
|
|
89
|
+
}
|
|
28
90
|
//# sourceMappingURL=at-identifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"at-identifier.js","sourceRoot":"","sources":["../src/at-identifier.ts"],"names":[],"mappings":";;
|
|
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"]}
|
package/dist/aturi.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { DidString } from './did.js';
|
|
|
4
4
|
import { NsidString } from './nsid.js';
|
|
5
5
|
import { RecordKeyString } from './recordkey.js';
|
|
6
6
|
export * from './aturi_validation.js';
|
|
7
|
+
export type { AtIdentifierString, AtUriString, DidString, NsidString, RecordKeyString, };
|
|
7
8
|
export declare const ATP_URI_REGEX: RegExp;
|
|
8
9
|
export declare class AtUri {
|
|
9
10
|
hash: string;
|
|
@@ -15,7 +16,7 @@ export declare class AtUri {
|
|
|
15
16
|
get protocol(): string;
|
|
16
17
|
get origin(): `at://did:${string}:${string}` | `at://${string}.${string}`;
|
|
17
18
|
get did(): DidString;
|
|
18
|
-
get hostname():
|
|
19
|
+
get hostname(): AtIdentifierString;
|
|
19
20
|
set hostname(v: string);
|
|
20
21
|
get search(): string;
|
|
21
22
|
set search(v: string);
|
package/dist/aturi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi.d.ts","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"aturi.d.ts","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAGnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,SAAS,EAAmB,MAAM,UAAU,CAAA;AACrD,OAAO,EAAE,UAAU,EAAmB,MAAM,WAAW,CAAA;AACvD,OAAO,EAAE,eAAe,EAAwB,MAAM,gBAAgB,CAAA;AAEtE,cAAc,uBAAuB,CAAA;AAGrC,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,UAAU,EACV,eAAe,GAChB,CAAA;AAED,eAAO,MAAM,aAAa,QAEyE,CAAA;AAInG,qBAAa,KAAK;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,kBAAkB,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,eAAe,CAAA;gBAEjB,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK;IAgB9C,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAOnE,IAAI,QAAQ,WAEX;IAED,IAAI,MAAM,gEAET;IAED,IAAI,GAAG,IAAI,SAAS,CAInB;IAED,IAAI,QAAQ,IAAI,kBAAkB,CAEjC;IAED,IAAI,QAAQ,CAAC,CAAC,EAAE,MAAM,EAGrB;IAED,IAAI,MAAM,IAII,MAAM,CAFnB;IAED,IAAI,MAAM,CAAC,CAAC,EAAE,MAAM,EAEnB;IAED,IAAI,UAAU,IAUI,MAAM,CARvB;IAED,IAAI,cAAc,IAAI,UAAU,CAI/B;IAED,IAAI,UAAU,CAAC,CAAC,EAAE,MAAM,EAGvB;IAED,qBAAqB,CAAC,CAAC,EAAE,MAAM;IAM/B,IAAI,IAAI,IAUI,MAAM,CARjB;IAED,IAAI,QAAQ,IAAI,eAAe,CAI9B;IAED,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAGjB;IAED,eAAe,CAAC,CAAC,EAAE,MAAM;IAOzB,IAAI,IAAI,gBAEP;IAED,QAAQ,IAAI,WAAW;CAexB"}
|
package/dist/aturi.js
CHANGED
|
@@ -44,9 +44,10 @@ class AtUri {
|
|
|
44
44
|
return `at://${this.host}`;
|
|
45
45
|
}
|
|
46
46
|
get did() {
|
|
47
|
-
const {
|
|
48
|
-
(0,
|
|
49
|
-
|
|
47
|
+
const { host } = this;
|
|
48
|
+
if ((0, at_identifier_js_1.isDidIdentifier)(host))
|
|
49
|
+
return host;
|
|
50
|
+
throw new did_js_1.InvalidDidError(`AtUri "${this}" does not have a DID hostname`);
|
|
50
51
|
}
|
|
51
52
|
get hostname() {
|
|
52
53
|
return this.host;
|
package/dist/aturi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi.js","sourceRoot":"","sources":["../src/aturi.ts"],"names":[],"mappings":";;;;AAAA,
|
|
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 path = this.pathname || '/'\n if (!path.startsWith('/')) {\n path = `/${path}`\n }\n let qs = ''\n if (this.searchParams.size) {\n qs = `?${this.searchParams.toString()}`\n }\n let hash = this.hash\n if (hash && !hash.startsWith('#')) {\n hash = `#${hash}`\n }\n return `at://${this.host}${path}${qs}${hash}` 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"]}
|
package/dist/aturi_validation.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aturi_validation.js","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"aturi_validation.js","sourceRoot":"","sources":["../src/aturi_validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtIdentifierString, ensureValidAtIdentifier } from './at-identifier.js'\nimport { ensureValidDidRegex } from './did.js'\nimport { ensureValidHandleRegex } from './handle.js'\nimport { NsidString, isValidNsid } from './nsid.js'\n\nexport type AtUriString =\n | `at://${AtIdentifierString}`\n | `at://${AtIdentifierString}/${NsidString}`\n | `at://${AtIdentifierString}/${NsidString}/${string}`\n\n// Human-readable constraints on ATURI:\n// - following regular URLs, a 8KByte hard total length limit\n// - follows ATURI docs on website\n// - all ASCII characters, no whitespace. non-ASCII could be URL-encoded\n// - starts \"at://\"\n// - \"authority\" is a valid DID or a valid handle\n// - optionally, follow \"authority\" with \"/\" and valid NSID as start of path\n// - optionally, if NSID given, follow that with \"/\" and rkey\n// - rkey path component can include URL-encoded (\"percent encoded\"), or:\n// ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\" / \":\" / \"@\" / \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\" / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n// [a-zA-Z0-9._~:@!$&'\\(\\)*+,;=-]\n// - rkey must have at least one char\n// - regardless of path component, a fragment can follow as \"#\" and then a JSON pointer (RFC-6901)\n\nexport function ensureValidAtUri<I extends string>(\n input: I,\n): asserts input is I & AtUriString {\n const fragmentIndex = input.indexOf('#')\n if (fragmentIndex !== -1) {\n if (input.charCodeAt(fragmentIndex + 1) !== 47) {\n throw new Error('ATURI fragment must be non-empty and start with slash')\n }\n if (input.includes('#', fragmentIndex + 1)) {\n throw new Error('ATURI can have at most one \"#\", separating fragment out')\n }\n\n // NOTE: enforcing *some* checks here for sanity. Eg, at least no whitespace\n const fragment = input.slice(fragmentIndex + 1)\n if (!/^\\/[a-zA-Z0-9._~:@!$&')(*+,;=%[\\]/-]*$/.test(fragment)) {\n throw new Error('Disallowed characters in ATURI fragment (ASCII)')\n }\n }\n\n const uri = fragmentIndex === -1 ? input : input.slice(0, fragmentIndex)\n\n if (uri.length > 8 * 1024) {\n throw new Error('ATURI is far too long')\n }\n\n if (!uri.startsWith('at://')) {\n throw new Error('ATURI must start with \"at://\"')\n }\n\n // check that all chars are boring ASCII\n if (!/^[a-zA-Z0-9._~:@!$&')(*+,;=%/-]*$/.test(uri)) {\n throw new Error('Disallowed characters in ATURI (ASCII)')\n }\n\n const authorityEnd = uri.indexOf('/', 5)\n const authority =\n authorityEnd === -1 ? uri.slice(5) : uri.slice(5, authorityEnd)\n try {\n ensureValidAtIdentifier(authority)\n } catch (cause) {\n throw new Error('ATURI authority must be a valid handle or DID', { cause })\n }\n\n const collectionStart = authorityEnd === -1 ? -1 : authorityEnd + 1\n const collectionEnd =\n collectionStart === -1 ? -1 : uri.indexOf('/', collectionStart)\n\n if (collectionStart !== -1) {\n const collection =\n collectionEnd === -1\n ? uri.slice(collectionStart)\n : uri.slice(collectionStart, collectionEnd)\n\n if (collection.length === 0) {\n throw new Error(\n 'ATURI can not have a slash after authority without a path segment',\n )\n }\n if (!isValidNsid(collection)) {\n throw new Error(\n 'ATURI requires first path segment (if supplied) to be valid NSID',\n )\n }\n }\n\n const recordKeyStart = collectionEnd === -1 ? -1 : collectionEnd + 1\n const recordKeyEnd =\n recordKeyStart === -1 ? -1 : uri.indexOf('/', recordKeyStart)\n\n if (recordKeyStart !== -1) {\n if (recordKeyStart === uri.length) {\n throw new Error(\n 'ATURI can not have a slash after collection, unless record key is provided',\n )\n }\n // would validate rkey here, but there are basically no constraints!\n }\n\n if (recordKeyEnd !== -1) {\n throw new Error(\n 'ATURI path can have at most two parts, and no trailing slash',\n )\n }\n}\n\nexport function ensureValidAtUriRegex<I extends string>(\n input: I,\n): asserts input is I & AtUriString {\n // simple regex to enforce most constraints via just regex and length.\n // hand wrote this regex based on above constraints. whew!\n const aturiRegex =\n /^at:\\/\\/(?<authority>[a-zA-Z0-9._:%-]+)(\\/(?<collection>[a-zA-Z0-9-.]+)(\\/(?<rkey>[a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(#(?<fragment>\\/[a-zA-Z0-9._~:@!$&%')(*+,;=\\-[\\]/\\\\]*))?$/\n const rm = input.match(aturiRegex)\n if (!rm || !rm.groups) {\n throw new Error(\"ATURI didn't validate via regex\")\n }\n const groups = rm.groups\n\n try {\n ensureValidHandleRegex(groups.authority)\n } catch {\n try {\n ensureValidDidRegex(groups.authority)\n } catch {\n throw new Error('ATURI authority must be a valid handle or DID')\n }\n }\n\n if (groups.collection && !isValidNsid(groups.collection)) {\n throw new Error('ATURI collection path segment must be a valid NSID')\n }\n\n if (input.length > 8 * 1024) {\n throw new Error('ATURI is far too long')\n }\n}\n\nexport function isValidAtUri<I extends string>(\n input: I,\n): input is I & AtUriString {\n try {\n ensureValidAtUri(input)\n } catch {\n return false\n }\n\n return true\n}\n"]}
|
package/dist/datetime.js
CHANGED
|
@@ -226,9 +226,6 @@ function parseDate(date) {
|
|
|
226
226
|
if (fullYear > 9999) {
|
|
227
227
|
return failure('datetime year is too far in the future');
|
|
228
228
|
}
|
|
229
|
-
if (fullYear < 10) {
|
|
230
|
-
return failure('datetime so close to year zero not allowed');
|
|
231
|
-
}
|
|
232
229
|
return success(date);
|
|
233
230
|
}
|
|
234
231
|
//# sourceMappingURL=datetime.js.map
|
package/dist/datetime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datetime.js","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Indicates a date or string is not a valid representation of a datetime\n * according to the atproto\n * {@link https://atproto.com/specs/lexicon#datetime specification}.\n */\nexport class InvalidDatetimeError extends Error {}\n\n/**\n * A subset of {@link DatetimeString} that represent valid datetime strings with\n * the format: `YYYY-MM-DDTHH:mm:ss.sssZ`, as returned by `Date.toISOString()\n * for dates between the years 0000 and 9999.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString}\n */\nexport type ISODatetimeString =\n // @TODO Switch to branded types for more accurate type safety.\n `${string}-${string}-${string}T${string}:${string}:${string}.${string}Z`\n\n/**\n * Represents a {@link Date} that can be safely stringified into a valid atproto\n * {@link DatetimeString} using the {@link Date.toISOString toISOString()}\n * method.\n */\nexport interface AtprotoDate extends Date {\n toISOString(): ISODatetimeString\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function assertAtprotoDate(date: Date): asserts date is AtprotoDate {\n const res = parseDate(date)\n if (!res.success) {\n throw new InvalidDatetimeError(res.message)\n }\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function asAtprotoDate(date: Date): AtprotoDate {\n assertAtprotoDate(date)\n return date\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function isAtprotoDate(date: Date): date is AtprotoDate {\n return parseDate(date).success\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function ifAtprotoDate(date: Date): AtprotoDate | undefined {\n return isAtprotoDate(date) ? date : undefined\n}\n\n/**\n * Datetime strings in atproto data structures and API calls should meet the\n * {@link https://ijmacd.github.io/rfc3339-iso8601/ intersecting} requirements\n * of the RFC 3339, ISO 8601, and WHATWG HTML datetime standards.\n *\n * @note This literal template type is not accurate enough to ensure that a\n * string is a valid atproto datetime. The {@link DatetimeString} validation\n * functions ({@link assertDatetimeString}, {@link isDatetimeString}, etc)\n * should be used to validate that a string meets the atproto datetime\n * requirements, and the {@link toDatetimeString} function should be used to\n * convert a {@link Date} object into a valid {@link DatetimeString} string.\n *\n * @example \"2024-01-15T12:30:00Z\"\n * @example \"2024-01-15T12:30:00.000Z\"\n * @example \"2024-01-15T12:30:00+00:00\"\n * @example \"2024-01-15T11:30:00-01:00\"\n * @see {@link https://atproto.com/specs/lexicon#datetime atproto Lexicon datetime format}\n * @see {@link https://www.rfc-editor.org/rfc/rfc3339 RFC 3339}\n * @see {@link https://www.iso.org/iso-8601-date-and-time-format.html ISO 8601}\n */\nexport type DatetimeString =\n // @TODO Switch to branded types for more accurate type safety?\n | `${string}-${string}-${string}T${string}:${string}:${string}Z`\n | `${string}-${string}-${string}T${string}:${string}:${string}${'+' | '-'}${string}:${string}`\n\n/**\n * Validates that a string is a valid {@link DatetimeString} format string,\n * throwing an error if it is not.\n *\n * @throws InvalidDatetimeError if the input string does not meet the atproto 'datetime' format requirements.\n * @see {@link DatetimeString}\n */\nexport function assertDatetimeString<I>(\n input: I,\n): asserts input is I & DatetimeString {\n const result = parseString(input)\n if (!result.success) {\n throw new InvalidDatetimeError(result.message)\n }\n}\n\n/**\n * Casts a string to a {@link DatetimeString} if it is a valid datetime format\n * string, throwing an error if it is not.\n *\n * @throws InvalidDatetimeError if the input string does not meet the atproto 'datetime' format requirements.\n * @see {@link DatetimeString}\n */\nexport function asDatetimeString<I>(input: I): I & DatetimeString {\n assertDatetimeString(input)\n return input\n}\n\n/**\n * Checks if a string is a valid {@link DatetimeString} format string.\n *\n * @see {@link DatetimeString}\n */\nexport function isDatetimeString<I>(input: I): input is I & DatetimeString {\n return parseString(input).success\n}\n\n/**\n * Returns the input if it is a valid {@link DatetimeString} format string, or\n * `undefined` if it is not.\n *\n * @see {@link DatetimeString}\n */\nexport function ifDatetimeString<I>(\n input: I,\n): undefined | (I & DatetimeString) {\n return isDatetimeString(input) ? input : undefined\n}\n\n/**\n * Returns the current date and time as a {@link DatetimeString}.\n *\n * @see {@link DatetimeString}\n */\nexport function currentDatetimeString(): DatetimeString {\n return toDatetimeString(new Date())\n}\n\n/**\n * Converts any {@link Date} into a {@link DatetimeString} if possible, throwing\n * an error if the date is not a valid atproto datetime.\n *\n * This is short-hand for `asAtprotoDate(date).toISOString()`.\n *\n * @throws InvalidDatetimeError if the input date is not a valid atproto datetime (eg, it is too far in the future or past, or it normalizes to a negative year).\n * @see {@link DatetimeString}\n */\nexport function toDatetimeString(date: Date): DatetimeString {\n return asAtprotoDate(date).toISOString()\n}\n\n/**\n * Takes a flexible datetime string and normalizes its representation.\n *\n * This function will work with any valid value that can be parsed as a date. It\n * *additionally* is more flexible about accepting datetimes that are missing\n * timezone information, and normalizing them to a valid atproto datetime.\n *\n * One use-case is a consistent, sortable string. Another is to work with older\n * invalid createdAt datetimes.\n *\n * @returns ISODatetimeString - a valid atproto datetime with millisecond precision (3 sub-second digits) and UTC timezone with trailing 'Z' syntax.\n * @throws InvalidDatetimeError - if the input string could not be parsed as a datetime, even with permissive parsing.\n */\nexport function normalizeDatetime(dtStr: string): ISODatetimeString {\n // Parse the string as is\n const date = new Date(dtStr)\n if (isAtprotoDate(date)) {\n return date.toISOString()\n }\n\n // if dtStr is not a valid date, try parsing again with a timezone\n if (isNaN(date.getTime()) && !/.*(([+-]\\d\\d:?\\d\\d)|[a-zA-Z])$/.test(dtStr)) {\n const date = new Date(`${dtStr}Z`)\n if (isAtprotoDate(date)) {\n return date.toISOString()\n }\n }\n\n throw new InvalidDatetimeError(\n 'datetime did not parse as any timestamp format',\n )\n}\n\n/**\n * Variant of {@link normalizeDatetime} which always returns a valid datetime\n * string.\n *\n * If a {@link InvalidDatetimeError} is encountered, returns the UNIX epoch time\n * as a UTC datetime (`1970-01-01T00:00:00.000Z`).\n *\n * @see {@link normalizeDatetime}\n */\nexport function normalizeDatetimeAlways(dtStr: string): ISODatetimeString {\n try {\n return normalizeDatetime(dtStr)\n } catch (err) {\n return '1970-01-01T00:00:00.000Z'\n }\n}\n\n// Legacy exports (should we deprecate these ?)\nexport {\n assertDatetimeString as ensureValidDatetime,\n isDatetimeString as isValidDatetime,\n}\n\n// -----------------------------------------------------------------------------\n// ------------------------- Internal validation logic -------------------------\n// -----------------------------------------------------------------------------\n\n// Validation utils that allow avoiding try/catch for control flow (performance\n// optimization). Other syntax formats should also use this pattern to avoid\n// try/catch in their validation logic, at which point these utils can be moved\n// to a common internal utils.\ntype FailureResult = { success: false; message: string }\nconst failure = (m: string): FailureResult => ({ success: false, message: m })\ntype SuccessResult<V> = { success: true; value: V }\nconst success = <V>(v: V): SuccessResult<V> => ({ success: true, value: v })\ntype Result<V> = FailureResult | SuccessResult<V>\n\n/**\n * @see {@link https://www.rfc-editor.org/rfc/rfc3339#section-5.6 Internet Date/Time Format}\n *\n * @example\n * ```abnf\n * date-fullyear = 4DIGIT\n * date-month = 2DIGIT ; 01-12\n * date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n * ; month/year\n * time-hour = 2DIGIT ; 00-23\n * time-minute = 2DIGIT ; 00-59\n * time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second\n * ; rules\n * time-secfrac = \".\" 1*DIGIT\n * time-numoffset = (\"+\" / \"-\") time-hour \":\" time-minute\n * time-offset = \"Z\" / time-numoffset\n * partial-time = time-hour \":\" time-minute \":\" time-second\n * [time-secfrac]\n * full-date = date-fullyear \"-\" date-month \"-\" date-mday\n * full-time = partial-time time-offset\n * date-time = full-date \"T\" full-time\n * ```\n */\nconst DATETIME_REGEX =\n /^(?<full_year>[0-9]{4})-(?<date_month>0[1-9]|1[012])-(?<date_mday>[0-2][0-9]|3[01])T(?<time_hour>[0-1][0-9]|2[0-3]):(?<time_minute>[0-5][0-9]):(?<time_second>[0-5][0-9]|60)(?<time_secfrac>\\.[0-9]+)?(?<time_offset>Z|(?<time_numoffset>[+-](?:[0-1][0-9]|2[0-3]):[0-5][0-9]))$/\n\n/**\n * Validates that the input is a datetime string according to atproto Lexicon\n * rules, and parses it into a Date object.\n */\nfunction parseString(input: unknown): Result<AtprotoDate> {\n // @NOTE Performing cheap tests first\n if (typeof input !== 'string') {\n return failure('datetime must be a string')\n }\n if (input.length > 64) {\n return failure('datetime is too long (64 chars max)')\n }\n if (input.endsWith('-00:00')) {\n return failure('datetime can not use \"-00:00\" for UTC timezone')\n }\n if (!DATETIME_REGEX.test(input)) {\n return failure(\n \"datetime is not in a valid format (must match RFC 3339 & ISO 8601 with 'Z' or ±hh:mm timezone)\",\n )\n }\n\n // must parse as ISO 8601; this also verifies semantics like leap seconds and\n // correct number of days in month, which the regex does not check for\n const date = new Date(input)\n\n return parseDate(date)\n}\n\n/**\n * Ensures that a Date object represents a valid datetime according to atproto\n * Lexicon rules. This ensures that `date.toISOString()` will produce a valid\n * datetime string that can be used where {@link DatetimeString} is expected.\n */\nfunction parseDate(date: Date): Result<AtprotoDate> {\n const fullYear = date.getUTCFullYear()\n // Ensures that the date is valid. We could check isNaN(date.getTime()) here\n // but since we'll check the year anyway, we just use that for the validity\n // check since an invalid date will have NaN year.\n if (Number.isNaN(fullYear)) {\n return failure('datetime did not parse as ISO 8601')\n }\n // Ensure that the ISO string representation does not start with ±YYYYYY\n if (fullYear < 0) {\n return failure('datetime normalized to a negative time')\n }\n if (fullYear > 9999) {\n return failure('datetime year is too far in the future')\n }\n if (fullYear < 10) {\n return failure('datetime so close to year zero not allowed')\n }\n return success(date as AtprotoDate)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"datetime.js","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Indicates a date or string is not a valid representation of a datetime\n * according to the atproto\n * {@link https://atproto.com/specs/lexicon#datetime specification}.\n */\nexport class InvalidDatetimeError extends Error {}\n\n/**\n * A subset of {@link DatetimeString} that represent valid datetime strings with\n * the format: `YYYY-MM-DDTHH:mm:ss.sssZ`, as returned by `Date.toISOString()\n * for dates between the years 0000 and 9999.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString}\n */\nexport type ISODatetimeString =\n // @TODO Switch to branded types for more accurate type safety.\n `${string}-${string}-${string}T${string}:${string}:${string}.${string}Z`\n\n/**\n * Represents a {@link Date} that can be safely stringified into a valid atproto\n * {@link DatetimeString} using the {@link Date.toISOString toISOString()}\n * method.\n */\nexport interface AtprotoDate extends Date {\n toISOString(): ISODatetimeString\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function assertAtprotoDate(date: Date): asserts date is AtprotoDate {\n const res = parseDate(date)\n if (!res.success) {\n throw new InvalidDatetimeError(res.message)\n }\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function asAtprotoDate(date: Date): AtprotoDate {\n assertAtprotoDate(date)\n return date\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function isAtprotoDate(date: Date): date is AtprotoDate {\n return parseDate(date).success\n}\n\n/**\n * @see {@link AtprotoDate}\n */\nexport function ifAtprotoDate(date: Date): AtprotoDate | undefined {\n return isAtprotoDate(date) ? date : undefined\n}\n\n/**\n * Datetime strings in atproto data structures and API calls should meet the\n * {@link https://ijmacd.github.io/rfc3339-iso8601/ intersecting} requirements\n * of the RFC 3339, ISO 8601, and WHATWG HTML datetime standards.\n *\n * @note This literal template type is not accurate enough to ensure that a\n * string is a valid atproto datetime. The {@link DatetimeString} validation\n * functions ({@link assertDatetimeString}, {@link isDatetimeString}, etc)\n * should be used to validate that a string meets the atproto datetime\n * requirements, and the {@link toDatetimeString} function should be used to\n * convert a {@link Date} object into a valid {@link DatetimeString} string.\n *\n * @example \"2024-01-15T12:30:00Z\"\n * @example \"2024-01-15T12:30:00.000Z\"\n * @example \"2024-01-15T12:30:00+00:00\"\n * @example \"2024-01-15T11:30:00-01:00\"\n * @see {@link https://atproto.com/specs/lexicon#datetime atproto Lexicon datetime format}\n * @see {@link https://www.rfc-editor.org/rfc/rfc3339 RFC 3339}\n * @see {@link https://www.iso.org/iso-8601-date-and-time-format.html ISO 8601}\n */\nexport type DatetimeString =\n // @TODO Switch to branded types for more accurate type safety?\n | `${string}-${string}-${string}T${string}:${string}:${string}Z`\n | `${string}-${string}-${string}T${string}:${string}:${string}${'+' | '-'}${string}:${string}`\n\n/**\n * Validates that a string is a valid {@link DatetimeString} format string,\n * throwing an error if it is not.\n *\n * @throws InvalidDatetimeError if the input string does not meet the atproto 'datetime' format requirements.\n * @see {@link DatetimeString}\n */\nexport function assertDatetimeString<I>(\n input: I,\n): asserts input is I & DatetimeString {\n const result = parseString(input)\n if (!result.success) {\n throw new InvalidDatetimeError(result.message)\n }\n}\n\n/**\n * Casts a string to a {@link DatetimeString} if it is a valid datetime format\n * string, throwing an error if it is not.\n *\n * @throws InvalidDatetimeError if the input string does not meet the atproto 'datetime' format requirements.\n * @see {@link DatetimeString}\n */\nexport function asDatetimeString<I>(input: I): I & DatetimeString {\n assertDatetimeString(input)\n return input\n}\n\n/**\n * Checks if a string is a valid {@link DatetimeString} format string.\n *\n * @see {@link DatetimeString}\n */\nexport function isDatetimeString<I>(input: I): input is I & DatetimeString {\n return parseString(input).success\n}\n\n/**\n * Returns the input if it is a valid {@link DatetimeString} format string, or\n * `undefined` if it is not.\n *\n * @see {@link DatetimeString}\n */\nexport function ifDatetimeString<I>(\n input: I,\n): undefined | (I & DatetimeString) {\n return isDatetimeString(input) ? input : undefined\n}\n\n/**\n * Returns the current date and time as a {@link DatetimeString}.\n *\n * @see {@link DatetimeString}\n */\nexport function currentDatetimeString(): DatetimeString {\n return toDatetimeString(new Date())\n}\n\n/**\n * Converts any {@link Date} into a {@link DatetimeString} if possible, throwing\n * an error if the date is not a valid atproto datetime.\n *\n * This is short-hand for `asAtprotoDate(date).toISOString()`.\n *\n * @throws InvalidDatetimeError if the input date is not a valid atproto datetime (eg, it is too far in the future or past, or it normalizes to a negative year).\n * @see {@link DatetimeString}\n */\nexport function toDatetimeString(date: Date): DatetimeString {\n return asAtprotoDate(date).toISOString()\n}\n\n/**\n * Takes a flexible datetime string and normalizes its representation.\n *\n * This function will work with any valid value that can be parsed as a date. It\n * *additionally* is more flexible about accepting datetimes that are missing\n * timezone information, and normalizing them to a valid atproto datetime.\n *\n * One use-case is a consistent, sortable string. Another is to work with older\n * invalid createdAt datetimes.\n *\n * @returns ISODatetimeString - a valid atproto datetime with millisecond precision (3 sub-second digits) and UTC timezone with trailing 'Z' syntax.\n * @throws InvalidDatetimeError - if the input string could not be parsed as a datetime, even with permissive parsing.\n */\nexport function normalizeDatetime(dtStr: string): ISODatetimeString {\n // Parse the string as is\n const date = new Date(dtStr)\n if (isAtprotoDate(date)) {\n return date.toISOString()\n }\n\n // if dtStr is not a valid date, try parsing again with a timezone\n if (isNaN(date.getTime()) && !/.*(([+-]\\d\\d:?\\d\\d)|[a-zA-Z])$/.test(dtStr)) {\n const date = new Date(`${dtStr}Z`)\n if (isAtprotoDate(date)) {\n return date.toISOString()\n }\n }\n\n throw new InvalidDatetimeError(\n 'datetime did not parse as any timestamp format',\n )\n}\n\n/**\n * Variant of {@link normalizeDatetime} which always returns a valid datetime\n * string.\n *\n * If a {@link InvalidDatetimeError} is encountered, returns the UNIX epoch time\n * as a UTC datetime (`1970-01-01T00:00:00.000Z`).\n *\n * @see {@link normalizeDatetime}\n */\nexport function normalizeDatetimeAlways(dtStr: string): ISODatetimeString {\n try {\n return normalizeDatetime(dtStr)\n } catch (err) {\n return '1970-01-01T00:00:00.000Z'\n }\n}\n\n// Legacy exports (should we deprecate these ?)\nexport {\n assertDatetimeString as ensureValidDatetime,\n isDatetimeString as isValidDatetime,\n}\n\n// -----------------------------------------------------------------------------\n// ------------------------- Internal validation logic -------------------------\n// -----------------------------------------------------------------------------\n\n// Validation utils that allow avoiding try/catch for control flow (performance\n// optimization). Other syntax formats should also use this pattern to avoid\n// try/catch in their validation logic, at which point these utils can be moved\n// to a common internal utils.\ntype FailureResult = { success: false; message: string }\nconst failure = (m: string): FailureResult => ({ success: false, message: m })\ntype SuccessResult<V> = { success: true; value: V }\nconst success = <V>(v: V): SuccessResult<V> => ({ success: true, value: v })\ntype Result<V> = FailureResult | SuccessResult<V>\n\n/**\n * @see {@link https://www.rfc-editor.org/rfc/rfc3339#section-5.6 Internet Date/Time Format}\n *\n * @example\n * ```abnf\n * date-fullyear = 4DIGIT\n * date-month = 2DIGIT ; 01-12\n * date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n * ; month/year\n * time-hour = 2DIGIT ; 00-23\n * time-minute = 2DIGIT ; 00-59\n * time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second\n * ; rules\n * time-secfrac = \".\" 1*DIGIT\n * time-numoffset = (\"+\" / \"-\") time-hour \":\" time-minute\n * time-offset = \"Z\" / time-numoffset\n * partial-time = time-hour \":\" time-minute \":\" time-second\n * [time-secfrac]\n * full-date = date-fullyear \"-\" date-month \"-\" date-mday\n * full-time = partial-time time-offset\n * date-time = full-date \"T\" full-time\n * ```\n */\nconst DATETIME_REGEX =\n /^(?<full_year>[0-9]{4})-(?<date_month>0[1-9]|1[012])-(?<date_mday>[0-2][0-9]|3[01])T(?<time_hour>[0-1][0-9]|2[0-3]):(?<time_minute>[0-5][0-9]):(?<time_second>[0-5][0-9]|60)(?<time_secfrac>\\.[0-9]+)?(?<time_offset>Z|(?<time_numoffset>[+-](?:[0-1][0-9]|2[0-3]):[0-5][0-9]))$/\n\n/**\n * Validates that the input is a datetime string according to atproto Lexicon\n * rules, and parses it into a Date object.\n */\nfunction parseString(input: unknown): Result<AtprotoDate> {\n // @NOTE Performing cheap tests first\n if (typeof input !== 'string') {\n return failure('datetime must be a string')\n }\n if (input.length > 64) {\n return failure('datetime is too long (64 chars max)')\n }\n if (input.endsWith('-00:00')) {\n return failure('datetime can not use \"-00:00\" for UTC timezone')\n }\n if (!DATETIME_REGEX.test(input)) {\n return failure(\n \"datetime is not in a valid format (must match RFC 3339 & ISO 8601 with 'Z' or ±hh:mm timezone)\",\n )\n }\n\n // must parse as ISO 8601; this also verifies semantics like leap seconds and\n // correct number of days in month, which the regex does not check for\n const date = new Date(input)\n\n return parseDate(date)\n}\n\n/**\n * Ensures that a Date object represents a valid datetime according to atproto\n * Lexicon rules. This ensures that `date.toISOString()` will produce a valid\n * datetime string that can be used where {@link DatetimeString} is expected.\n */\nfunction parseDate(date: Date): Result<AtprotoDate> {\n const fullYear = date.getUTCFullYear()\n // Ensures that the date is valid. We could check isNaN(date.getTime()) here\n // but since we'll check the year anyway, we just use that for the validity\n // check since an invalid date will have NaN year.\n if (Number.isNaN(fullYear)) {\n return failure('datetime did not parse as ISO 8601')\n }\n // Ensure that the ISO string representation does not start with ±YYYYYY\n if (fullYear < 0) {\n return failure('datetime normalized to a negative time')\n }\n if (fullYear > 9999) {\n return failure('datetime year is too far in the future')\n }\n return success(date as AtprotoDate)\n}\n"]}
|
package/package.json
CHANGED
package/src/at-identifier.ts
CHANGED
|
@@ -6,13 +6,45 @@ import {
|
|
|
6
6
|
isValidHandle,
|
|
7
7
|
} from './handle.js'
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* An "at-identifier" string - either a {@link DidString} or a {@link HandleString}
|
|
11
|
+
*
|
|
12
|
+
* @example `"did:plc:1234..."`, `"did:web:example.com"` or `"alice.bsky.social"`
|
|
13
|
+
*/
|
|
9
14
|
export type AtIdentifierString = DidString | HandleString
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Discriminates {@link HandleString} from a valid {@link AtIdentifierString}.
|
|
18
|
+
*
|
|
19
|
+
* @return `true` if the identifier is a handle, `false` otherwise
|
|
20
|
+
*/
|
|
21
|
+
export function isHandleIdentifier(id: AtIdentifierString): id is HandleString {
|
|
22
|
+
return !isDidIdentifier(id)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Discriminates {@link DidString} from a valid {@link AtIdentifierString}.
|
|
27
|
+
*
|
|
28
|
+
* @return `true` if the identifier is a DID, `false` otherwise
|
|
29
|
+
*/
|
|
30
|
+
export function isDidIdentifier(id: AtIdentifierString): id is DidString {
|
|
31
|
+
return id.startsWith('did:')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validates that a string is a valid {@link AtIdentifierString} format string,
|
|
36
|
+
* throwing an error if it is not.
|
|
37
|
+
*
|
|
38
|
+
* @throws InvalidHandleError if the input string does not meet the atproto 'datetime' format requirements.
|
|
39
|
+
* @see {@link AtIdentifierString}
|
|
40
|
+
*/
|
|
41
|
+
export function assertAtIdentifierString<I>(
|
|
42
|
+
input: I,
|
|
43
|
+
): asserts input is I & AtIdentifierString {
|
|
14
44
|
try {
|
|
15
|
-
if (input
|
|
45
|
+
if (!input || typeof input !== 'string') {
|
|
46
|
+
throw new TypeError('Identifier must be a non-empty string')
|
|
47
|
+
} else if (input.startsWith('did:')) {
|
|
16
48
|
ensureValidDidRegex(input)
|
|
17
49
|
} else {
|
|
18
50
|
ensureValidHandleRegex(input)
|
|
@@ -22,12 +54,51 @@ export function ensureValidAtIdentifier(
|
|
|
22
54
|
}
|
|
23
55
|
}
|
|
24
56
|
|
|
25
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Casts a string to a {@link AtIdentifierString} if it is a valid at-identifier
|
|
59
|
+
* string, throwing an error if it is not.
|
|
60
|
+
*
|
|
61
|
+
* @throws InvalidHandleError if the input string does not meet the atproto 'at-identifier' format requirements.
|
|
62
|
+
* @see {@link AtIdentifierString}
|
|
63
|
+
*/
|
|
64
|
+
export function asAtIdentifierString<I>(input: I): I & AtIdentifierString {
|
|
65
|
+
assertAtIdentifierString(input)
|
|
66
|
+
return input
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Type guard that checks if a value is a valid AT identifier (DID or handle).
|
|
71
|
+
*
|
|
72
|
+
* @param value - The value to check
|
|
73
|
+
* @returns `true` if the value is a valid AT identifier
|
|
74
|
+
* @see {@link AtIdentifierString}
|
|
75
|
+
*/
|
|
76
|
+
export function isAtIdentifierString<I>(
|
|
26
77
|
input: I,
|
|
27
78
|
): input is I & AtIdentifierString {
|
|
28
|
-
if (input
|
|
79
|
+
if (!input || typeof input !== 'string') {
|
|
80
|
+
return false
|
|
81
|
+
} else if (input.startsWith('did:')) {
|
|
29
82
|
return isValidDid(input)
|
|
30
83
|
} else {
|
|
31
84
|
return isValidHandle(input)
|
|
32
85
|
}
|
|
33
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns the input if it is a valid {@link AtIdentifierString} format string, or
|
|
90
|
+
* `undefined` if it is not.
|
|
91
|
+
*
|
|
92
|
+
* @see {@link AtIdentifierString}
|
|
93
|
+
*/
|
|
94
|
+
export function ifAtIdentifierString<I>(
|
|
95
|
+
input: I,
|
|
96
|
+
): undefined | (I & AtIdentifierString) {
|
|
97
|
+
return isAtIdentifierString(input) ? input : undefined
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Legacy exports (should we deprecate these ?)
|
|
101
|
+
export {
|
|
102
|
+
assertAtIdentifierString as ensureValidAtIdentifier,
|
|
103
|
+
isAtIdentifierString as isValidAtIdentifier,
|
|
104
|
+
}
|
package/src/aturi.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AtIdentifierString,
|
|
3
|
+
ensureValidAtIdentifier,
|
|
4
|
+
isDidIdentifier,
|
|
5
|
+
} from './at-identifier.js'
|
|
2
6
|
import { AtUriString } from './aturi_validation.js'
|
|
3
|
-
import { DidString,
|
|
7
|
+
import { DidString, InvalidDidError } from './did.js'
|
|
4
8
|
import { NsidString, ensureValidNsid } from './nsid.js'
|
|
5
9
|
import { RecordKeyString, ensureValidRecordKey } from './recordkey.js'
|
|
6
10
|
|
|
7
11
|
export * from './aturi_validation.js'
|
|
8
12
|
|
|
13
|
+
// Re-export types used in public interface
|
|
14
|
+
export type {
|
|
15
|
+
AtIdentifierString,
|
|
16
|
+
AtUriString,
|
|
17
|
+
DidString,
|
|
18
|
+
NsidString,
|
|
19
|
+
RecordKeyString,
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
export const ATP_URI_REGEX =
|
|
10
23
|
// proto- --did-------------- --name---------------- --path---- --query-- --hash--
|
|
11
24
|
/^(at:\/\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\/[^?#\s]*)?(\?[^#\s]+)?(#[^\s]+)?$/i
|
|
@@ -50,12 +63,12 @@ export class AtUri {
|
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
get did(): DidString {
|
|
53
|
-
const {
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
const { host } = this
|
|
67
|
+
if (isDidIdentifier(host)) return host
|
|
68
|
+
throw new InvalidDidError(`AtUri "${this}" does not have a DID hostname`)
|
|
56
69
|
}
|
|
57
70
|
|
|
58
|
-
get hostname() {
|
|
71
|
+
get hostname(): AtIdentifierString {
|
|
59
72
|
return this.host
|
|
60
73
|
}
|
|
61
74
|
|
package/src/aturi_validation.ts
CHANGED
package/src/datetime.ts
CHANGED
|
@@ -297,8 +297,5 @@ function parseDate(date: Date): Result<AtprotoDate> {
|
|
|
297
297
|
if (fullYear > 9999) {
|
|
298
298
|
return failure('datetime year is too far in the future')
|
|
299
299
|
}
|
|
300
|
-
if (fullYear < 10) {
|
|
301
|
-
return failure('datetime so close to year zero not allowed')
|
|
302
|
-
}
|
|
303
300
|
return success(date as AtprotoDate)
|
|
304
301
|
}
|
package/tests/datetime.test.ts
CHANGED
|
@@ -131,6 +131,37 @@ describe(normalizeDatetime, () => {
|
|
|
131
131
|
expect(normalizeDatetime('Fri, 02 Jan 1999 12:34:56 GMT')).toEqual(
|
|
132
132
|
'1999-01-02T12:34:56.000Z',
|
|
133
133
|
)
|
|
134
|
+
expect(normalizeDatetime('0001-01-01T00:00:00+01:00')).toEqual(
|
|
135
|
+
'0000-12-31T23:00:00.000Z',
|
|
136
|
+
)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('accepts years 1-9', () => {
|
|
140
|
+
expect(normalizeDatetime('0009-12-31T23:59:59Z')).toEqual(
|
|
141
|
+
'0009-12-31T23:59:59.000Z',
|
|
142
|
+
)
|
|
143
|
+
expect(normalizeDatetime('0005-06-15T12:00:00Z')).toEqual(
|
|
144
|
+
'0005-06-15T12:00:00.000Z',
|
|
145
|
+
)
|
|
146
|
+
expect(normalizeDatetime('0001-01-01T00:00:00Z')).toEqual(
|
|
147
|
+
'0001-01-01T00:00:00.000Z',
|
|
148
|
+
)
|
|
149
|
+
expect(normalizeDatetime('0002-03-04T05:06:07.890Z')).toEqual(
|
|
150
|
+
'0002-03-04T05:06:07.890Z',
|
|
151
|
+
)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('accepts single-digit years with leading zeros', () => {
|
|
155
|
+
expect(normalizeDatetime('0007-01-01T00:00:00Z')).toEqual(
|
|
156
|
+
'0007-01-01T00:00:00.000Z',
|
|
157
|
+
)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('accepts year 1 with timezone offsets', () => {
|
|
161
|
+
// Year 1 with negative offset stays in valid range
|
|
162
|
+
expect(normalizeDatetime('0001-12-31T23:00:00-01:00')).toEqual(
|
|
163
|
+
'0002-01-01T00:00:00.000Z',
|
|
164
|
+
)
|
|
134
165
|
})
|
|
135
166
|
|
|
136
167
|
it('throws on invalid input', () => {
|
|
@@ -145,9 +176,6 @@ describe(normalizeDatetime, () => {
|
|
|
145
176
|
expect(() => normalizeDatetime('0000-01-01T00:00:00+01:00')).toThrow(
|
|
146
177
|
InvalidDatetimeError,
|
|
147
178
|
)
|
|
148
|
-
expect(() => normalizeDatetime('0001-01-01T00:00:00+01:00')).toThrow(
|
|
149
|
-
InvalidDatetimeError,
|
|
150
|
-
)
|
|
151
179
|
// 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
|
|
152
180
|
// UTC advances it to 10000-01-01T00:00:00Z, which is out of range
|
|
153
181
|
expect(() => normalizeDatetime('9999-12-31T23:59:00-00:01')).toThrow(
|