@mdbase-dev/connect-protocol 0.1.0-beta.94 → 0.1.0-beta.95
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 +2 -2
- package/dist/capabilities.d.ts +34 -15
- package/dist/capabilities.js +77 -37
- package/dist/compatibility.d.ts +3 -3
- package/dist/compatibility.js +5 -4
- package/dist/files.d.ts +14 -0
- package/dist/files.js +9 -0
- package/dist/index.d.ts +18 -3
- package/dist/index.js +2 -0
- package/dist/manifest.d.ts +18 -1
- package/dist/manifest.js +65 -2
- package/package.json +2 -2
- package/schemas/application-capability-catalog.v1.json +40 -0
- package/schemas/application-capability-catalog.v2.json +68 -0
- package/schemas/connect-protocol.v1.schema.json +1 -0
- package/schemas/mdbase-app.legacy-v1.schema.json +405 -0
- package/schemas/mdbase-app.schema.json +19 -43
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApplicationFileRequest } from "./files.js";
|
|
2
2
|
import { type CollectionOperation } from "./operations.js";
|
|
3
3
|
import type { AuthorizationBindingProtocolVersion, ConnectContractRequirements } from "./compatibility.js";
|
|
4
4
|
export type ApplicationAuthorizationFlow = "authorization_code" | "device_code";
|
|
@@ -21,7 +21,7 @@ export interface ApplicationAuthorizationBinding {
|
|
|
21
21
|
code_challenge: string;
|
|
22
22
|
contracts: ConnectContractRequirements;
|
|
23
23
|
requested_operations: CollectionOperation[];
|
|
24
|
-
requested_files?:
|
|
24
|
+
requested_files?: ApplicationFileRequest;
|
|
25
25
|
collection_id?: string;
|
|
26
26
|
}
|
|
27
27
|
export interface ApplicationAuthorizationProof {
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import type { CollectionOperation } from "./operations.js";
|
|
2
|
-
export declare const APPLICATION_CAPABILITY_CONTRACT_VERSION:
|
|
3
|
-
/**
|
|
4
|
-
* Stable application-facing abilities. Applications declare intent in these
|
|
5
|
-
* terms; Connect alone translates them into wire-level collection operations.
|
|
6
|
-
*/
|
|
2
|
+
export declare const APPLICATION_CAPABILITY_CONTRACT_VERSION: 2;
|
|
7
3
|
export declare const APPLICATION_CAPABILITY_DEFINITIONS: {
|
|
4
|
+
readonly "collection.read": readonly ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "validate", "read_type"];
|
|
5
|
+
readonly "records.create": readonly ["create"];
|
|
6
|
+
readonly "records.edit": readonly ["update", "rename"];
|
|
7
|
+
readonly "records.delete": readonly ["delete"];
|
|
8
|
+
readonly "views.manage": readonly ["create_view_source", "update_view_source", "delete_view_source"];
|
|
9
|
+
readonly "definitions.manage": readonly ["create_type", "update_type", "assess_type_pack", "apply_type_pack"];
|
|
10
|
+
readonly "background.schedule": readonly ["list_timers", "put_timer", "cancel_timer", "reconcile_timers"];
|
|
11
|
+
readonly "offline.replica": readonly ["sync"];
|
|
12
|
+
};
|
|
13
|
+
export declare const APPLICATION_SETUP_OPERATIONS: readonly ["assess_collection_setup", "apply_collection_setup"];
|
|
14
|
+
export type ApplicationCapabilityId = keyof typeof APPLICATION_CAPABILITY_DEFINITIONS;
|
|
15
|
+
export interface ApplicationCapabilityRequirements {
|
|
16
|
+
contract_version: typeof APPLICATION_CAPABILITY_CONTRACT_VERSION;
|
|
17
|
+
required: ApplicationCapabilityId[];
|
|
18
|
+
optional?: ApplicationCapabilityId[];
|
|
19
|
+
}
|
|
20
|
+
export declare function operationsForApplicationCapabilities(requirements: ApplicationCapabilityRequirements, options?: {
|
|
21
|
+
includeOptional?: boolean;
|
|
22
|
+
}): CollectionOperation[];
|
|
23
|
+
export declare function capabilityOperations(capability: ApplicationCapabilityId): CollectionOperation[];
|
|
24
|
+
export declare function applicationOperationSelectionIsAtomic(requirements: ApplicationCapabilityRequirements, operations: readonly string[]): boolean;
|
|
25
|
+
export declare const APPLICATION_CAPABILITY_V1_CONTRACT_VERSION: 1;
|
|
26
|
+
export declare const APPLICATION_CAPABILITY_V1_DEFINITIONS: Readonly<{
|
|
8
27
|
readonly "collection.inspect": readonly ["describe"];
|
|
9
28
|
readonly "records.watch": readonly ["changes"];
|
|
10
29
|
readonly "records.read": readonly ["read"];
|
|
@@ -39,14 +58,14 @@ export declare const APPLICATION_CAPABILITY_DEFINITIONS: {
|
|
|
39
58
|
readonly "files.replace": readonly [];
|
|
40
59
|
readonly "files.move": readonly [];
|
|
41
60
|
readonly "files.delete": readonly [];
|
|
42
|
-
}
|
|
43
|
-
export type
|
|
44
|
-
export interface
|
|
45
|
-
contract_version: typeof
|
|
46
|
-
required:
|
|
47
|
-
optional?:
|
|
61
|
+
}>;
|
|
62
|
+
export type LegacyApplicationCapabilityId = keyof typeof APPLICATION_CAPABILITY_V1_DEFINITIONS;
|
|
63
|
+
export interface LegacyApplicationCapabilityRequirements {
|
|
64
|
+
contract_version: typeof APPLICATION_CAPABILITY_V1_CONTRACT_VERSION;
|
|
65
|
+
required: LegacyApplicationCapabilityId[];
|
|
66
|
+
optional?: LegacyApplicationCapabilityId[];
|
|
48
67
|
}
|
|
49
|
-
export declare function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export declare function
|
|
68
|
+
export declare function capabilityOperationsForContractVersion(contractVersion: number, capability: string): CollectionOperation[] | undefined;
|
|
69
|
+
/** Fresh issuance only; never use this ceiling to reject retained authority or terminal replay. */
|
|
70
|
+
export declare const FRESH_APPLICATION_AUTHORIZATION_VERSIONS: readonly [1];
|
|
71
|
+
export declare function permitsFreshApplicationAuthorization(version: number): boolean;
|
package/dist/capabilities.js
CHANGED
|
@@ -1,44 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Stable application-facing abilities. Applications declare intent in these
|
|
4
|
-
* terms; Connect alone translates them into wire-level collection operations.
|
|
5
|
-
*/
|
|
1
|
+
// Generated by scripts/generate-application-capabilities.mjs. Do not edit directly.
|
|
2
|
+
export const APPLICATION_CAPABILITY_CONTRACT_VERSION = 2;
|
|
6
3
|
export const APPLICATION_CAPABILITY_DEFINITIONS = {
|
|
7
|
-
"collection.
|
|
8
|
-
"records.watch": ["changes"],
|
|
9
|
-
"records.read": ["read"],
|
|
10
|
-
"records.query": ["query"],
|
|
11
|
-
"records.validate": ["validate"],
|
|
4
|
+
"collection.read": ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "validate", "read_type"],
|
|
12
5
|
"records.create": ["create"],
|
|
13
|
-
"records.
|
|
6
|
+
"records.edit": ["update", "rename"],
|
|
14
7
|
"records.delete": ["delete"],
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"views.source.create": ["create_view_source"],
|
|
20
|
-
"views.source.update": ["update_view_source"],
|
|
21
|
-
"views.source.delete": ["delete_view_source"],
|
|
22
|
-
"definitions.contracts.current": [],
|
|
23
|
-
"definitions.read": ["read_type"],
|
|
24
|
-
"definitions.create": ["create_type"],
|
|
25
|
-
"definitions.update": ["update_type"],
|
|
26
|
-
"definitions.type-pack.inspect": ["assess_type_pack"],
|
|
27
|
-
"definitions.type-pack.apply": ["assess_type_pack", "apply_type_pack"],
|
|
28
|
-
"collection.setup.apply": ["assess_collection_setup", "apply_collection_setup"],
|
|
29
|
-
"timers.list": ["list_timers"],
|
|
30
|
-
"timers.put": ["put_timer"],
|
|
31
|
-
"timers.cancel": ["cancel_timer"],
|
|
32
|
-
"timers.reconcile": ["reconcile_timers"],
|
|
33
|
-
"sync.offline-replica": ["sync"],
|
|
34
|
-
"notifications.background-delivery": [],
|
|
35
|
-
"files.list": [],
|
|
36
|
-
"files.read": [],
|
|
37
|
-
"files.add": [],
|
|
38
|
-
"files.replace": [],
|
|
39
|
-
"files.move": [],
|
|
40
|
-
"files.delete": []
|
|
8
|
+
"views.manage": ["create_view_source", "update_view_source", "delete_view_source"],
|
|
9
|
+
"definitions.manage": ["create_type", "update_type", "assess_type_pack", "apply_type_pack"],
|
|
10
|
+
"background.schedule": ["list_timers", "put_timer", "cancel_timer", "reconcile_timers"],
|
|
11
|
+
"offline.replica": ["sync"]
|
|
41
12
|
};
|
|
13
|
+
export const APPLICATION_SETUP_OPERATIONS = [
|
|
14
|
+
"assess_collection_setup",
|
|
15
|
+
"apply_collection_setup"
|
|
16
|
+
];
|
|
42
17
|
export function operationsForApplicationCapabilities(requirements, options = {}) {
|
|
43
18
|
const capabilities = options.includeOptional === false
|
|
44
19
|
? requirements.required
|
|
@@ -48,3 +23,68 @@ export function operationsForApplicationCapabilities(requirements, options = {})
|
|
|
48
23
|
export function capabilityOperations(capability) {
|
|
49
24
|
return [...APPLICATION_CAPABILITY_DEFINITIONS[capability]];
|
|
50
25
|
}
|
|
26
|
+
export function applicationOperationSelectionIsAtomic(requirements, operations) {
|
|
27
|
+
const selected = new Set(operations);
|
|
28
|
+
const declared = [...requirements.required, ...(requirements.optional ?? [])];
|
|
29
|
+
const allowed = new Set(declared.flatMap((capability) => APPLICATION_CAPABILITY_DEFINITIONS[capability]));
|
|
30
|
+
if ([...selected].some((operation) => !allowed.has(operation))) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return declared.every((capability) => {
|
|
34
|
+
const capabilityOperations = APPLICATION_CAPABILITY_DEFINITIONS[capability];
|
|
35
|
+
const count = capabilityOperations.filter((operation) => selected.has(operation)).length;
|
|
36
|
+
return requirements.required.includes(capability)
|
|
37
|
+
? count === capabilityOperations.length
|
|
38
|
+
: count === 0 || count === capabilityOperations.length;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Frozen predecessor catalog. Lookup availability does not imply declaration acceptance.
|
|
42
|
+
export const APPLICATION_CAPABILITY_V1_CONTRACT_VERSION = 1;
|
|
43
|
+
export const APPLICATION_CAPABILITY_V1_DEFINITIONS = Object.freeze({
|
|
44
|
+
"collection.inspect": Object.freeze(["describe"]),
|
|
45
|
+
"records.watch": Object.freeze(["changes"]),
|
|
46
|
+
"records.read": Object.freeze(["read"]),
|
|
47
|
+
"records.query": Object.freeze(["query"]),
|
|
48
|
+
"records.validate": Object.freeze(["validate"]),
|
|
49
|
+
"records.create": Object.freeze(["create"]),
|
|
50
|
+
"records.update": Object.freeze(["update"]),
|
|
51
|
+
"records.delete": Object.freeze(["delete"]),
|
|
52
|
+
"records.rename": Object.freeze(["rename"]),
|
|
53
|
+
"views.list": Object.freeze(["list_views"]),
|
|
54
|
+
"views.execute": Object.freeze(["execute_view"]),
|
|
55
|
+
"views.source.read": Object.freeze(["read_view_source"]),
|
|
56
|
+
"views.source.create": Object.freeze(["create_view_source"]),
|
|
57
|
+
"views.source.update": Object.freeze(["update_view_source"]),
|
|
58
|
+
"views.source.delete": Object.freeze(["delete_view_source"]),
|
|
59
|
+
"definitions.contracts.current": Object.freeze([]),
|
|
60
|
+
"definitions.read": Object.freeze(["read_type"]),
|
|
61
|
+
"definitions.create": Object.freeze(["create_type"]),
|
|
62
|
+
"definitions.update": Object.freeze(["update_type"]),
|
|
63
|
+
"definitions.type-pack.inspect": Object.freeze(["assess_type_pack"]),
|
|
64
|
+
"definitions.type-pack.apply": Object.freeze(["assess_type_pack", "apply_type_pack"]),
|
|
65
|
+
"collection.setup.apply": Object.freeze(["assess_collection_setup", "apply_collection_setup"]),
|
|
66
|
+
"timers.list": Object.freeze(["list_timers"]),
|
|
67
|
+
"timers.put": Object.freeze(["put_timer"]),
|
|
68
|
+
"timers.cancel": Object.freeze(["cancel_timer"]),
|
|
69
|
+
"timers.reconcile": Object.freeze(["reconcile_timers"]),
|
|
70
|
+
"sync.offline-replica": Object.freeze(["sync"]),
|
|
71
|
+
"notifications.background-delivery": Object.freeze([]),
|
|
72
|
+
"files.list": Object.freeze([]),
|
|
73
|
+
"files.read": Object.freeze([]),
|
|
74
|
+
"files.add": Object.freeze([]),
|
|
75
|
+
"files.replace": Object.freeze([]),
|
|
76
|
+
"files.move": Object.freeze([]),
|
|
77
|
+
"files.delete": Object.freeze([])
|
|
78
|
+
});
|
|
79
|
+
export function capabilityOperationsForContractVersion(contractVersion, capability) {
|
|
80
|
+
const definitions = contractVersion === 1 ? APPLICATION_CAPABILITY_V1_DEFINITIONS
|
|
81
|
+
: contractVersion === 2 ? APPLICATION_CAPABILITY_DEFINITIONS : undefined;
|
|
82
|
+
if (!definitions || !Object.hasOwn(definitions, capability))
|
|
83
|
+
return undefined;
|
|
84
|
+
return [...definitions[capability]];
|
|
85
|
+
}
|
|
86
|
+
/** Fresh issuance only; never use this ceiling to reject retained authority or terminal replay. */
|
|
87
|
+
export const FRESH_APPLICATION_AUTHORIZATION_VERSIONS = Object.freeze([1]);
|
|
88
|
+
export function permitsFreshApplicationAuthorization(version) {
|
|
89
|
+
return FRESH_APPLICATION_AUTHORIZATION_VERSIONS.some((supported) => supported === version);
|
|
90
|
+
}
|
package/dist/compatibility.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApplicationFileRequest } from "./files.js";
|
|
2
2
|
import { type CollectionOperation } from "./operations.js";
|
|
3
3
|
export declare const OPERATION_TRANSPORT_PROTOCOL_VERSION: 3;
|
|
4
4
|
export declare const LEGACY_OPERATION_TRANSPORT_PROTOCOL_VERSION: 2;
|
|
@@ -8,7 +8,7 @@ export declare const AUTHORIZATION_BINDING_PROTOCOL_VERSION: 5;
|
|
|
8
8
|
export declare const LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION: 4;
|
|
9
9
|
export declare const SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS: readonly [5, 4];
|
|
10
10
|
export type AuthorizationBindingProtocolVersion = typeof SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS[number];
|
|
11
|
-
export declare const SEMANTIC_CAPABILITY_CONTRACT_VERSION:
|
|
11
|
+
export declare const SEMANTIC_CAPABILITY_CONTRACT_VERSION: 2;
|
|
12
12
|
export declare const DURABLE_MUTATION_CONTRACT_VERSION: 1;
|
|
13
13
|
export interface ConnectContractRequirements {
|
|
14
14
|
operation_transport: number;
|
|
@@ -30,7 +30,7 @@ export declare const CONNECT_CONTRACT_SUPPORT: ConnectContractSupport;
|
|
|
30
30
|
* Produce the exact contract ceiling signed during authorization. `sync` is
|
|
31
31
|
* conservatively mutation-capable because its action is selected per request.
|
|
32
32
|
*/
|
|
33
|
-
export declare function authorizationContractRequirements(operations: readonly CollectionOperation[], files?:
|
|
33
|
+
export declare function authorizationContractRequirements(operations: readonly CollectionOperation[], files?: ApplicationFileRequest, operationTransportRecovery?: readonly OperationTransportProtocolVersion[], semanticCapabilityContractVersion?: 1 | 2): ConnectContractRequirements;
|
|
34
34
|
export declare function supportsContractRequirements(required: ConnectContractRequirements, supported: ConnectContractSupport, durableMutation: boolean): boolean;
|
|
35
35
|
export declare function isSupportedOperationTransport(version: number): version is OperationTransportProtocolVersion;
|
|
36
36
|
export declare function isSupportedAuthorizationBinding(version: number): version is AuthorizationBindingProtocolVersion;
|
package/dist/compatibility.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { APPLICATION_CAPABILITY_CONTRACT_VERSION } from "./capabilities.js";
|
|
1
2
|
import { isMutatingOperation } from "./operations.js";
|
|
2
3
|
export const OPERATION_TRANSPORT_PROTOCOL_VERSION = 3;
|
|
3
4
|
export const LEGACY_OPERATION_TRANSPORT_PROTOCOL_VERSION = 2;
|
|
@@ -11,23 +12,23 @@ export const SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS = [
|
|
|
11
12
|
AUTHORIZATION_BINDING_PROTOCOL_VERSION,
|
|
12
13
|
LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION
|
|
13
14
|
];
|
|
14
|
-
export const SEMANTIC_CAPABILITY_CONTRACT_VERSION =
|
|
15
|
+
export const SEMANTIC_CAPABILITY_CONTRACT_VERSION = APPLICATION_CAPABILITY_CONTRACT_VERSION;
|
|
15
16
|
export const DURABLE_MUTATION_CONTRACT_VERSION = 1;
|
|
16
17
|
export const CONNECT_CONTRACT_SUPPORT = {
|
|
17
18
|
operation_transport: [...SUPPORTED_OPERATION_TRANSPORT_PROTOCOL_VERSIONS],
|
|
18
19
|
authorization_binding: [...SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS],
|
|
19
|
-
semantic_capabilities: [SEMANTIC_CAPABILITY_CONTRACT_VERSION],
|
|
20
|
+
semantic_capabilities: [SEMANTIC_CAPABILITY_CONTRACT_VERSION, 1],
|
|
20
21
|
durable_mutation: [DURABLE_MUTATION_CONTRACT_VERSION]
|
|
21
22
|
};
|
|
22
23
|
/**
|
|
23
24
|
* Produce the exact contract ceiling signed during authorization. `sync` is
|
|
24
25
|
* conservatively mutation-capable because its action is selected per request.
|
|
25
26
|
*/
|
|
26
|
-
export function authorizationContractRequirements(operations, files, operationTransportRecovery = []) {
|
|
27
|
+
export function authorizationContractRequirements(operations, files, operationTransportRecovery = [], semanticCapabilityContractVersion = APPLICATION_CAPABILITY_CONTRACT_VERSION) {
|
|
27
28
|
const requirements = {
|
|
28
29
|
operation_transport: OPERATION_TRANSPORT_PROTOCOL_VERSION,
|
|
29
30
|
authorization_binding: AUTHORIZATION_BINDING_PROTOCOL_VERSION,
|
|
30
|
-
semantic_capabilities:
|
|
31
|
+
semantic_capabilities: semanticCapabilityContractVersion
|
|
31
32
|
};
|
|
32
33
|
const recovery = [...new Set(operationTransportRecovery)]
|
|
33
34
|
.filter((version) => version !== OPERATION_TRANSPORT_PROTOCOL_VERSION)
|
package/dist/files.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** Frozen predecessor file declaration; never translate this into v2 intent. */
|
|
2
|
+
export interface LegacyApplicationFileRequirement {
|
|
3
|
+
actions: FileAction[];
|
|
4
|
+
scope: FileScope;
|
|
5
|
+
}
|
|
1
6
|
export declare const FILE_PROTOCOL_VERSION: 1;
|
|
2
7
|
export declare const FILE_TRANSFER_PROTOCOL_VERSION: 1;
|
|
3
8
|
export declare const DEFAULT_FILE_CHUNK_BYTES: number;
|
|
@@ -43,9 +48,18 @@ export type FileScope = {
|
|
|
43
48
|
};
|
|
44
49
|
/** File intent declared by an application before an authority is selected. */
|
|
45
50
|
export interface ApplicationFileRequirement {
|
|
51
|
+
required: FileAction[];
|
|
52
|
+
optional?: FileAction[];
|
|
53
|
+
scope: FileScope;
|
|
54
|
+
}
|
|
55
|
+
/** Exact file actions signed into an authorization request. */
|
|
56
|
+
export interface ApplicationFileRequest {
|
|
46
57
|
actions: FileAction[];
|
|
47
58
|
scope: FileScope;
|
|
48
59
|
}
|
|
60
|
+
export declare function applicationFileRequest(requirement: ApplicationFileRequirement, options?: {
|
|
61
|
+
includeOptional?: boolean;
|
|
62
|
+
}): ApplicationFileRequest;
|
|
49
63
|
export interface FileCapability {
|
|
50
64
|
kind: "files";
|
|
51
65
|
protocol_version: 1;
|
package/dist/files.js
CHANGED
|
@@ -21,6 +21,15 @@ const RELAY_FILE_MAGIC_BYTES = new Uint8Array([0x4d, 0x44, 0x42, 0x52]);
|
|
|
21
21
|
const RELAY_FILE_FRAME_VERSION = 1;
|
|
22
22
|
const RELAY_FILE_FRAME_FLAGS = 0;
|
|
23
23
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
24
|
+
export function applicationFileRequest(requirement, options = {}) {
|
|
25
|
+
return {
|
|
26
|
+
actions: [...new Set([
|
|
27
|
+
...requirement.required,
|
|
28
|
+
...(options.includeOptional === false ? [] : requirement.optional ?? [])
|
|
29
|
+
])],
|
|
30
|
+
scope: structuredClone(requirement.scope)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
24
33
|
export class FileFrameError extends Error {
|
|
25
34
|
code;
|
|
26
35
|
constructor(code, message) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ConnectProblem } from "./connect-problems.generated.js";
|
|
2
|
-
import type { ApplicationFileRequirement, CollectionFileDescriptor, FileCapability } from "./files.js";
|
|
2
|
+
import type { ApplicationFileRequirement, LegacyApplicationFileRequirement, CollectionFileDescriptor, FileCapability } from "./files.js";
|
|
3
3
|
import type { CollectionOperation } from "./operations.js";
|
|
4
|
-
import type { ApplicationCapabilityRequirements } from "./capabilities.js";
|
|
4
|
+
import type { ApplicationCapabilityRequirements, LegacyApplicationCapabilityRequirements } from "./capabilities.js";
|
|
5
5
|
import type { ContractRequirement, ContractSetupChoice, TypePackProvision } from "./type-packs.js";
|
|
6
6
|
import type { ConfigurationProvision, ConfigurationRequirement } from "./collection-setup.js";
|
|
7
7
|
import type { ApplicationAuthorizationProof } from "./application-authorization.js";
|
|
@@ -32,7 +32,8 @@ export declare const POLICY_FRESHNESS_LEASE_MINIMUM_CONNECTOR_VERSION: "0.1.0-be
|
|
|
32
32
|
export declare const HOSTED_PROVIDER_REQUIRED_CAPABILITIES: readonly ["durable-mutation-journal-v1", "durable-file-lifecycle-v1"];
|
|
33
33
|
export declare const HOSTED_CANDIDATE_B_ACTIVATION_CAPABILITY: "candidate-b-activation-v1";
|
|
34
34
|
export declare const HOSTED_PROVIDER_CAPABILITIES: readonly ["durable-mutation-journal-v1", "durable-file-lifecycle-v1", "mutation-replay-after-credential-retirement-v1", "candidate-b-activation-v1"];
|
|
35
|
-
export declare const
|
|
35
|
+
export declare const APPLICATION_DECLARATION_EVIDENCE_CAPABILITY: "application-declaration-evidence-v1";
|
|
36
|
+
export declare const RELAY_CAPABILITIES: readonly ["application-declaration-evidence-v1", "application-authorization-v4", "authorization-activation", "encrypted-relay", "policy-ack", "policy-freshness-lease-v1", "application-authorization-v5", "contract-setup-v1", "file-relay-v1", "protocol-usage-report-v1"];
|
|
36
37
|
export declare const AUTHORITY_PROOF_VERSION: 1;
|
|
37
38
|
export declare const AUTHORITY_PROOF_ALGORITHM: "P256-SHA256";
|
|
38
39
|
export declare const AUTHORITY_PROOF_DOMAIN: "mdbase-authority-request-proof-v1";
|
|
@@ -179,6 +180,18 @@ export interface ApplicationRequirements {
|
|
|
179
180
|
/** First-class non-Markdown file access requested independently of records. */
|
|
180
181
|
files?: ApplicationFileRequirement;
|
|
181
182
|
}
|
|
183
|
+
export type LegacyApplicationRequirements = Omit<ApplicationRequirements, "capabilities" | "files" | "access"> & {
|
|
184
|
+
access: "full_collection";
|
|
185
|
+
capabilities?: LegacyApplicationCapabilityRequirements;
|
|
186
|
+
files?: LegacyApplicationFileRequirement;
|
|
187
|
+
};
|
|
188
|
+
export type LegacyMdbaseWebAppManifest = Omit<MdbaseWebAppManifest, "requirements"> & {
|
|
189
|
+
requirements: LegacyApplicationRequirements;
|
|
190
|
+
};
|
|
191
|
+
export type LegacyMdbasePortableAppManifest = Omit<MdbasePortableAppManifest, "requirements"> & {
|
|
192
|
+
requirements: LegacyApplicationRequirements;
|
|
193
|
+
};
|
|
194
|
+
export type LegacyMdbaseAppManifest = LegacyMdbaseWebAppManifest | LegacyMdbasePortableAppManifest;
|
|
182
195
|
export interface ApplicationProvisions {
|
|
183
196
|
type_packs: TypePackProvision[];
|
|
184
197
|
/** Narrow semantic changes that may satisfy configuration requirements. */
|
|
@@ -234,6 +247,8 @@ export type MdbaseOperationResponse<Result = unknown> = {
|
|
|
234
247
|
problem: ConnectProblem;
|
|
235
248
|
};
|
|
236
249
|
export interface GrantPolicy {
|
|
250
|
+
/** Complete normalized JSON evidence; never independent operation authority. */
|
|
251
|
+
application_declaration?: unknown;
|
|
237
252
|
id: string;
|
|
238
253
|
application_id: string;
|
|
239
254
|
/** Stable declaration identity bound into the exact application authorization. */
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,9 @@ export const HOSTED_PROVIDER_CAPABILITIES = [
|
|
|
37
37
|
"mutation-replay-after-credential-retirement-v1",
|
|
38
38
|
HOSTED_CANDIDATE_B_ACTIVATION_CAPABILITY
|
|
39
39
|
];
|
|
40
|
+
export const APPLICATION_DECLARATION_EVIDENCE_CAPABILITY = "application-declaration-evidence-v1";
|
|
40
41
|
export const RELAY_CAPABILITIES = [
|
|
42
|
+
APPLICATION_DECLARATION_EVIDENCE_CAPABILITY,
|
|
41
43
|
...RELAY_REQUIRED_CAPABILITIES,
|
|
42
44
|
POLICY_FRESHNESS_LEASE_CAPABILITY,
|
|
43
45
|
"application-authorization-v5",
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LegacyMdbaseAppManifest } from "./index.js";
|
|
2
|
+
export type { LegacyApplicationFileRequirement, LegacyApplicationRequirements, LegacyMdbaseWebAppManifest, LegacyMdbasePortableAppManifest, LegacyMdbaseAppManifest } from "./index.js";
|
|
1
3
|
import { type ApplicationNotifications, type ApplicationProvisions, type ApplicationRequirements, type MdbasePortableAppManifest, type MdbaseWebAppManifest } from "./index.js";
|
|
2
4
|
export declare const APPLICATION_MANIFEST_MAX_BYTES = 524288;
|
|
3
5
|
export interface ManifestValidationIssue {
|
|
@@ -27,6 +29,18 @@ interface ValidatedManifestFields {
|
|
|
27
29
|
export type ValidatedWebAppManifest = Omit<MdbaseWebAppManifest, keyof ValidatedManifestFields> & ValidatedManifestFields;
|
|
28
30
|
export type ValidatedPortableAppManifest = Omit<MdbasePortableAppManifest, keyof ValidatedManifestFields> & ValidatedManifestFields;
|
|
29
31
|
export type ValidatedAppManifest = ValidatedWebAppManifest | ValidatedPortableAppManifest;
|
|
32
|
+
export type ValidatedLegacyAppManifest = LegacyMdbaseAppManifest & {
|
|
33
|
+
provisions: ApplicationProvisions;
|
|
34
|
+
notifications: ApplicationNotifications;
|
|
35
|
+
};
|
|
36
|
+
/** The discriminator describes the original declaration, not a translation. */
|
|
37
|
+
export type VersionedAppManifest = {
|
|
38
|
+
contractVersion: 1;
|
|
39
|
+
manifest: ValidatedLegacyAppManifest;
|
|
40
|
+
} | {
|
|
41
|
+
contractVersion: 2;
|
|
42
|
+
manifest: ValidatedAppManifest;
|
|
43
|
+
};
|
|
30
44
|
export declare class AppManifestValidationError extends Error {
|
|
31
45
|
readonly issues: ManifestValidationIssue[];
|
|
32
46
|
constructor(issues: ManifestValidationIssue[]);
|
|
@@ -39,7 +53,10 @@ export declare class AppManifestValidationError extends Error {
|
|
|
39
53
|
* rules here; consumers must not maintain parallel manifest validators.
|
|
40
54
|
*/
|
|
41
55
|
export declare function validateAppManifest(value: unknown, options?: ManifestValidationOptions): ManifestValidationResult;
|
|
56
|
+
/** Explicit opt-in to predecessor declarations; omission defaults to v1. */
|
|
57
|
+
export declare function validateVersionedAppManifest(value: unknown, options?: ManifestValidationOptions): ManifestValidationResult;
|
|
42
58
|
/** Validate, normalize optional sections, and return an isolated declaration. */
|
|
43
59
|
export declare function parseAppManifest(value: unknown, options?: ManifestValidationOptions): ValidatedAppManifest;
|
|
60
|
+
/** Validate and clone without changing capability IDs, file aliases, or version. */
|
|
61
|
+
export declare function parseVersionedAppManifest(value: unknown, options?: ManifestValidationOptions): VersionedAppManifest;
|
|
44
62
|
export declare function formatManifestValidationIssues(issues: readonly ManifestValidationIssue[]): string;
|
|
45
|
-
export {};
|
package/dist/manifest.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
3
3
|
import addFormatsModule from "ajv-formats";
|
|
4
4
|
import appManifestSchema from "../schemas/mdbase-app.schema.json" with { type: "json" };
|
|
5
|
+
import legacyAppManifestSchema from "../schemas/mdbase-app.legacy-v1.schema.json" with { type: "json" };
|
|
5
6
|
import { isNativeRedirectUri } from "./index.js";
|
|
6
7
|
export const APPLICATION_MANIFEST_MAX_BYTES = 524_288;
|
|
7
8
|
export class AppManifestValidationError extends Error {
|
|
@@ -22,6 +23,8 @@ const addFormats = addFormatsModule;
|
|
|
22
23
|
addFormats(ajv);
|
|
23
24
|
ajv.addSchema(appManifestSchema);
|
|
24
25
|
const appManifestValidator = requiredValidator(String(appManifestSchema.$id));
|
|
26
|
+
ajv.addSchema(legacyAppManifestSchema);
|
|
27
|
+
const legacyAppManifestValidator = requiredValidator(String(legacyAppManifestSchema.$id));
|
|
25
28
|
/**
|
|
26
29
|
* Validate the complete bundled application declaration accepted by Connect.
|
|
27
30
|
*
|
|
@@ -30,16 +33,35 @@ const appManifestValidator = requiredValidator(String(appManifestSchema.$id));
|
|
|
30
33
|
* rules here; consumers must not maintain parallel manifest validators.
|
|
31
34
|
*/
|
|
32
35
|
export function validateAppManifest(value, options = {}) {
|
|
36
|
+
return validateManifest(value, options, false);
|
|
37
|
+
}
|
|
38
|
+
/** Explicit opt-in to predecessor declarations; omission defaults to v1. */
|
|
39
|
+
export function validateVersionedAppManifest(value, options = {}) {
|
|
40
|
+
return validateManifest(value, options, true);
|
|
41
|
+
}
|
|
42
|
+
function declaredContractVersion(value) {
|
|
43
|
+
const requirements = asObject(asObject(value).requirements);
|
|
44
|
+
return requirements.capabilities === undefined
|
|
45
|
+
? 1
|
|
46
|
+
: asObject(requirements.capabilities).contract_version;
|
|
47
|
+
}
|
|
48
|
+
function validateManifest(value, options, versioned) {
|
|
33
49
|
const sizeIssue = validateSerializedSize(value, options.maxBytes ?? APPLICATION_MANIFEST_MAX_BYTES);
|
|
34
50
|
if (sizeIssue)
|
|
35
51
|
return invalid([sizeIssue]);
|
|
52
|
+
const version = versioned ? declaredContractVersion(value) : 2;
|
|
53
|
+
if (version !== 1 && version !== 2) {
|
|
54
|
+
return invalid([issue("/requirements/capabilities/contract_version", "contractVersion", "must declare supported contract version 1 or 2")]);
|
|
55
|
+
}
|
|
36
56
|
const candidate = options.allowLocal ? localManifestSchemaCandidate(value) : value;
|
|
37
|
-
const schemaResult = validationResult(appManifestValidator, candidate);
|
|
57
|
+
const schemaResult = validationResult(version === 1 ? legacyAppManifestValidator : appManifestValidator, candidate);
|
|
38
58
|
if (!schemaResult.valid)
|
|
39
59
|
return schemaResult;
|
|
40
60
|
const issues = [
|
|
41
61
|
...validateManifestOrigins(value, options.allowLocal === true),
|
|
42
|
-
...
|
|
62
|
+
...(version === 1
|
|
63
|
+
? validateLegacyCapabilityRequirements(value)
|
|
64
|
+
: validateCapabilityRequirements(value)),
|
|
43
65
|
...validateProvisionRequirements(value),
|
|
44
66
|
...validateConfigurationSetup(value)
|
|
45
67
|
];
|
|
@@ -50,6 +72,18 @@ export function parseAppManifest(value, options = {}) {
|
|
|
50
72
|
const result = validateAppManifest(value, options);
|
|
51
73
|
if (!result.valid)
|
|
52
74
|
throw new AppManifestValidationError(result.issues);
|
|
75
|
+
return normalizeManifest(value);
|
|
76
|
+
}
|
|
77
|
+
/** Validate and clone without changing capability IDs, file aliases, or version. */
|
|
78
|
+
export function parseVersionedAppManifest(value, options = {}) {
|
|
79
|
+
const result = validateVersionedAppManifest(value, options);
|
|
80
|
+
if (!result.valid)
|
|
81
|
+
throw new AppManifestValidationError(result.issues);
|
|
82
|
+
return declaredContractVersion(value) === 1
|
|
83
|
+
? { contractVersion: 1, manifest: normalizeManifest(value) }
|
|
84
|
+
: { contractVersion: 2, manifest: normalizeManifest(value) };
|
|
85
|
+
}
|
|
86
|
+
function normalizeManifest(value) {
|
|
53
87
|
const manifest = structuredClone(value);
|
|
54
88
|
return {
|
|
55
89
|
...manifest,
|
|
@@ -351,6 +385,35 @@ function configurationPointerError(path) {
|
|
|
351
385
|
: "must be inside a top-level x-* extension namespace";
|
|
352
386
|
}
|
|
353
387
|
function validateCapabilityRequirements(value) {
|
|
388
|
+
const manifest = asObject(value);
|
|
389
|
+
const requirements = asObject(manifest.requirements);
|
|
390
|
+
const capabilities = asObject(requirements.capabilities);
|
|
391
|
+
const issues = [];
|
|
392
|
+
if (requirements.access !== "full_collection") {
|
|
393
|
+
issues.push(issue("/requirements/access", "collectionAccess", "must explicitly request full_collection; legacy or omitted access is not widened"));
|
|
394
|
+
}
|
|
395
|
+
const required = Array.isArray(capabilities.required)
|
|
396
|
+
? capabilities.required.map(String)
|
|
397
|
+
: [];
|
|
398
|
+
const optional = Array.isArray(capabilities.optional)
|
|
399
|
+
? capabilities.optional.map(String)
|
|
400
|
+
: [];
|
|
401
|
+
const overlap = optional.find((capability) => required.includes(capability));
|
|
402
|
+
if (overlap) {
|
|
403
|
+
issues.push(issue("/requirements/capabilities/optional", "disjoint", `must not repeat required capability ${overlap}`));
|
|
404
|
+
}
|
|
405
|
+
const files = asObject(requirements.files);
|
|
406
|
+
const requiredFiles = Array.isArray(files.required) ? files.required.map(String) : [];
|
|
407
|
+
const optionalFiles = Array.isArray(files.optional) ? files.optional.map(String) : [];
|
|
408
|
+
const fileOverlap = optionalFiles.find((action) => requiredFiles.includes(action));
|
|
409
|
+
if (fileOverlap) {
|
|
410
|
+
issues.push(issue("/requirements/files/optional", "disjoint", `must not repeat required file action ${fileOverlap}`));
|
|
411
|
+
}
|
|
412
|
+
return issues;
|
|
413
|
+
}
|
|
414
|
+
// Frozen cross-field rules from c2596a6e; notably, absent capabilities skip
|
|
415
|
+
// alias/setup checks rather than inventing or widening a declaration.
|
|
416
|
+
function validateLegacyCapabilityRequirements(value) {
|
|
354
417
|
const manifest = asObject(value);
|
|
355
418
|
const requirements = asObject(manifest.requirements);
|
|
356
419
|
const capabilities = asObject(requirements.capabilities);
|
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.95",
|
|
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 && node ../../scripts/generate-operation-catalog.mjs --check",
|
|
39
|
+
"test": "node --test test/*.test.mjs && node ../../scripts/generate-connect-problems.mjs --check && node ../../scripts/generate-operation-catalog.mjs --check && node ../../scripts/generate-application-capabilities.mjs --check",
|
|
40
40
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"catalog_version": 1,
|
|
4
|
+
"capabilities": [
|
|
5
|
+
{ "id": "collection.inspect", "operations": ["describe"] },
|
|
6
|
+
{ "id": "records.watch", "operations": ["changes"] },
|
|
7
|
+
{ "id": "records.read", "operations": ["read"] },
|
|
8
|
+
{ "id": "records.query", "operations": ["query"] },
|
|
9
|
+
{ "id": "records.validate", "operations": ["validate"] },
|
|
10
|
+
{ "id": "records.create", "operations": ["create"] },
|
|
11
|
+
{ "id": "records.update", "operations": ["update"] },
|
|
12
|
+
{ "id": "records.delete", "operations": ["delete"] },
|
|
13
|
+
{ "id": "records.rename", "operations": ["rename"] },
|
|
14
|
+
{ "id": "views.list", "operations": ["list_views"] },
|
|
15
|
+
{ "id": "views.execute", "operations": ["execute_view"] },
|
|
16
|
+
{ "id": "views.source.read", "operations": ["read_view_source"] },
|
|
17
|
+
{ "id": "views.source.create", "operations": ["create_view_source"] },
|
|
18
|
+
{ "id": "views.source.update", "operations": ["update_view_source"] },
|
|
19
|
+
{ "id": "views.source.delete", "operations": ["delete_view_source"] },
|
|
20
|
+
{ "id": "definitions.contracts.current", "operations": [] },
|
|
21
|
+
{ "id": "definitions.read", "operations": ["read_type"] },
|
|
22
|
+
{ "id": "definitions.create", "operations": ["create_type"] },
|
|
23
|
+
{ "id": "definitions.update", "operations": ["update_type"] },
|
|
24
|
+
{ "id": "definitions.type-pack.inspect", "operations": ["assess_type_pack"] },
|
|
25
|
+
{ "id": "definitions.type-pack.apply", "operations": ["assess_type_pack", "apply_type_pack"] },
|
|
26
|
+
{ "id": "collection.setup.apply", "operations": ["assess_collection_setup", "apply_collection_setup"] },
|
|
27
|
+
{ "id": "timers.list", "operations": ["list_timers"] },
|
|
28
|
+
{ "id": "timers.put", "operations": ["put_timer"] },
|
|
29
|
+
{ "id": "timers.cancel", "operations": ["cancel_timer"] },
|
|
30
|
+
{ "id": "timers.reconcile", "operations": ["reconcile_timers"] },
|
|
31
|
+
{ "id": "sync.offline-replica", "operations": ["sync"] },
|
|
32
|
+
{ "id": "notifications.background-delivery", "operations": [] },
|
|
33
|
+
{ "id": "files.list", "operations": [] },
|
|
34
|
+
{ "id": "files.read", "operations": [] },
|
|
35
|
+
{ "id": "files.add", "operations": [] },
|
|
36
|
+
{ "id": "files.replace", "operations": [] },
|
|
37
|
+
{ "id": "files.move", "operations": [] },
|
|
38
|
+
{ "id": "files.delete", "operations": [] }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"catalog_version": 2,
|
|
4
|
+
"capabilities": [
|
|
5
|
+
{
|
|
6
|
+
"id": "collection.read",
|
|
7
|
+
"operations": [
|
|
8
|
+
"describe",
|
|
9
|
+
"changes",
|
|
10
|
+
"read",
|
|
11
|
+
"query",
|
|
12
|
+
"list_views",
|
|
13
|
+
"execute_view",
|
|
14
|
+
"read_view_source",
|
|
15
|
+
"validate",
|
|
16
|
+
"read_type"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "records.create",
|
|
21
|
+
"operations": ["create"]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "records.edit",
|
|
25
|
+
"operations": ["update", "rename"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "records.delete",
|
|
29
|
+
"operations": ["delete"]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "views.manage",
|
|
33
|
+
"operations": [
|
|
34
|
+
"create_view_source",
|
|
35
|
+
"update_view_source",
|
|
36
|
+
"delete_view_source"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "definitions.manage",
|
|
41
|
+
"operations": [
|
|
42
|
+
"create_type",
|
|
43
|
+
"update_type",
|
|
44
|
+
"assess_type_pack",
|
|
45
|
+
"apply_type_pack"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "background.schedule",
|
|
50
|
+
"operations": [
|
|
51
|
+
"list_timers",
|
|
52
|
+
"put_timer",
|
|
53
|
+
"cancel_timer",
|
|
54
|
+
"reconcile_timers"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "offline.replica",
|
|
59
|
+
"operations": ["sync"]
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"structured_authorities": {
|
|
63
|
+
"application_setup": [
|
|
64
|
+
"assess_collection_setup",
|
|
65
|
+
"apply_collection_setup"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://mdbase.dev/connect/schemas/mdbase-app.legacy-v1.json",
|
|
4
|
+
"title": "mdbase connect bundled application manifest v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["manifest_version", "id", "name"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"manifest_version": { "const": 1 },
|
|
10
|
+
"distribution": {
|
|
11
|
+
"enum": ["web", "portable"],
|
|
12
|
+
"description": "Portable declarations use device authorization and do not claim a browser origin."
|
|
13
|
+
},
|
|
14
|
+
"id": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 5,
|
|
17
|
+
"maxLength": 150,
|
|
18
|
+
"pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*){2,}$",
|
|
19
|
+
"description": "Stable reverse-domain application identifier. This is presentation metadata, not publisher authentication."
|
|
20
|
+
},
|
|
21
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
22
|
+
"homepage": { "type": "string", "format": "uri", "pattern": "^https://" },
|
|
23
|
+
"project_url": { "type": "string", "format": "uri", "pattern": "^https://" },
|
|
24
|
+
"icon": { "type": "string", "format": "uri", "pattern": "^https://" },
|
|
25
|
+
"redirect_uris": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"minItems": 1,
|
|
28
|
+
"maxItems": 10,
|
|
29
|
+
"uniqueItems": true,
|
|
30
|
+
"items": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"format": "uri",
|
|
33
|
+
"description": "An HTTPS URI on the homepage origin or a private-use scheme derived from the application ID."
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"requirements": { "$ref": "#/$defs/requirements" },
|
|
37
|
+
"provisions": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["type_packs"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"configuration": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"maxItems": 50,
|
|
45
|
+
"items": { "$ref": "#/$defs/configurationProvision" }
|
|
46
|
+
},
|
|
47
|
+
"type_packs": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"maxItems": 20,
|
|
50
|
+
"items": { "$ref": "#/$defs/typePackProvision" }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"notifications": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"required": ["criteria"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"criteria": {
|
|
60
|
+
"type": "array",
|
|
61
|
+
"maxItems": 50,
|
|
62
|
+
"items": { "$ref": "#/$defs/notificationCriterion" }
|
|
63
|
+
},
|
|
64
|
+
"native_delivery": { "$ref": "#/$defs/nativeNotificationDelivery" }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{
|
|
70
|
+
"required": ["homepage", "redirect_uris"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"distribution": { "const": "web" },
|
|
73
|
+
"homepage": true,
|
|
74
|
+
"redirect_uris": true,
|
|
75
|
+
"project_url": false
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"required": ["distribution"],
|
|
80
|
+
"properties": {
|
|
81
|
+
"distribution": { "const": "portable" },
|
|
82
|
+
"homepage": false,
|
|
83
|
+
"redirect_uris": false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"$defs": {
|
|
88
|
+
"identifier": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"minLength": 1,
|
|
91
|
+
"maxLength": 100,
|
|
92
|
+
"pattern": "^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
|
|
93
|
+
},
|
|
94
|
+
"duration": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"pattern": "^[0-9]+(ms|s|m|h|d)$"
|
|
97
|
+
},
|
|
98
|
+
"contract": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"required": ["id", "version", "digest"],
|
|
102
|
+
"properties": {
|
|
103
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
104
|
+
"version": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
|
|
107
|
+
},
|
|
108
|
+
"digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"requirements": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"additionalProperties": false,
|
|
114
|
+
"required": ["access", "contracts"],
|
|
115
|
+
"properties": {
|
|
116
|
+
"capabilities": { "$ref": "#/$defs/capabilityRequirements" },
|
|
117
|
+
"access": {
|
|
118
|
+
"const": "full_collection",
|
|
119
|
+
"description": "Compatibility declaration for collection-level application authorization."
|
|
120
|
+
},
|
|
121
|
+
"collection_kind": {
|
|
122
|
+
"const": "hosted",
|
|
123
|
+
"description": "Require a durable provider-backed collection with direct sync support."
|
|
124
|
+
},
|
|
125
|
+
"files": { "$ref": "#/$defs/fileRequirement" },
|
|
126
|
+
"configuration": {
|
|
127
|
+
"type": "array",
|
|
128
|
+
"maxItems": 50,
|
|
129
|
+
"items": { "$ref": "#/$defs/configurationRequirement" }
|
|
130
|
+
},
|
|
131
|
+
"contracts": {
|
|
132
|
+
"type": "array",
|
|
133
|
+
"maxItems": 20,
|
|
134
|
+
"uniqueItems": true,
|
|
135
|
+
"items": { "$ref": "#/$defs/contract" }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"capabilityRequirements": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"additionalProperties": false,
|
|
142
|
+
"required": ["contract_version", "required"],
|
|
143
|
+
"properties": {
|
|
144
|
+
"contract_version": { "const": 1 },
|
|
145
|
+
"required": {
|
|
146
|
+
"type": "array",
|
|
147
|
+
"maxItems": 32,
|
|
148
|
+
"uniqueItems": true,
|
|
149
|
+
"items": { "$ref": "#/$defs/applicationCapability" }
|
|
150
|
+
},
|
|
151
|
+
"optional": {
|
|
152
|
+
"type": "array",
|
|
153
|
+
"maxItems": 32,
|
|
154
|
+
"uniqueItems": true,
|
|
155
|
+
"items": { "$ref": "#/$defs/applicationCapability" }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"configurationValue": {
|
|
160
|
+
"anyOf": [
|
|
161
|
+
{ "type": "string" },
|
|
162
|
+
{ "type": "number" },
|
|
163
|
+
{ "type": "boolean" },
|
|
164
|
+
{ "type": "null" }
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
"configurationRequirement": {
|
|
168
|
+
"type": "object",
|
|
169
|
+
"additionalProperties": false,
|
|
170
|
+
"required": ["id", "path", "predicate", "value"],
|
|
171
|
+
"properties": {
|
|
172
|
+
"id": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"minLength": 1,
|
|
175
|
+
"maxLength": 100,
|
|
176
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
|
|
177
|
+
},
|
|
178
|
+
"path": { "type": "string", "minLength": 1, "maxLength": 1024 },
|
|
179
|
+
"predicate": { "const": "contains" },
|
|
180
|
+
"value": { "$ref": "#/$defs/configurationValue" }
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"configurationProvision": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": false,
|
|
186
|
+
"required": ["requirement", "operation", "path", "value"],
|
|
187
|
+
"properties": {
|
|
188
|
+
"requirement": {
|
|
189
|
+
"type": "string",
|
|
190
|
+
"minLength": 1,
|
|
191
|
+
"maxLength": 100,
|
|
192
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
|
|
193
|
+
},
|
|
194
|
+
"operation": { "const": "set_add" },
|
|
195
|
+
"path": { "type": "string", "minLength": 1, "maxLength": 1024 },
|
|
196
|
+
"value": { "$ref": "#/$defs/configurationValue" }
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"applicationCapability": {
|
|
200
|
+
"enum": [
|
|
201
|
+
"collection.inspect",
|
|
202
|
+
"records.watch",
|
|
203
|
+
"records.read",
|
|
204
|
+
"records.query",
|
|
205
|
+
"records.validate",
|
|
206
|
+
"records.create",
|
|
207
|
+
"records.update",
|
|
208
|
+
"records.delete",
|
|
209
|
+
"records.rename",
|
|
210
|
+
"views.list",
|
|
211
|
+
"views.execute",
|
|
212
|
+
"views.source.read",
|
|
213
|
+
"views.source.create",
|
|
214
|
+
"views.source.update",
|
|
215
|
+
"views.source.delete",
|
|
216
|
+
"definitions.contracts.current",
|
|
217
|
+
"definitions.read",
|
|
218
|
+
"definitions.create",
|
|
219
|
+
"definitions.update",
|
|
220
|
+
"definitions.type-pack.inspect",
|
|
221
|
+
"definitions.type-pack.apply",
|
|
222
|
+
"collection.setup.apply",
|
|
223
|
+
"timers.list",
|
|
224
|
+
"timers.put",
|
|
225
|
+
"timers.cancel",
|
|
226
|
+
"timers.reconcile",
|
|
227
|
+
"sync.offline-replica",
|
|
228
|
+
"notifications.background-delivery",
|
|
229
|
+
"files.list",
|
|
230
|
+
"files.read",
|
|
231
|
+
"files.add",
|
|
232
|
+
"files.replace",
|
|
233
|
+
"files.move",
|
|
234
|
+
"files.delete"
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
"fileRequirement": {
|
|
238
|
+
"type": "object",
|
|
239
|
+
"additionalProperties": false,
|
|
240
|
+
"required": ["actions", "scope"],
|
|
241
|
+
"properties": {
|
|
242
|
+
"actions": {
|
|
243
|
+
"type": "array",
|
|
244
|
+
"minItems": 1,
|
|
245
|
+
"maxItems": 6,
|
|
246
|
+
"uniqueItems": true,
|
|
247
|
+
"items": {
|
|
248
|
+
"enum": ["list", "read", "add", "replace", "move", "delete"]
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"scope": { "$ref": "#/$defs/fileScope" }
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"fileScope": {
|
|
255
|
+
"oneOf": [
|
|
256
|
+
{
|
|
257
|
+
"type": "object",
|
|
258
|
+
"additionalProperties": false,
|
|
259
|
+
"required": ["kind", "folders"],
|
|
260
|
+
"properties": {
|
|
261
|
+
"kind": { "const": "selected_folders" },
|
|
262
|
+
"folders": {
|
|
263
|
+
"type": "array",
|
|
264
|
+
"minItems": 1,
|
|
265
|
+
"maxItems": 100,
|
|
266
|
+
"uniqueItems": true,
|
|
267
|
+
"items": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"minLength": 1,
|
|
270
|
+
"maxLength": 1024,
|
|
271
|
+
"pattern": "^(?!/)(?!\\.)(?!.*//)(?!.*\\\\)(?!.*/\\.)(?!(?:_contracts|_schemas|_types|_views|node_modules)(?:/|$))(?!.*\\/(?:_contracts|_schemas|_types|_views|node_modules)(?:/|$))(?!.*/$).+$"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"type": "object",
|
|
278
|
+
"additionalProperties": false,
|
|
279
|
+
"required": ["kind"],
|
|
280
|
+
"properties": { "kind": { "const": "collection" } }
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
},
|
|
284
|
+
"typePackProvision": {
|
|
285
|
+
"type": "object",
|
|
286
|
+
"additionalProperties": false,
|
|
287
|
+
"required": ["manifest", "resources", "provides"],
|
|
288
|
+
"properties": {
|
|
289
|
+
"manifest": { "$ref": "#/$defs/typePackManifest" },
|
|
290
|
+
"resources": {
|
|
291
|
+
"type": "array",
|
|
292
|
+
"minItems": 1,
|
|
293
|
+
"maxItems": 100,
|
|
294
|
+
"items": { "$ref": "#/$defs/typePackSourceResource" }
|
|
295
|
+
},
|
|
296
|
+
"provides": {
|
|
297
|
+
"type": "array",
|
|
298
|
+
"maxItems": 20,
|
|
299
|
+
"uniqueItems": true,
|
|
300
|
+
"items": { "$ref": "#/$defs/contract" }
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
"typePackManifest": {
|
|
305
|
+
"type": "object",
|
|
306
|
+
"required": ["kind", "id", "version", "resources"],
|
|
307
|
+
"properties": {
|
|
308
|
+
"kind": { "const": "mdbase.type-pack" },
|
|
309
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
310
|
+
"version": { "$ref": "#/$defs/contract/properties/version" },
|
|
311
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
312
|
+
"description": { "type": "string", "maxLength": 500 },
|
|
313
|
+
"resources": {
|
|
314
|
+
"type": "array",
|
|
315
|
+
"minItems": 1,
|
|
316
|
+
"maxItems": 100,
|
|
317
|
+
"items": { "$ref": "#/$defs/typePackManifestResource" }
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"patternProperties": { "^x-[A-Za-z][A-Za-z0-9._:-]{0,127}$": true },
|
|
321
|
+
"additionalProperties": false
|
|
322
|
+
},
|
|
323
|
+
"typePackManifestResource": {
|
|
324
|
+
"type": "object",
|
|
325
|
+
"additionalProperties": false,
|
|
326
|
+
"required": ["kind", "mode", "source", "target", "digest"],
|
|
327
|
+
"properties": {
|
|
328
|
+
"kind": { "enum": ["contract", "type", "schema"] },
|
|
329
|
+
"mode": { "enum": ["managed", "seed"] },
|
|
330
|
+
"source": { "type": "string", "minLength": 1, "maxLength": 240 },
|
|
331
|
+
"target": { "type": "string", "minLength": 1, "maxLength": 240 },
|
|
332
|
+
"digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"typePackSourceResource": {
|
|
336
|
+
"type": "object",
|
|
337
|
+
"additionalProperties": false,
|
|
338
|
+
"required": ["source", "document"],
|
|
339
|
+
"properties": {
|
|
340
|
+
"source": { "type": "string", "minLength": 1, "maxLength": 240 },
|
|
341
|
+
"document": { "type": "string", "maxLength": 262144 }
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"notificationCriterion": {
|
|
345
|
+
"type": "object",
|
|
346
|
+
"additionalProperties": false,
|
|
347
|
+
"required": ["id", "event", "presentation"],
|
|
348
|
+
"properties": {
|
|
349
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
350
|
+
"event": { "$ref": "#/$defs/contract" },
|
|
351
|
+
"if": {
|
|
352
|
+
"type": "object",
|
|
353
|
+
"additionalProperties": false,
|
|
354
|
+
"required": ["$expr"],
|
|
355
|
+
"properties": {
|
|
356
|
+
"$expr": { "type": "string", "minLength": 1, "maxLength": 4096 }
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
"debounce": { "$ref": "#/$defs/duration" },
|
|
360
|
+
"minimum_interval": { "$ref": "#/$defs/duration" },
|
|
361
|
+
"presentation": {
|
|
362
|
+
"type": "object",
|
|
363
|
+
"additionalProperties": false,
|
|
364
|
+
"required": ["title"],
|
|
365
|
+
"properties": {
|
|
366
|
+
"title": { "type": "string", "minLength": 1, "maxLength": 80 },
|
|
367
|
+
"body": { "type": "string", "maxLength": 160 },
|
|
368
|
+
"tag": { "type": "string", "minLength": 1, "maxLength": 80 }
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
"nativeNotificationDelivery": {
|
|
374
|
+
"oneOf": [
|
|
375
|
+
{
|
|
376
|
+
"type": "object",
|
|
377
|
+
"additionalProperties": false,
|
|
378
|
+
"required": ["mode", "firebase_project_id"],
|
|
379
|
+
"properties": {
|
|
380
|
+
"mode": { "const": "managed_fcm" },
|
|
381
|
+
"firebase_project_id": {
|
|
382
|
+
"type": "string",
|
|
383
|
+
"minLength": 6,
|
|
384
|
+
"maxLength": 63,
|
|
385
|
+
"pattern": "^[a-z][a-z0-9-]*[a-z0-9]$"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"type": "object",
|
|
391
|
+
"additionalProperties": false,
|
|
392
|
+
"required": ["mode", "url"],
|
|
393
|
+
"properties": {
|
|
394
|
+
"mode": { "const": "webhook" },
|
|
395
|
+
"url": {
|
|
396
|
+
"type": "string",
|
|
397
|
+
"format": "uri",
|
|
398
|
+
"pattern": "^https://"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"additionalProperties": false,
|
|
142
142
|
"required": ["contract_version", "required"],
|
|
143
143
|
"properties": {
|
|
144
|
-
"contract_version": { "const":
|
|
144
|
+
"contract_version": { "const": 2 },
|
|
145
145
|
"required": {
|
|
146
146
|
"type": "array",
|
|
147
147
|
"maxItems": 32,
|
|
@@ -198,59 +198,35 @@
|
|
|
198
198
|
},
|
|
199
199
|
"applicationCapability": {
|
|
200
200
|
"enum": [
|
|
201
|
-
"collection.
|
|
202
|
-
"records.watch",
|
|
203
|
-
"records.read",
|
|
204
|
-
"records.query",
|
|
205
|
-
"records.validate",
|
|
201
|
+
"collection.read",
|
|
206
202
|
"records.create",
|
|
207
|
-
"records.
|
|
203
|
+
"records.edit",
|
|
208
204
|
"records.delete",
|
|
209
|
-
"
|
|
210
|
-
"
|
|
211
|
-
"
|
|
212
|
-
"
|
|
213
|
-
"views.source.create",
|
|
214
|
-
"views.source.update",
|
|
215
|
-
"views.source.delete",
|
|
216
|
-
"definitions.contracts.current",
|
|
217
|
-
"definitions.read",
|
|
218
|
-
"definitions.create",
|
|
219
|
-
"definitions.update",
|
|
220
|
-
"definitions.type-pack.inspect",
|
|
221
|
-
"definitions.type-pack.apply",
|
|
222
|
-
"collection.setup.apply",
|
|
223
|
-
"timers.list",
|
|
224
|
-
"timers.put",
|
|
225
|
-
"timers.cancel",
|
|
226
|
-
"timers.reconcile",
|
|
227
|
-
"sync.offline-replica",
|
|
228
|
-
"notifications.background-delivery",
|
|
229
|
-
"files.list",
|
|
230
|
-
"files.read",
|
|
231
|
-
"files.add",
|
|
232
|
-
"files.replace",
|
|
233
|
-
"files.move",
|
|
234
|
-
"files.delete"
|
|
205
|
+
"views.manage",
|
|
206
|
+
"definitions.manage",
|
|
207
|
+
"background.schedule",
|
|
208
|
+
"offline.replica"
|
|
235
209
|
]
|
|
236
210
|
},
|
|
237
211
|
"fileRequirement": {
|
|
238
212
|
"type": "object",
|
|
239
213
|
"additionalProperties": false,
|
|
240
|
-
"required": ["
|
|
214
|
+
"required": ["required", "scope"],
|
|
241
215
|
"properties": {
|
|
242
|
-
"
|
|
243
|
-
|
|
244
|
-
"minItems": 1,
|
|
245
|
-
"maxItems": 6,
|
|
246
|
-
"uniqueItems": true,
|
|
247
|
-
"items": {
|
|
248
|
-
"enum": ["list", "read", "add", "replace", "move", "delete"]
|
|
249
|
-
}
|
|
250
|
-
},
|
|
216
|
+
"required": { "$ref": "#/$defs/fileActions" },
|
|
217
|
+
"optional": { "$ref": "#/$defs/fileActions" },
|
|
251
218
|
"scope": { "$ref": "#/$defs/fileScope" }
|
|
252
219
|
}
|
|
253
220
|
},
|
|
221
|
+
"fileActions": {
|
|
222
|
+
"type": "array",
|
|
223
|
+
"minItems": 1,
|
|
224
|
+
"maxItems": 6,
|
|
225
|
+
"uniqueItems": true,
|
|
226
|
+
"items": {
|
|
227
|
+
"enum": ["list", "read", "add", "replace", "move", "delete"]
|
|
228
|
+
}
|
|
229
|
+
},
|
|
254
230
|
"fileScope": {
|
|
255
231
|
"oneOf": [
|
|
256
232
|
{
|