@atcute/did-plc 0.1.7 → 0.3.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/README.md +83 -5
- package/dist/client.d.ts +68 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +127 -0
- package/dist/client.js.map +1 -0
- package/dist/data.d.ts +15 -9
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +97 -5
- package/dist/data.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/typedefs.d.ts +2 -0
- package/dist/typedefs.d.ts.map +1 -1
- package/dist/typedefs.js +21 -86
- package/dist/typedefs.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +22 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +36 -1
- package/dist/utils.js.map +1 -1
- package/lib/client.ts +195 -0
- package/lib/data.ts +120 -6
- package/lib/index.ts +1 -0
- package/lib/typedefs.ts +23 -101
- package/lib/types.ts +23 -0
- package/lib/utils.ts +44 -1
- package/package.json +15 -10
package/lib/typedefs.ts
CHANGED
|
@@ -42,10 +42,8 @@ const _unsignedLegacyCreateOperation = v.object({
|
|
|
42
42
|
prev: v.null(),
|
|
43
43
|
signingKey: didKeyString,
|
|
44
44
|
recoveryKey: didKeyString,
|
|
45
|
-
handle: v.string()
|
|
46
|
-
service: v
|
|
47
|
-
.string()
|
|
48
|
-
.assert((input) => input.length <= 512, `service endpoint too long (max 512 characters)`),
|
|
45
|
+
handle: v.string(),
|
|
46
|
+
service: v.string(),
|
|
49
47
|
}) satisfies v.Type<t.UnsignedLegacyCreateOperation>;
|
|
50
48
|
|
|
51
49
|
export const unsignedLegacyCreateOperation: v.Type<t.UnsignedLegacyCreateOperation> =
|
|
@@ -58,108 +56,17 @@ export const legacyCreateOperation: v.Type<t.LegacyCreateOperation> = _unsignedL
|
|
|
58
56
|
|
|
59
57
|
// #region plc_operation
|
|
60
58
|
export const service: v.Type<t.Service> = v.object({
|
|
61
|
-
type: v.string()
|
|
62
|
-
endpoint: v
|
|
63
|
-
.string()
|
|
64
|
-
.assert((input) => input.length <= 512, `service endpoint too long (max 512 characters)`),
|
|
59
|
+
type: v.string(),
|
|
60
|
+
endpoint: v.string(),
|
|
65
61
|
});
|
|
66
62
|
|
|
67
63
|
const _unsignedOperation = v.object({
|
|
68
64
|
type: v.literal('plc_operation'),
|
|
69
65
|
prev: v.string().nullable(),
|
|
70
|
-
rotationKeys: v.array(didKeyString)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return v.err(`missing rotation keys`);
|
|
75
|
-
} else if (length > 10) {
|
|
76
|
-
return v.err(`too many rotation keys (max 10 keys)`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (let i = 0; i < length; i++) {
|
|
80
|
-
const key = input[i];
|
|
81
|
-
|
|
82
|
-
for (let j = 0; j < i; j++) {
|
|
83
|
-
if (input[j] === key) {
|
|
84
|
-
return v.err({
|
|
85
|
-
message: `duplicate "${key}" rotation key`,
|
|
86
|
-
path: [i],
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return v.ok(input);
|
|
93
|
-
}),
|
|
94
|
-
verificationMethods: v.record(permissiveDidKeyString).chain((input) => {
|
|
95
|
-
const length = Object.keys(input).length;
|
|
96
|
-
|
|
97
|
-
if (length > 10) {
|
|
98
|
-
return v.err(`too many verification method entries (max 10)`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
for (const id in input) {
|
|
102
|
-
const key = input[id];
|
|
103
|
-
|
|
104
|
-
if (id.length > 32) {
|
|
105
|
-
return v.err({
|
|
106
|
-
message: `verification method id too long (max 32 characters)`,
|
|
107
|
-
path: [id],
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (key.length > 256) {
|
|
112
|
-
return v.err({
|
|
113
|
-
message: `verification method key too long (max 256 characters)`,
|
|
114
|
-
path: [id],
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return v.ok(input);
|
|
120
|
-
}),
|
|
121
|
-
alsoKnownAs: v
|
|
122
|
-
.array(v.string().assert((input) => input.length <= 256, `aka entry too long (max 256 characters)`))
|
|
123
|
-
.chain((input) => {
|
|
124
|
-
const length = input.length;
|
|
125
|
-
|
|
126
|
-
if (length > 10) {
|
|
127
|
-
return v.err(`too many aka entries (max 10)`);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
for (let i = 0; i < length; i++) {
|
|
131
|
-
const aka = input[i];
|
|
132
|
-
|
|
133
|
-
for (let j = 0; j < i; j++) {
|
|
134
|
-
if (input[j] === aka) {
|
|
135
|
-
return v.err({
|
|
136
|
-
message: `duplicate "${aka}" aka entry`,
|
|
137
|
-
path: [i],
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return v.ok(input);
|
|
144
|
-
}),
|
|
145
|
-
services: v.record(service).chain((input) => {
|
|
146
|
-
const length = Object.keys(input).length;
|
|
147
|
-
|
|
148
|
-
if (length > 10) {
|
|
149
|
-
return v.err(`too many service entries (max 10)`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
for (const id in input) {
|
|
153
|
-
if (id.length > 32) {
|
|
154
|
-
return v.err({
|
|
155
|
-
message: `service id too long (max 32 characters)`,
|
|
156
|
-
path: [id],
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return v.ok(input);
|
|
162
|
-
}),
|
|
66
|
+
rotationKeys: v.array(didKeyString),
|
|
67
|
+
verificationMethods: v.record(permissiveDidKeyString),
|
|
68
|
+
alsoKnownAs: v.array(v.string()),
|
|
69
|
+
services: v.record(service),
|
|
163
70
|
}) satisfies v.Type<t.UnsignedOperation>;
|
|
164
71
|
|
|
165
72
|
export const unsignedOperation: v.Type<t.UnsignedOperation> = _unsignedOperation;
|
|
@@ -213,3 +120,18 @@ export const indexedEntryLog: v.Type<t.IndexedEntryLog> = v
|
|
|
213
120
|
.tuple([_indexedEntry.extend({ operation: compatibleOperation })])
|
|
214
121
|
.concat(v.array(_indexedEntry.extend({ operation: operationOrTombstone })));
|
|
215
122
|
// #endregion
|
|
123
|
+
|
|
124
|
+
// #region Client response schemas
|
|
125
|
+
export const plcState: v.Type<t.PlcState> = v.object({
|
|
126
|
+
did: didPlcString,
|
|
127
|
+
rotationKeys: v.array(didKeyString),
|
|
128
|
+
verificationMethods: v.record(permissiveDidKeyString),
|
|
129
|
+
alsoKnownAs: v.array(v.string()),
|
|
130
|
+
services: v.record(service),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export const sequencedEntry: v.Type<t.SequencedEntry> = _indexedEntry.extend({
|
|
134
|
+
type: v.literal('sequenced_op'),
|
|
135
|
+
seq: v.number(),
|
|
136
|
+
});
|
|
137
|
+
// #endregion
|
package/lib/types.ts
CHANGED
|
@@ -71,3 +71,26 @@ export type IndexedEntryLog = [
|
|
|
71
71
|
genesis: IndexedEntry<CompatibleOperation>,
|
|
72
72
|
...IndexedEntry<OperationOrTombstone>[],
|
|
73
73
|
];
|
|
74
|
+
|
|
75
|
+
// #region client response types
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* current identity state derived from the did:plc operation log
|
|
79
|
+
*/
|
|
80
|
+
export interface PlcState {
|
|
81
|
+
did: DidPlcString;
|
|
82
|
+
rotationKeys: DidKeyString[];
|
|
83
|
+
verificationMethods: Record<string, DidKeyString>;
|
|
84
|
+
alsoKnownAs: string[];
|
|
85
|
+
services: Record<string, Service>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* operation entry with sequence number from /export endpoint
|
|
90
|
+
*/
|
|
91
|
+
export interface SequencedEntry extends IndexedEntry {
|
|
92
|
+
type: 'sequenced_op';
|
|
93
|
+
seq: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// #endregion
|
package/lib/utils.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as CBOR from '@atcute/cbor';
|
|
2
|
+
import type { PrivateKey } from '@atcute/crypto';
|
|
2
3
|
import { verifySigWithDidKey } from '@atcute/crypto';
|
|
3
|
-
import { fromBase64Url } from '@atcute/multibase';
|
|
4
|
+
import { fromBase64Url, toBase32, toBase64Url } from '@atcute/multibase';
|
|
5
|
+
import { toSha256 } from '@atcute/uint8array';
|
|
4
6
|
|
|
5
7
|
import * as t from './types.js';
|
|
6
8
|
|
|
@@ -22,6 +24,17 @@ export const wrapAtprotoPrefix = (str: string): string => {
|
|
|
22
24
|
return `at://${stripped}`;
|
|
23
25
|
};
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* derives the did:plc identifier from a genesis operation
|
|
29
|
+
* @param op signed genesis operation
|
|
30
|
+
* @returns the did:plc string
|
|
31
|
+
*/
|
|
32
|
+
export const deriveDidFromGenesisOp = async (op: t.CompatibleOperation): Promise<t.DidPlcString> => {
|
|
33
|
+
const opBytes = CBOR.encode(op);
|
|
34
|
+
const hash = await toSha256(opBytes);
|
|
35
|
+
return `did:plc:${toBase32(hash).slice(0, 24)}`;
|
|
36
|
+
};
|
|
37
|
+
|
|
25
38
|
export const normalizeOp = (op: t.CompatibleOperation): t.Operation => {
|
|
26
39
|
if (op.type === 'create') {
|
|
27
40
|
return {
|
|
@@ -64,3 +77,33 @@ export const isSignedOperationValid = async (
|
|
|
64
77
|
|
|
65
78
|
return null;
|
|
66
79
|
};
|
|
80
|
+
|
|
81
|
+
// #region signing operations
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* signs an unsigned plc operation
|
|
85
|
+
* @param op unsigned operation to sign
|
|
86
|
+
* @param key private key to sign with (must be one of the rotation keys)
|
|
87
|
+
* @returns signed operation
|
|
88
|
+
*/
|
|
89
|
+
export const signOperation = async (op: t.UnsignedOperation, key: PrivateKey): Promise<t.Operation> => {
|
|
90
|
+
const data = CBOR.encode(op);
|
|
91
|
+
const sig = await key.sign(data);
|
|
92
|
+
|
|
93
|
+
return { ...op, sig: toBase64Url(sig) };
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* signs an unsigned plc tombstone
|
|
98
|
+
* @param op unsigned tombstone to sign
|
|
99
|
+
* @param key private key to sign with (must be one of the rotation keys)
|
|
100
|
+
* @returns signed tombstone
|
|
101
|
+
*/
|
|
102
|
+
export const signTombstone = async (op: t.UnsignedTombstone, key: PrivateKey): Promise<t.Tombstone> => {
|
|
103
|
+
const data = CBOR.encode(op);
|
|
104
|
+
const sig = await key.sign(data);
|
|
105
|
+
|
|
106
|
+
return { ...op, sig: toBase64Url(sig) };
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// #endregion
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@atcute/did-plc",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "validations, type definitions and schemas for did:plc operations",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"atproto",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"url": "https://github.com/mary-ext/atcute",
|
|
14
14
|
"directory": "packages/identity/did-plc"
|
|
15
15
|
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
16
19
|
"files": [
|
|
17
20
|
"dist/",
|
|
18
21
|
"lib/",
|
|
@@ -24,21 +27,23 @@
|
|
|
24
27
|
},
|
|
25
28
|
"sideEffects": false,
|
|
26
29
|
"devDependencies": {
|
|
27
|
-
"@
|
|
30
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
31
|
+
"vitest": "^4.0.16"
|
|
28
32
|
},
|
|
29
33
|
"dependencies": {
|
|
30
34
|
"@badrap/valita": "^0.4.6",
|
|
31
|
-
"@atcute/cbor": "^2.2.
|
|
32
|
-
"@atcute/
|
|
33
|
-
"@atcute/
|
|
34
|
-
"@atcute/identity": "^1.1.
|
|
35
|
-
"@atcute/lexicons": "^1.2.
|
|
35
|
+
"@atcute/cbor": "^2.2.8",
|
|
36
|
+
"@atcute/cid": "^2.2.6",
|
|
37
|
+
"@atcute/crypto": "^2.3.0",
|
|
38
|
+
"@atcute/identity": "^1.1.3",
|
|
39
|
+
"@atcute/lexicons": "^1.2.6",
|
|
36
40
|
"@atcute/multibase": "^1.1.6",
|
|
37
|
-
"@atcute/uint8array": "^1.0.
|
|
41
|
+
"@atcute/uint8array": "^1.0.6",
|
|
42
|
+
"@atcute/util-fetch": "^1.0.4"
|
|
38
43
|
},
|
|
39
44
|
"scripts": {
|
|
40
|
-
"build": "
|
|
41
|
-
"test": "
|
|
45
|
+
"build": "tsgo --project tsconfig.build.json",
|
|
46
|
+
"test": "vitest",
|
|
42
47
|
"prepublish": "rm -rf dist; pnpm run build"
|
|
43
48
|
}
|
|
44
49
|
}
|