@dxos/keys 0.9.0 → 0.9.1-main.c7dcc2e112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs.map +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs.map +2 -2
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/DXN.d.ts +23 -1
- package/dist/types/src/DXN.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/DXN.test.ts +38 -1
- package/src/DXN.ts +37 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/keys",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1-main.c7dcc2e112",
|
|
4
4
|
"description": "Key utils and definitions.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"ulidx": "^2.3.0",
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
30
|
+
"@dxos/invariant": "0.9.1-main.c7dcc2e112",
|
|
31
|
+
"@dxos/debug": "0.9.1-main.c7dcc2e112",
|
|
32
|
+
"@dxos/node-std": "0.9.1-main.c7dcc2e112"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"base32-decode": "^1.0.0",
|
package/src/DXN.test.ts
CHANGED
|
@@ -23,6 +23,40 @@ describe('DXN.isDXN', () => {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
describe('DXN.Name', () => {
|
|
27
|
+
test('accepts valid NSIDs — no type errors', () => {
|
|
28
|
+
// Three-segment minimum (first + middle + final), all camelCase.
|
|
29
|
+
DXN.make('a.b.c');
|
|
30
|
+
// Multi-segment, all camelCase.
|
|
31
|
+
DXN.make('org.dxos.type.calendar');
|
|
32
|
+
// Hyphen is allowed in a MIDDLE segment.
|
|
33
|
+
DXN.make('org.dxos.app-framework.event.startup');
|
|
34
|
+
// Versioned form.
|
|
35
|
+
DXN.make('org.dxos.type.calendar', '1.0.0');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('rejects invalid NSIDs — compile-time type errors', () => {
|
|
39
|
+
// Wrapped in a never-called arrow so the invalid calls are type-checked but
|
|
40
|
+
// never executed at runtime. If Name unexpectedly starts accepting any
|
|
41
|
+
// of these, the @ts-expect-error directive itself becomes a build error
|
|
42
|
+
// ("Unused '@ts-expect-error' directive"), causing the CI check to fail.
|
|
43
|
+
void (() => {
|
|
44
|
+
// No dots — single segment.
|
|
45
|
+
// @ts-expect-error
|
|
46
|
+
DXN.make('unknown');
|
|
47
|
+
// Hyphen in the FINAL segment.
|
|
48
|
+
// @ts-expect-error
|
|
49
|
+
DXN.make('com.example.type.registry-entry');
|
|
50
|
+
// Hyphen in the final segment, versioned.
|
|
51
|
+
// @ts-expect-error
|
|
52
|
+
DXN.make('com.example.type.registry-entry', '0.1.0');
|
|
53
|
+
// Common mistake: kebab-case activation event name.
|
|
54
|
+
// @ts-expect-error
|
|
55
|
+
DXN.make('org.dxos.app-framework.event.setup-react-surface');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
26
60
|
describe('DXN.make', () => {
|
|
27
61
|
test('produces unversioned DXN', ({ expect }) => {
|
|
28
62
|
expect(DXN.make('org.dxos.type.calendar')).toBe('dxn:org.dxos.type.calendar');
|
|
@@ -32,9 +66,12 @@ describe('DXN.make', () => {
|
|
|
32
66
|
expect(DXN.make('org.dxos.type.calendar', '1.0.0')).toBe('dxn:org.dxos.type.calendar:1.0.0');
|
|
33
67
|
});
|
|
34
68
|
|
|
35
|
-
test('throws on invalid NSID', ({ expect }) => {
|
|
69
|
+
test('throws on invalid NSID at runtime', ({ expect }) => {
|
|
70
|
+
// @ts-expect-error intentionally invalid NSIDs — verifying runtime throws
|
|
36
71
|
expect(() => DXN.make('not-a-valid-nsid')).toThrow();
|
|
72
|
+
// @ts-expect-error
|
|
37
73
|
expect(() => DXN.make('com.example.type.registry-entry')).toThrow();
|
|
74
|
+
// @ts-expect-error
|
|
38
75
|
expect(() => DXN.make('com.example.type.registry-entry', '0.1.0')).toThrow();
|
|
39
76
|
});
|
|
40
77
|
});
|
package/src/DXN.ts
CHANGED
|
@@ -30,6 +30,33 @@ const DXN_SPEC_REGEXP =
|
|
|
30
30
|
*/
|
|
31
31
|
export type DXN = URI.URI & { readonly __DXN: unique symbol };
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Compile-time validation for NSID strings (the `dxn:` prefix is absent here).
|
|
35
|
+
*
|
|
36
|
+
* Checks two rules expressible with template literal types:
|
|
37
|
+
* - Must contain at least one dot (multi-segment).
|
|
38
|
+
* - Final segment (after the last dot) must not contain a hyphen.
|
|
39
|
+
*
|
|
40
|
+
* TypeScript template literal inference is non-greedy: `${string}.${infer Rest}`
|
|
41
|
+
* always splits at the first dot. The type recurses until `Rest` has no more dots,
|
|
42
|
+
* at which point it is the true final segment and is checked for hyphens.
|
|
43
|
+
*
|
|
44
|
+
* Broad `string` passes through unchanged so that template-literal call sites
|
|
45
|
+
* whose prefix segment is `string` are not rejected — those are validated at
|
|
46
|
+
* runtime by the regex inside `parse`.
|
|
47
|
+
*/
|
|
48
|
+
export type Name<T extends string> = [string] extends [T]
|
|
49
|
+
? string
|
|
50
|
+
: T extends `${string}.${infer Rest}`
|
|
51
|
+
? Rest extends `${string}.${string}`
|
|
52
|
+
? [Name<Rest>] extends [never]
|
|
53
|
+
? never
|
|
54
|
+
: T
|
|
55
|
+
: Rest extends `${string}-${string}`
|
|
56
|
+
? never
|
|
57
|
+
: T
|
|
58
|
+
: never;
|
|
59
|
+
|
|
33
60
|
/**
|
|
34
61
|
* Cheap prefix check — does not validate the full DXN grammar.
|
|
35
62
|
* Sufficient for narrowing a URI to a DXN.
|
|
@@ -40,11 +67,19 @@ export const isDXN = (value: unknown): value is DXN => typeof value === 'string'
|
|
|
40
67
|
* Constructs a DXN from an NSID (and optional version). Throws if the result
|
|
41
68
|
* is not a valid DXN. Use `tryMake` for non-throwing string parsing.
|
|
42
69
|
*
|
|
70
|
+
* Static NSID strings are validated at compile time via {@link Name}:
|
|
71
|
+
* the final segment must be camelCase (no hyphens). Template-literal strings
|
|
72
|
+
* with a runtime prefix are accepted here but still validated at runtime.
|
|
73
|
+
*
|
|
43
74
|
* @example make('org.dxos.type.calendar') → 'dxn:org.dxos.type.calendar'
|
|
44
75
|
* @example make('org.dxos.type.calendar', '1.0.0') → 'dxn:org.dxos.type.calendar:1.0.0'
|
|
45
76
|
*/
|
|
46
|
-
export const make
|
|
47
|
-
|
|
77
|
+
export const make: {
|
|
78
|
+
<T extends string>(
|
|
79
|
+
nsid: [Name<T>] extends [never] ? `Invalid NSID "${T}": final segment must be camelCase (no hyphens)` : T,
|
|
80
|
+
version?: string,
|
|
81
|
+
): DXN;
|
|
82
|
+
} = (nsid: string, version?: string): DXN => parse(version != null ? `dxn:${nsid}:${version}` : `dxn:${nsid}`);
|
|
48
83
|
|
|
49
84
|
/**
|
|
50
85
|
* Parses a full DXN string. Returns undefined on failure.
|