@mdbase-dev/connect-protocol 0.1.0-beta.31 → 0.1.0-beta.33
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/application-authorization.d.ts +4 -1
- package/dist/application-authorization.js +23 -5
- package/dist/capabilities.d.ts +1 -0
- package/dist/capabilities.js +1 -0
- package/dist/collection-setup.d.ts +83 -0
- package/dist/collection-setup.js +1 -0
- package/dist/compatibility.d.ts +26 -0
- package/dist/compatibility.js +35 -0
- package/dist/connect-problems.generated.d.ts +151 -2
- package/dist/connect-problems.generated.js +161 -0
- package/dist/files.d.ts +2 -0
- package/dist/index.d.ts +33 -14
- package/dist/index.js +16 -4
- package/dist/manifest.js +118 -5
- package/dist/mutation-fingerprint.d.ts +6 -0
- package/dist/mutation-fingerprint.js +116 -0
- package/dist/operations.d.ts +11 -6
- package/dist/operations.js +147 -5
- package/package.json +2 -2
- package/schemas/catalog.json +2 -1
- package/schemas/connect-problem.v1.schema.json +634 -32
- package/schemas/connect-problems.v1.catalog.json +78 -1
- package/schemas/connect-protocol.v1.schema.json +104 -14
- package/schemas/encrypted-relay.v1.schema.json +1 -1
- package/schemas/encrypted-relay.v2.schema.json +63 -0
- package/schemas/files.v1.schema.json +7 -2
- package/schemas/mdbase-app.schema.json +51 -0
- package/schemas/mdbase-query.v0.3.schema.json +171 -0
- package/schemas/operation-catalog.v1.json +42 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { mutationOperationIdentifier, operationInputSchemaVersion } from "./operations.js";
|
|
2
|
+
export const MUTATION_FINGERPRINT_SCHEMA_VERSION = 1;
|
|
3
|
+
export const MUTATION_FINGERPRINT_DOMAIN = "mdbase-connect mutation fingerprint v1\0";
|
|
4
|
+
const encoder = new TextEncoder();
|
|
5
|
+
export function canonicalMutationInput(input) {
|
|
6
|
+
return canonicalize(input, new Set());
|
|
7
|
+
}
|
|
8
|
+
export function mutationFingerprintTranscript(operation, input) {
|
|
9
|
+
const identifier = mutationOperationIdentifier(operation, input);
|
|
10
|
+
if (identifier === null)
|
|
11
|
+
throw new TypeError("The operation is not a canonical mutation.");
|
|
12
|
+
const identifierBytes = encoder.encode(identifier);
|
|
13
|
+
const inputBytes = encoder.encode(canonicalMutationInput(input));
|
|
14
|
+
const domainBytes = encoder.encode(MUTATION_FINGERPRINT_DOMAIN);
|
|
15
|
+
const transcript = new Uint8Array(domainBytes.length + 4 + 8 + identifierBytes.length + 4 + 8 + inputBytes.length);
|
|
16
|
+
const view = new DataView(transcript.buffer);
|
|
17
|
+
let offset = 0;
|
|
18
|
+
transcript.set(domainBytes, offset);
|
|
19
|
+
offset += domainBytes.length;
|
|
20
|
+
view.setUint32(offset, MUTATION_FINGERPRINT_SCHEMA_VERSION, false);
|
|
21
|
+
offset += 4;
|
|
22
|
+
offset = writeField(transcript, view, offset, identifierBytes);
|
|
23
|
+
view.setUint32(offset, operationInputSchemaVersion(operation, input), false);
|
|
24
|
+
offset += 4;
|
|
25
|
+
writeField(transcript, view, offset, inputBytes);
|
|
26
|
+
return transcript;
|
|
27
|
+
}
|
|
28
|
+
export async function mutationFingerprint(operation, input) {
|
|
29
|
+
const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", mutationFingerprintTranscript(operation, input).buffer));
|
|
30
|
+
return base64Url(digest);
|
|
31
|
+
}
|
|
32
|
+
function canonicalize(value, ancestors) {
|
|
33
|
+
if (value === null)
|
|
34
|
+
return "null";
|
|
35
|
+
if (value === true)
|
|
36
|
+
return "true";
|
|
37
|
+
if (value === false)
|
|
38
|
+
return "false";
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
assertUnicodeScalarString(value);
|
|
41
|
+
return JSON.stringify(value);
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === "number") {
|
|
44
|
+
if (!Number.isFinite(value))
|
|
45
|
+
throw new TypeError("Mutation input numbers must be finite.");
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
}
|
|
48
|
+
if (typeof value !== "object") {
|
|
49
|
+
throw new TypeError(`Mutation input contains unsupported ${typeof value}.`);
|
|
50
|
+
}
|
|
51
|
+
if (ancestors.has(value))
|
|
52
|
+
throw new TypeError("Mutation input must not contain cycles.");
|
|
53
|
+
ancestors.add(value);
|
|
54
|
+
try {
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
57
|
+
throw new TypeError("Mutation input must not contain symbol properties.");
|
|
58
|
+
}
|
|
59
|
+
const children = [];
|
|
60
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
61
|
+
if (!Object.hasOwn(value, index))
|
|
62
|
+
throw new TypeError("Mutation input arrays must not be sparse.");
|
|
63
|
+
children.push(canonicalize(value[index], ancestors));
|
|
64
|
+
}
|
|
65
|
+
return `[${children.join(",")}]`;
|
|
66
|
+
}
|
|
67
|
+
const prototype = Object.getPrototypeOf(value);
|
|
68
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
69
|
+
throw new TypeError("Mutation input objects must be plain JSON objects.");
|
|
70
|
+
}
|
|
71
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
72
|
+
throw new TypeError("Mutation input must not contain symbol properties.");
|
|
73
|
+
}
|
|
74
|
+
const entries = Object.entries(value);
|
|
75
|
+
for (const [key] of entries)
|
|
76
|
+
assertUnicodeScalarString(key);
|
|
77
|
+
entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
78
|
+
return `{${entries.map(([key, child]) => {
|
|
79
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
80
|
+
if (!descriptor || descriptor.get || descriptor.set) {
|
|
81
|
+
throw new TypeError("Mutation input must not contain accessor properties.");
|
|
82
|
+
}
|
|
83
|
+
return `${JSON.stringify(key)}:${canonicalize(child, ancestors)}`;
|
|
84
|
+
}).join(",")}}`;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
ancestors.delete(value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function assertUnicodeScalarString(value) {
|
|
91
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
92
|
+
const unit = value.charCodeAt(index);
|
|
93
|
+
if (unit >= 0xd800 && unit <= 0xdbff) {
|
|
94
|
+
const next = value.charCodeAt(index + 1);
|
|
95
|
+
if (!(next >= 0xdc00 && next <= 0xdfff)) {
|
|
96
|
+
throw new TypeError("Mutation input strings must not contain lone surrogates.");
|
|
97
|
+
}
|
|
98
|
+
index += 1;
|
|
99
|
+
}
|
|
100
|
+
else if (unit >= 0xdc00 && unit <= 0xdfff) {
|
|
101
|
+
throw new TypeError("Mutation input strings must not contain lone surrogates.");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function writeField(transcript, view, offset, value) {
|
|
106
|
+
view.setBigUint64(offset, BigInt(value.length), false);
|
|
107
|
+
offset += 8;
|
|
108
|
+
transcript.set(value, offset);
|
|
109
|
+
return offset + value.length;
|
|
110
|
+
}
|
|
111
|
+
function base64Url(value) {
|
|
112
|
+
return btoa(String.fromCharCode(...value))
|
|
113
|
+
.replaceAll("+", "-")
|
|
114
|
+
.replaceAll("/", "_")
|
|
115
|
+
.replace(/=+$/u, "");
|
|
116
|
+
}
|
package/dist/operations.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* compilation, and public types must all consume this value; do not maintain a
|
|
4
|
-
* second operation allow-list at an SDK or service boundary.
|
|
5
|
-
*/
|
|
6
|
-
export declare const COLLECTION_OPERATIONS: readonly ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "create_view_source", "update_view_source", "delete_view_source", "validate", "create", "update", "delete", "rename", "read_type", "create_type", "update_type", "assess_type_pack", "apply_type_pack", "list_timers", "put_timer", "cancel_timer", "reconcile_timers", "sync"];
|
|
1
|
+
export declare const OPERATION_CATALOG_VERSION: 1;
|
|
2
|
+
export declare const COLLECTION_OPERATIONS: readonly ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "create_view_source", "update_view_source", "delete_view_source", "validate", "create", "update", "delete", "rename", "read_type", "create_type", "update_type", "assess_type_pack", "apply_type_pack", "assess_collection_setup", "apply_collection_setup", "list_timers", "put_timer", "cancel_timer", "reconcile_timers", "sync"];
|
|
7
3
|
export type CollectionOperation = typeof COLLECTION_OPERATIONS[number];
|
|
4
|
+
export declare const FILE_CONTROL_MESSAGE_TYPES: readonly ["list_files", "open_file_upload", "open_file_download", "move_file", "delete_file", "get_file_transfer_status", "commit_file_upload", "abort_file_transfer"];
|
|
5
|
+
export type FileControlMessageType = typeof FILE_CONTROL_MESSAGE_TYPES[number];
|
|
6
|
+
export type EncryptedOperation = CollectionOperation | "file_control";
|
|
7
|
+
export declare const MUTATING_OPERATION_IDENTIFIERS: readonly ["create_view_source", "update_view_source", "delete_view_source", "create", "update", "delete", "rename", "create_type", "update_type", "apply_type_pack", "apply_collection_setup", "put_timer", "cancel_timer", "reconcile_timers", "sync:mutate", "file_control:open_file_upload", "file_control:move_file", "file_control:delete_file", "file_control:commit_file_upload", "file_control:abort_file_transfer"];
|
|
8
|
+
export type MutationOperationIdentifier = typeof MUTATING_OPERATION_IDENTIFIERS[number];
|
|
8
9
|
export declare function isCollectionOperation(value: string): value is CollectionOperation;
|
|
9
10
|
export declare function areCollectionOperations(values: readonly string[]): values is readonly CollectionOperation[];
|
|
11
|
+
export declare function isFileControlMessageType(value: string): value is FileControlMessageType;
|
|
12
|
+
export declare function operationInputSchemaVersion(operation: EncryptedOperation, input: unknown): number;
|
|
13
|
+
export declare function mutationOperationIdentifier(operation: EncryptedOperation, input: unknown): MutationOperationIdentifier | null;
|
|
14
|
+
export declare function isMutatingOperation(operation: EncryptedOperation, input: unknown): boolean;
|
package/dist/operations.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* compilation, and public types must all consume this value; do not maintain a
|
|
4
|
-
* second operation allow-list at an SDK or service boundary.
|
|
5
|
-
*/
|
|
1
|
+
// Generated by scripts/generate-operation-catalog.mjs. Do not edit directly.
|
|
2
|
+
export const OPERATION_CATALOG_VERSION = 1;
|
|
6
3
|
export const COLLECTION_OPERATIONS = [
|
|
7
4
|
"describe",
|
|
8
5
|
"changes",
|
|
@@ -24,16 +21,161 @@ export const COLLECTION_OPERATIONS = [
|
|
|
24
21
|
"update_type",
|
|
25
22
|
"assess_type_pack",
|
|
26
23
|
"apply_type_pack",
|
|
24
|
+
"assess_collection_setup",
|
|
25
|
+
"apply_collection_setup",
|
|
27
26
|
"list_timers",
|
|
28
27
|
"put_timer",
|
|
29
28
|
"cancel_timer",
|
|
30
29
|
"reconcile_timers",
|
|
31
30
|
"sync"
|
|
32
31
|
];
|
|
32
|
+
export const FILE_CONTROL_MESSAGE_TYPES = [
|
|
33
|
+
"list_files",
|
|
34
|
+
"open_file_upload",
|
|
35
|
+
"open_file_download",
|
|
36
|
+
"move_file",
|
|
37
|
+
"delete_file",
|
|
38
|
+
"get_file_transfer_status",
|
|
39
|
+
"commit_file_upload",
|
|
40
|
+
"abort_file_transfer"
|
|
41
|
+
];
|
|
42
|
+
const COLLECTION_MUTATION_RULES = {
|
|
43
|
+
"describe": "never",
|
|
44
|
+
"changes": "never",
|
|
45
|
+
"read": "never",
|
|
46
|
+
"query": "never",
|
|
47
|
+
"list_views": "never",
|
|
48
|
+
"execute_view": "never",
|
|
49
|
+
"read_view_source": "never",
|
|
50
|
+
"create_view_source": "always",
|
|
51
|
+
"update_view_source": "always",
|
|
52
|
+
"delete_view_source": "always",
|
|
53
|
+
"validate": "never",
|
|
54
|
+
"create": "always",
|
|
55
|
+
"update": "always",
|
|
56
|
+
"delete": "always",
|
|
57
|
+
"rename": "always",
|
|
58
|
+
"read_type": "never",
|
|
59
|
+
"create_type": "always",
|
|
60
|
+
"update_type": "always",
|
|
61
|
+
"assess_type_pack": "never",
|
|
62
|
+
"apply_type_pack": "always",
|
|
63
|
+
"assess_collection_setup": "never",
|
|
64
|
+
"apply_collection_setup": "always",
|
|
65
|
+
"list_timers": "never",
|
|
66
|
+
"put_timer": "always",
|
|
67
|
+
"cancel_timer": "always",
|
|
68
|
+
"reconcile_timers": "always",
|
|
69
|
+
"sync": "sync_action"
|
|
70
|
+
};
|
|
71
|
+
const COLLECTION_INPUT_SCHEMA_VERSIONS = {
|
|
72
|
+
"describe": 1,
|
|
73
|
+
"changes": 1,
|
|
74
|
+
"read": 1,
|
|
75
|
+
"query": 1,
|
|
76
|
+
"list_views": 1,
|
|
77
|
+
"execute_view": 1,
|
|
78
|
+
"read_view_source": 1,
|
|
79
|
+
"create_view_source": 1,
|
|
80
|
+
"update_view_source": 1,
|
|
81
|
+
"delete_view_source": 1,
|
|
82
|
+
"validate": 1,
|
|
83
|
+
"create": 1,
|
|
84
|
+
"update": 1,
|
|
85
|
+
"delete": 1,
|
|
86
|
+
"rename": 1,
|
|
87
|
+
"read_type": 1,
|
|
88
|
+
"create_type": 1,
|
|
89
|
+
"update_type": 1,
|
|
90
|
+
"assess_type_pack": 1,
|
|
91
|
+
"apply_type_pack": 1,
|
|
92
|
+
"assess_collection_setup": 1,
|
|
93
|
+
"apply_collection_setup": 1,
|
|
94
|
+
"list_timers": 1,
|
|
95
|
+
"put_timer": 1,
|
|
96
|
+
"cancel_timer": 1,
|
|
97
|
+
"reconcile_timers": 1,
|
|
98
|
+
"sync": 1
|
|
99
|
+
};
|
|
100
|
+
const FILE_CONTROL_INPUT_SCHEMA_VERSIONS = {
|
|
101
|
+
"list_files": 1,
|
|
102
|
+
"open_file_upload": 1,
|
|
103
|
+
"open_file_download": 1,
|
|
104
|
+
"move_file": 1,
|
|
105
|
+
"delete_file": 1,
|
|
106
|
+
"get_file_transfer_status": 1,
|
|
107
|
+
"commit_file_upload": 1,
|
|
108
|
+
"abort_file_transfer": 1
|
|
109
|
+
};
|
|
110
|
+
const MUTATING_FILE_CONTROL_MESSAGE_TYPES = new Set([
|
|
111
|
+
"open_file_upload",
|
|
112
|
+
"move_file",
|
|
113
|
+
"delete_file",
|
|
114
|
+
"commit_file_upload",
|
|
115
|
+
"abort_file_transfer"
|
|
116
|
+
]);
|
|
117
|
+
export const MUTATING_OPERATION_IDENTIFIERS = [
|
|
118
|
+
"create_view_source",
|
|
119
|
+
"update_view_source",
|
|
120
|
+
"delete_view_source",
|
|
121
|
+
"create",
|
|
122
|
+
"update",
|
|
123
|
+
"delete",
|
|
124
|
+
"rename",
|
|
125
|
+
"create_type",
|
|
126
|
+
"update_type",
|
|
127
|
+
"apply_type_pack",
|
|
128
|
+
"apply_collection_setup",
|
|
129
|
+
"put_timer",
|
|
130
|
+
"cancel_timer",
|
|
131
|
+
"reconcile_timers",
|
|
132
|
+
"sync:mutate",
|
|
133
|
+
"file_control:open_file_upload",
|
|
134
|
+
"file_control:move_file",
|
|
135
|
+
"file_control:delete_file",
|
|
136
|
+
"file_control:commit_file_upload",
|
|
137
|
+
"file_control:abort_file_transfer"
|
|
138
|
+
];
|
|
33
139
|
const COLLECTION_OPERATION_SET = new Set(COLLECTION_OPERATIONS);
|
|
140
|
+
const FILE_CONTROL_MESSAGE_TYPE_SET = new Set(FILE_CONTROL_MESSAGE_TYPES);
|
|
34
141
|
export function isCollectionOperation(value) {
|
|
35
142
|
return COLLECTION_OPERATION_SET.has(value);
|
|
36
143
|
}
|
|
37
144
|
export function areCollectionOperations(values) {
|
|
38
145
|
return values.every(isCollectionOperation);
|
|
39
146
|
}
|
|
147
|
+
export function isFileControlMessageType(value) {
|
|
148
|
+
return FILE_CONTROL_MESSAGE_TYPE_SET.has(value);
|
|
149
|
+
}
|
|
150
|
+
export function operationInputSchemaVersion(operation, input) {
|
|
151
|
+
if (operation === "file_control") {
|
|
152
|
+
const type = isObject(input) && typeof input.type === "string" ? input.type : "";
|
|
153
|
+
if (!isFileControlMessageType(type))
|
|
154
|
+
throw new Error(`Unknown file-control message: ${type}`);
|
|
155
|
+
return FILE_CONTROL_INPUT_SCHEMA_VERSIONS[type];
|
|
156
|
+
}
|
|
157
|
+
return COLLECTION_INPUT_SCHEMA_VERSIONS[operation];
|
|
158
|
+
}
|
|
159
|
+
export function mutationOperationIdentifier(operation, input) {
|
|
160
|
+
const object = isObject(input) ? input : {};
|
|
161
|
+
if (object.dry_run === true)
|
|
162
|
+
return null;
|
|
163
|
+
if (operation === "file_control") {
|
|
164
|
+
const type = typeof object.type === "string" ? object.type : "";
|
|
165
|
+
return MUTATING_FILE_CONTROL_MESSAGE_TYPES.has(type)
|
|
166
|
+
? `file_control:${type}`
|
|
167
|
+
: null;
|
|
168
|
+
}
|
|
169
|
+
const rule = COLLECTION_MUTATION_RULES[operation];
|
|
170
|
+
if (rule === "always")
|
|
171
|
+
return operation;
|
|
172
|
+
if (rule === "sync_action" && object.action === "mutate")
|
|
173
|
+
return "sync:mutate";
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
export function isMutatingOperation(operation, input) {
|
|
177
|
+
return mutationOperationIdentifier(operation, input) !== null;
|
|
178
|
+
}
|
|
179
|
+
function isObject(value) {
|
|
180
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
181
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mdbase-dev/connect-protocol",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.33",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
39
|
-
"test": "node --test test/*.test.mjs && node ../../scripts/generate-connect-problems.mjs --check",
|
|
39
|
+
"test": "node --test test/*.test.mjs && node ../../scripts/generate-connect-problems.mjs --check && node ../../scripts/generate-operation-catalog.mjs --check",
|
|
40
40
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/schemas/catalog.json
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"connect_protocol": "./connect-protocol.v1.schema.json",
|
|
8
8
|
"connect_problem": "./connect-problem.v1.schema.json",
|
|
9
9
|
"connect_problem_catalog": "./connect-problems.v1.catalog.json",
|
|
10
|
-
"
|
|
10
|
+
"mdbase_query_v0_3": "./mdbase-query.v0.3.schema.json",
|
|
11
|
+
"encrypted_relay": "./encrypted-relay.v2.schema.json",
|
|
11
12
|
"files": "./files.v1.schema.json",
|
|
12
13
|
"notification_webhook": "./notification-webhook.v1.schema.json",
|
|
13
14
|
"sync": "./sync.v1.schema.json"
|