@xnetjs/data 0.0.2 → 0.0.3
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 +112 -0
- package/dist/account-BZHETMC4.js +12 -0
- package/dist/auth/index.d.ts +413 -0
- package/dist/auth/index.js +102 -0
- package/dist/budget-WR2Q4YH3.js +12 -0
- package/dist/canvas-HFMMVQLD.js +10 -0
- package/dist/channel-DXDYJ3ZH.js +12 -0
- package/dist/chat-message-QUUQGXEW.js +10 -0
- package/dist/chunk-2DSDH2ZH.js +52 -0
- package/dist/chunk-337E3ZOB.js +54 -0
- package/dist/chunk-37267RSF.js +41 -0
- package/dist/chunk-3JKW6TDE.js +66 -0
- package/dist/chunk-3RB2FMMO.js +148 -0
- package/dist/{chunk-BQBPA5HS.js → chunk-4U4KBCDN.js} +16 -2
- package/dist/chunk-4UQTURCD.js +722 -0
- package/dist/chunk-54Y7QRZN.js +109 -0
- package/dist/chunk-5FHJE7RC.js +46 -0
- package/dist/chunk-64AM6JPM.js +40 -0
- package/dist/chunk-6CN53PON.js +143 -0
- package/dist/chunk-6GB7PBCR.js +40 -0
- package/dist/chunk-6OPZJ7MG.js +78 -0
- package/dist/{chunk-IDMBCRUC.js → chunk-77UDLVQY.js} +1 -1
- package/dist/chunk-7GMJOAPF.js +52 -0
- package/dist/chunk-7PDRDSFH.js +69 -0
- package/dist/chunk-C5IRSEEZ.js +69 -0
- package/dist/chunk-CHZUSBSB.js +45 -0
- package/dist/chunk-DOZJXPQ6.js +232 -0
- package/dist/chunk-DTPSTMUD.js +105 -0
- package/dist/chunk-E6S557NC.js +402 -0
- package/dist/chunk-EIQX7INF.js +45 -0
- package/dist/chunk-ENTHVNOC.js +57 -0
- package/dist/chunk-EZA5UT4Y.js +5543 -0
- package/dist/chunk-GR3NDMBY.js +76 -0
- package/dist/chunk-GWYVY2K4.js +63 -0
- package/dist/chunk-IJ675GJR.js +42 -0
- package/dist/chunk-IP4BEVVD.js +57 -0
- package/dist/chunk-J4IVJA5M.js +102 -0
- package/dist/chunk-J6EDPPH4.js +84 -0
- package/dist/{chunk-SZC345Z2.js → chunk-JESSJ5CZ.js} +1015 -1110
- package/dist/chunk-JO6Y4E5V.js +73 -0
- package/dist/chunk-KPVBKKZS.js +47 -0
- package/dist/chunk-KTJLFJEQ.js +43 -0
- package/dist/chunk-L66WXPTW.js +32 -0
- package/dist/chunk-LGD72FVD.js +48 -0
- package/dist/chunk-O64EK5FK.js +887 -0
- package/dist/chunk-PMUQACPY.js +33 -0
- package/dist/chunk-PSUJEKVV.js +1722 -0
- package/dist/chunk-QVZXXJSP.js +6122 -0
- package/dist/chunk-QWFTRZQT.js +142 -0
- package/dist/chunk-RSSZXCLL.js +51 -0
- package/dist/chunk-S4B5OPZU.js +415 -0
- package/dist/{chunk-VYR5GPJP.js → chunk-SPFKSTPT.js} +9 -3
- package/dist/chunk-U6CBYDVZ.js +50 -0
- package/dist/chunk-UDUPQKRB.js +49 -0
- package/dist/chunk-V6HZ3LF3.js +380 -0
- package/dist/chunk-WMSCEBFH.js +64 -0
- package/dist/chunk-WUR3WWQM.js +65 -0
- package/dist/chunk-XHEA5UER.js +75 -0
- package/dist/chunk-XTUGZSYQ.js +75 -0
- package/dist/chunk-XWA3CJQ6.js +45 -0
- package/dist/chunk-YEQ6LLUY.js +49 -0
- package/dist/chunk-YWN27MLD.js +47 -0
- package/dist/chunk-YXZGYLKV.js +65 -0
- package/dist/chunk-ZICIXDEE.js +54 -0
- package/dist/chunk-ZMCO3O2Z.js +70 -0
- package/dist/clone-DpbVKLUu.d.ts +2205 -0
- package/dist/comment-Y2Y3QYYI.js +10 -0
- package/dist/crm-AAJXYIJY.js +68 -0
- package/dist/dashboard-3DC6B4BJ.js +10 -0
- package/dist/database/index.d.ts +882 -0
- package/dist/database/index.js +523 -0
- package/dist/database-DRGL5UYS.js +10 -0
- package/dist/database-field-F47FY3XX.js +10 -0
- package/dist/database-row-77OXW2NF.js +10 -0
- package/dist/database-select-option-6S3TTA7A.js +10 -0
- package/dist/database-view-YW7DQG5X.js +10 -0
- package/dist/experiment-TJOAL3OH.js +10 -0
- package/dist/external-item-D654PKLI.js +14 -0
- package/dist/external-reference-K5SUJIQN.js +8 -0
- package/dist/feed-L23XTNDI.js +12 -0
- package/dist/feed-item-GSO242J6.js +12 -0
- package/dist/folder-ONKSWHUT.js +21 -0
- package/dist/game-BF7DP5JU.js +50 -0
- package/dist/grant-NNKMWXPX.js +8 -0
- package/dist/grant-expiration-cleaner-DBiNTWbD.d.ts +249 -0
- package/dist/import-batch-JMESZ4VI.js +12 -0
- package/dist/inbox-state-AN6FPCIY.js +12 -0
- package/dist/index.d.ts +224 -4548
- package/dist/index.js +2206 -4754
- package/dist/map-AUV65YS3.js +10 -0
- package/dist/media-asset-4I5YPR4R.js +8 -0
- package/dist/memory-NV6EVJBE.js +12 -0
- package/dist/metric-X7W55DLC.js +10 -0
- package/dist/milestone-JJGOGK4R.js +12 -0
- package/dist/moderation-QSUVBCNS.js +30 -0
- package/dist/observation-UZSXXGBC.js +10 -0
- package/dist/page-NI7XODS6.js +10 -0
- package/dist/posting-FZOYVGH2.js +12 -0
- package/dist/profile-42A43GES.js +8 -0
- package/dist/project-G2CTLDXY.js +10 -0
- package/dist/query-ast-CWo6_30h.d.ts +226 -0
- package/dist/reaction-VAQBVDUD.js +10 -0
- package/dist/registry-CQoN_p8U.d.ts +99 -0
- package/dist/saved-view-AOQHGHNM.js +10 -0
- package/dist/schema/index.d.ts +6182 -0
- package/dist/schema/index.js +631 -0
- package/dist/schema-extension-5BUXRDPB.js +18 -0
- package/dist/space-PZ737CIS.js +42 -0
- package/dist/space-membership-N35NRX6F.js +14 -0
- package/dist/store/index.d.ts +391 -0
- package/dist/store/index.js +135 -0
- package/dist/store-CvgiydME.d.ts +338 -0
- package/dist/sync/awareness.d.ts +69 -0
- package/dist/sync/awareness.js +20 -0
- package/dist/system-Q5I6STDC.js +38 -0
- package/dist/tag-C677GKQW.js +16 -0
- package/dist/task-WQLMPN5U.js +16 -0
- package/dist/task-view-T5S32V4L.js +10 -0
- package/dist/transaction-U3ITRGJP.js +12 -0
- package/dist/transcription-F3T23LLO.js +12 -0
- package/dist/types-Def-5Nka.d.ts +1525 -0
- package/dist/updates.d.ts +44 -0
- package/dist/updates.js +16 -0
- package/dist/user-widget-4SJJFPG5.js +8 -0
- package/dist/view-types-DSde-uT_.d.ts +316 -0
- package/package.json +32 -9
- package/dist/canvas-HSIKIQFK.js +0 -7
- package/dist/chunk-2L5ZUGG5.js +0 -53
- package/dist/chunk-4MTS5KAQ.js +0 -22
- package/dist/chunk-GZHARFKC.js +0 -25
- package/dist/chunk-WKIKJTI2.js +0 -45
- package/dist/comment-277JD7DV.js +0 -7
- package/dist/database-W3KEHLI3.js +0 -7
- package/dist/database-row-3O2QSNZN.js +0 -7
- package/dist/grant-QVZ454YC.js +0 -7
- package/dist/page-O4WTOIEO.js +0 -7
- package/dist/task-SEKAYJH7.js +0 -7
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spaceCascadeAuthorization
|
|
3
|
+
} from "./chunk-6OPZJ7MG.js";
|
|
4
|
+
import {
|
|
5
|
+
checkbox,
|
|
6
|
+
defineSchema,
|
|
7
|
+
json,
|
|
8
|
+
number,
|
|
9
|
+
relation,
|
|
10
|
+
text
|
|
11
|
+
} from "./chunk-O64EK5FK.js";
|
|
12
|
+
|
|
13
|
+
// src/schema/schemas/database-field.ts
|
|
14
|
+
var DatabaseFieldSchema = defineSchema({
|
|
15
|
+
name: "DatabaseField",
|
|
16
|
+
namespace: "xnet://xnet.fyi/",
|
|
17
|
+
properties: {
|
|
18
|
+
/** Parent database */
|
|
19
|
+
database: relation({
|
|
20
|
+
target: "xnet://xnet.fyi/Database@2.0.0",
|
|
21
|
+
required: true
|
|
22
|
+
}),
|
|
23
|
+
/** Display name */
|
|
24
|
+
name: text({ required: true, maxLength: 200 }),
|
|
25
|
+
/** Field type (FieldType union, enforced in field-operations) */
|
|
26
|
+
type: text({ required: true, maxLength: 50 }),
|
|
27
|
+
/**
|
|
28
|
+
* Type-specific configuration (FieldConfig).
|
|
29
|
+
* Note: select/multiSelect options are NOT stored here — they are
|
|
30
|
+
* separate DatabaseSelectOption nodes so concurrent option creation
|
|
31
|
+
* merges cleanly.
|
|
32
|
+
*/
|
|
33
|
+
config: json({}),
|
|
34
|
+
/** Fractional index for default column ordering */
|
|
35
|
+
sortKey: text({ required: true }),
|
|
36
|
+
/** Default column width in pixels (table views) */
|
|
37
|
+
width: number({ min: 40, integer: true }),
|
|
38
|
+
/** Whether this is the title field (exactly one per database) */
|
|
39
|
+
isTitle: checkbox({}),
|
|
40
|
+
/** Hidden by default (views can override) */
|
|
41
|
+
hidden: checkbox({})
|
|
42
|
+
},
|
|
43
|
+
// Inherits access from its home Space (exploration 0181/0192).
|
|
44
|
+
authorization: spaceCascadeAuthorization("database")
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
DatabaseFieldSchema
|
|
49
|
+
};
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkbox,
|
|
3
|
+
defineSchema,
|
|
4
|
+
number,
|
|
5
|
+
person,
|
|
6
|
+
select,
|
|
7
|
+
text
|
|
8
|
+
} from "./chunk-O64EK5FK.js";
|
|
9
|
+
|
|
10
|
+
// src/schema/schemas/system.ts
|
|
11
|
+
import { base64ToBytes, hashHex, verify } from "@xnetjs/crypto";
|
|
12
|
+
import { parseDID } from "@xnetjs/identity";
|
|
13
|
+
var SCHEMA_STATUS_OPTIONS = [
|
|
14
|
+
{ id: "draft", name: "Draft", color: "gray" },
|
|
15
|
+
{ id: "published", name: "Published", color: "green" },
|
|
16
|
+
{ id: "deprecated", name: "Deprecated", color: "yellow" },
|
|
17
|
+
{ id: "revoked", name: "Revoked", color: "red" }
|
|
18
|
+
];
|
|
19
|
+
var COMPATIBILITY_MODE_OPTIONS = [
|
|
20
|
+
{ id: "compatible", name: "Compatible", color: "green" },
|
|
21
|
+
{ id: "requires-lens", name: "Requires Lens", color: "yellow" },
|
|
22
|
+
{ id: "breaking", name: "Breaking", color: "red" }
|
|
23
|
+
];
|
|
24
|
+
var POLICY_STATUS_OPTIONS = [
|
|
25
|
+
{ id: "active", name: "Active", color: "green" },
|
|
26
|
+
{ id: "superseded", name: "Superseded", color: "yellow" },
|
|
27
|
+
{ id: "revoked", name: "Revoked", color: "red" }
|
|
28
|
+
];
|
|
29
|
+
var PRESENCE_VISIBILITY_OPTIONS = [
|
|
30
|
+
{ id: "private", name: "Private", color: "gray" },
|
|
31
|
+
{ id: "trusted-app", name: "Trusted App", color: "blue" },
|
|
32
|
+
{ id: "public-metadata", name: "Public Metadata", color: "green" }
|
|
33
|
+
];
|
|
34
|
+
var PRESENCE_COUNT_BUCKET_OPTIONS = [
|
|
35
|
+
{ id: "0", name: "0", color: "gray" },
|
|
36
|
+
{ id: "1", name: "1", color: "gray" },
|
|
37
|
+
{ id: "2-5", name: "2-5", color: "blue" },
|
|
38
|
+
{ id: "6-20", name: "6-20", color: "blue" },
|
|
39
|
+
{ id: "21-100", name: "21-100", color: "green" },
|
|
40
|
+
{ id: "100+", name: "100+", color: "green" }
|
|
41
|
+
];
|
|
42
|
+
var SCHEMA_IRI_PATTERN = /^xnet:\/\/[^/]+\/.+/;
|
|
43
|
+
var SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
|
|
44
|
+
var CONTENT_HASH_PATTERN = /^cid:blake3:[0-9a-f]{64}$/;
|
|
45
|
+
var SYSTEM_NAMESPACE_KINDS = ["schema", "compat", "policy", "presence", "authz"];
|
|
46
|
+
var SchemaDefinitionSchema = defineSchema({
|
|
47
|
+
name: "SchemaDefinition",
|
|
48
|
+
namespace: "xnet://xnet.fyi/",
|
|
49
|
+
properties: {
|
|
50
|
+
schemaIri: text({ required: true, pattern: SCHEMA_IRI_PATTERN }),
|
|
51
|
+
version: text({ required: true, pattern: SEMVER_PATTERN }),
|
|
52
|
+
authority: text({ required: true }),
|
|
53
|
+
definitionBytes: text({ required: true, minLength: 2 }),
|
|
54
|
+
contentHash: text({ required: true, pattern: CONTENT_HASH_PATTERN }),
|
|
55
|
+
publishedAt: number({ required: true, integer: true, min: 0 }),
|
|
56
|
+
status: select({ options: SCHEMA_STATUS_OPTIONS, required: true, default: "draft" }),
|
|
57
|
+
signature: text({ required: true, minLength: 80 }),
|
|
58
|
+
signingKeyId: text({ required: true }),
|
|
59
|
+
policyDefaults: text({}),
|
|
60
|
+
authorityProof: text({})
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
var SchemaCompatibilitySchema = defineSchema({
|
|
64
|
+
name: "SchemaCompatibility",
|
|
65
|
+
namespace: "xnet://xnet.fyi/",
|
|
66
|
+
properties: {
|
|
67
|
+
fromSchema: text({ required: true, pattern: SCHEMA_IRI_PATTERN }),
|
|
68
|
+
toSchema: text({ required: true, pattern: SCHEMA_IRI_PATTERN }),
|
|
69
|
+
mode: select({ options: COMPATIBILITY_MODE_OPTIONS, required: true }),
|
|
70
|
+
lossless: checkbox({ default: false }),
|
|
71
|
+
lensRef: text({}),
|
|
72
|
+
validatedAt: number({ required: true, integer: true, min: 0 }),
|
|
73
|
+
validatedBy: person({})
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
var SyncPolicySchema = defineSchema({
|
|
77
|
+
name: "SyncPolicy",
|
|
78
|
+
namespace: "xnet://xnet.fyi/",
|
|
79
|
+
properties: {
|
|
80
|
+
subjectDid: person({ required: true }),
|
|
81
|
+
matchRules: text({ required: true, minLength: 2 }),
|
|
82
|
+
destinations: text({ required: true, minLength: 2 }),
|
|
83
|
+
priority: number({ required: true, integer: true, min: 0 }),
|
|
84
|
+
revision: number({ required: true, integer: true, min: 1 }),
|
|
85
|
+
effectiveFrom: number({ required: true, integer: true, min: 0 }),
|
|
86
|
+
expiresAt: number({ integer: true, min: 0 }),
|
|
87
|
+
status: select({ options: POLICY_STATUS_OPTIONS, required: true, default: "active" })
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
var PresenceSummarySchema = defineSchema({
|
|
91
|
+
name: "PresenceSummary",
|
|
92
|
+
namespace: "xnet://xnet.fyi/",
|
|
93
|
+
properties: {
|
|
94
|
+
subjectDid: person({ required: true }),
|
|
95
|
+
schemaIri: text({ required: true, pattern: SCHEMA_IRI_PATTERN }),
|
|
96
|
+
namespace: text({ required: true }),
|
|
97
|
+
countBucket: select({ options: PRESENCE_COUNT_BUCKET_OPTIONS, required: true }),
|
|
98
|
+
visibility: select({ options: PRESENCE_VISIBILITY_OPTIONS, required: true }),
|
|
99
|
+
lastUpdatedAt: number({ required: true, integer: true, min: 0 }),
|
|
100
|
+
sourcePolicy: text({}),
|
|
101
|
+
noise: text({})
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
var SYSTEM_SCHEMA_IRIS = {
|
|
105
|
+
SchemaDefinition: SchemaDefinitionSchema.schema["@id"],
|
|
106
|
+
SchemaCompatibility: SchemaCompatibilitySchema.schema["@id"],
|
|
107
|
+
SyncPolicy: SyncPolicySchema.schema["@id"],
|
|
108
|
+
PresenceSummary: PresenceSummarySchema.schema["@id"]
|
|
109
|
+
};
|
|
110
|
+
var SYSTEM_SCHEMA_BASE_IRIS = {
|
|
111
|
+
SchemaDefinition: "xnet://xnet.fyi/SchemaDefinition",
|
|
112
|
+
SchemaCompatibility: "xnet://xnet.fyi/SchemaCompatibility",
|
|
113
|
+
SyncPolicy: "xnet://xnet.fyi/SyncPolicy",
|
|
114
|
+
PresenceSummary: "xnet://xnet.fyi/PresenceSummary",
|
|
115
|
+
Grant: "xnet://xnet.fyi/Grant"
|
|
116
|
+
};
|
|
117
|
+
function isSystemSchemaIri(schemaIri) {
|
|
118
|
+
return Object.values(SYSTEM_SCHEMA_IRIS).some((iri) => schemaIri === iri) || Object.values(SYSTEM_SCHEMA_BASE_IRIS).some((iri) => schemaIri === iri);
|
|
119
|
+
}
|
|
120
|
+
function buildSystemNamespace(subjectDid, kind) {
|
|
121
|
+
return `xnet://${subjectDid}/sys/${kind}/`;
|
|
122
|
+
}
|
|
123
|
+
function buildSystemNodeId(subjectDid, kind, localId) {
|
|
124
|
+
return `${buildSystemNamespace(subjectDid, kind)}${localId.replace(/^\/+/, "")}`;
|
|
125
|
+
}
|
|
126
|
+
function parseSystemNamespaceResource(resource) {
|
|
127
|
+
const kinds = SYSTEM_NAMESPACE_KINDS.join("|");
|
|
128
|
+
const match = new RegExp(`^xnet://(did:key:[^/]+)/sys/(${kinds})(?:/(.*))?$`).exec(resource);
|
|
129
|
+
if (!match) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const [, subjectDid, kind, localId = ""] = match;
|
|
133
|
+
return {
|
|
134
|
+
subjectDid,
|
|
135
|
+
kind,
|
|
136
|
+
namespace: buildSystemNamespace(subjectDid, kind),
|
|
137
|
+
localId
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function isSystemNamespaceResource(resource) {
|
|
141
|
+
return parseSystemNamespaceResource(resource) !== null;
|
|
142
|
+
}
|
|
143
|
+
function resolveSchemaAuthority(options) {
|
|
144
|
+
const authority = extractSchemaIriAuthority(options.schemaIri);
|
|
145
|
+
const declaredAuthority = options.authority ?? authority ?? "";
|
|
146
|
+
const errors = [];
|
|
147
|
+
if (!authority) {
|
|
148
|
+
errors.push({
|
|
149
|
+
path: "schemaIri",
|
|
150
|
+
message: "schemaIri must use xnet://<authority>/<name> format",
|
|
151
|
+
value: options.schemaIri
|
|
152
|
+
});
|
|
153
|
+
return { valid: false, errors, kind: "invalid", authority: declaredAuthority };
|
|
154
|
+
}
|
|
155
|
+
if (declaredAuthority !== authority) {
|
|
156
|
+
errors.push({
|
|
157
|
+
path: "authority",
|
|
158
|
+
message: "authority must match schema IRI authority",
|
|
159
|
+
value: declaredAuthority
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (authority.startsWith("did:key:")) {
|
|
163
|
+
if (options.publisherDid && options.publisherDid !== authority) {
|
|
164
|
+
errors.push({
|
|
165
|
+
path: "publisherDid",
|
|
166
|
+
message: "DID authority schemas must be published by the authority DID",
|
|
167
|
+
value: options.publisherDid
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return { valid: errors.length === 0, errors, kind: "did", authority };
|
|
171
|
+
}
|
|
172
|
+
if (!options.allowDomainAuthority) {
|
|
173
|
+
errors.push({
|
|
174
|
+
path: "schemaIri",
|
|
175
|
+
message: "domain authority schemas require explicit domain linkage policy",
|
|
176
|
+
value: options.schemaIri
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (options.allowDomainAuthority && !options.domainLinkageProof) {
|
|
180
|
+
errors.push({
|
|
181
|
+
path: "domainLinkageProof",
|
|
182
|
+
message: "domain authority schemas require a domain linkage proof"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return { valid: errors.length === 0, errors, kind: "domain", authority };
|
|
186
|
+
}
|
|
187
|
+
function computeSchemaDefinitionContentHash(definitionBytes) {
|
|
188
|
+
return `cid:blake3:${hashHex(new TextEncoder().encode(definitionBytes))}`;
|
|
189
|
+
}
|
|
190
|
+
function createSchemaDefinitionSigningPayload(input) {
|
|
191
|
+
return new TextEncoder().encode(canonicalJson(input));
|
|
192
|
+
}
|
|
193
|
+
function validateSchemaDefinitionNode(node, options = {}) {
|
|
194
|
+
const flattened = flattenNodeProperties(node);
|
|
195
|
+
const schemaResult = SchemaDefinitionSchema.validate(flattened);
|
|
196
|
+
const errors = [...schemaResult.errors];
|
|
197
|
+
const schemaIri = asString(flattened.schemaIri);
|
|
198
|
+
const version = asString(flattened.version);
|
|
199
|
+
const authority = asString(flattened.authority);
|
|
200
|
+
const definitionBytes = asString(flattened.definitionBytes);
|
|
201
|
+
const contentHash = asString(flattened.contentHash);
|
|
202
|
+
const signature = asString(flattened.signature);
|
|
203
|
+
const publishedAt = asNumber(flattened.publishedAt);
|
|
204
|
+
const status = asSchemaDefinitionStatus(flattened.status);
|
|
205
|
+
const authorityProof = asString(flattened.authorityProof);
|
|
206
|
+
if (schemaIri && authority) {
|
|
207
|
+
const authorityResult = resolveSchemaAuthority({
|
|
208
|
+
schemaIri,
|
|
209
|
+
authority,
|
|
210
|
+
publisherDid: options.publisherDid ?? asDid(flattened.createdBy),
|
|
211
|
+
allowDomainAuthority: options.allowDomainAuthority,
|
|
212
|
+
domainLinkageProof: options.domainLinkageProof ?? authorityProof ?? void 0
|
|
213
|
+
});
|
|
214
|
+
errors.push(...authorityResult.errors);
|
|
215
|
+
}
|
|
216
|
+
if (definitionBytes && contentHash) {
|
|
217
|
+
const expectedHash = computeSchemaDefinitionContentHash(definitionBytes);
|
|
218
|
+
if (contentHash !== expectedHash) {
|
|
219
|
+
errors.push({
|
|
220
|
+
path: "contentHash",
|
|
221
|
+
message: "contentHash must match definitionBytes",
|
|
222
|
+
value: contentHash
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const parsedDefinition = parseSchemaJson(definitionBytes);
|
|
226
|
+
if (!parsedDefinition) {
|
|
227
|
+
errors.push({
|
|
228
|
+
path: "definitionBytes",
|
|
229
|
+
message: "definitionBytes must contain a valid JSON schema definition"
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
if (parsedDefinition["@id"] !== schemaIri) {
|
|
233
|
+
errors.push({
|
|
234
|
+
path: "definitionBytes.@id",
|
|
235
|
+
message: "embedded schema @id must match schemaIri",
|
|
236
|
+
value: parsedDefinition["@id"]
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (parsedDefinition.version !== version) {
|
|
240
|
+
errors.push({
|
|
241
|
+
path: "definitionBytes.version",
|
|
242
|
+
message: "embedded schema version must match version",
|
|
243
|
+
value: parsedDefinition.version
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (schemaIri && version && authority && contentHash && publishedAt !== null && status && signature) {
|
|
249
|
+
const signatureResult = validateSchemaDefinitionSignature({
|
|
250
|
+
signature,
|
|
251
|
+
authority,
|
|
252
|
+
authorityPublicKey: options.authorityPublicKey,
|
|
253
|
+
payload: createSchemaDefinitionSigningPayload({
|
|
254
|
+
schemaIri,
|
|
255
|
+
version,
|
|
256
|
+
authority,
|
|
257
|
+
contentHash,
|
|
258
|
+
publishedAt,
|
|
259
|
+
status
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
errors.push(...signatureResult.errors);
|
|
263
|
+
}
|
|
264
|
+
return { valid: errors.length === 0, errors };
|
|
265
|
+
}
|
|
266
|
+
function validateSchemaDefinitionSignature(input) {
|
|
267
|
+
const errors = [];
|
|
268
|
+
let signatureBytes;
|
|
269
|
+
try {
|
|
270
|
+
signatureBytes = base64ToBytes(input.signature);
|
|
271
|
+
} catch {
|
|
272
|
+
return {
|
|
273
|
+
valid: false,
|
|
274
|
+
errors: [{ path: "signature", message: "signature must be base64 encoded" }]
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (signatureBytes.length !== 64) {
|
|
278
|
+
errors.push({
|
|
279
|
+
path: "signature",
|
|
280
|
+
message: "signature must be an Ed25519 signature"
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
const publicKey = input.authorityPublicKey ?? getAuthorityPublicKey(input.authority);
|
|
284
|
+
if (publicKey && !verify(input.payload, signatureBytes, publicKey)) {
|
|
285
|
+
errors.push({
|
|
286
|
+
path: "signature",
|
|
287
|
+
message: "signature must verify against schema authority"
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return { valid: errors.length === 0, errors };
|
|
291
|
+
}
|
|
292
|
+
function getAuthorityPublicKey(authority) {
|
|
293
|
+
if (!authority.startsWith("did:key:")) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
return parseDID(authority);
|
|
298
|
+
} catch {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function extractSchemaIriAuthority(schemaIri) {
|
|
303
|
+
const match = /^xnet:\/\/([^/]+)\/.+/.exec(schemaIri);
|
|
304
|
+
return match?.[1] ?? null;
|
|
305
|
+
}
|
|
306
|
+
function flattenNodeProperties(node) {
|
|
307
|
+
if (!isRecord(node)) {
|
|
308
|
+
return {};
|
|
309
|
+
}
|
|
310
|
+
if (isRecord(node.properties)) {
|
|
311
|
+
return {
|
|
312
|
+
id: node.id,
|
|
313
|
+
schemaId: node.schemaId,
|
|
314
|
+
createdAt: node.createdAt,
|
|
315
|
+
createdBy: node.createdBy,
|
|
316
|
+
...node.properties
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return node;
|
|
320
|
+
}
|
|
321
|
+
function parseSchemaJson(definitionBytes) {
|
|
322
|
+
try {
|
|
323
|
+
const parsed = JSON.parse(definitionBytes);
|
|
324
|
+
if (!isRecord(parsed)) return null;
|
|
325
|
+
if (typeof parsed["@id"] !== "string") return null;
|
|
326
|
+
if (typeof parsed.version !== "string") return null;
|
|
327
|
+
if (!Array.isArray(parsed.properties)) return null;
|
|
328
|
+
return parsed;
|
|
329
|
+
} catch {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function canonicalJson(value) {
|
|
334
|
+
return JSON.stringify(sortKeys(value));
|
|
335
|
+
}
|
|
336
|
+
function sortKeys(value) {
|
|
337
|
+
if (value === null || typeof value !== "object") {
|
|
338
|
+
return value;
|
|
339
|
+
}
|
|
340
|
+
if (Array.isArray(value)) {
|
|
341
|
+
return value.map(sortKeys);
|
|
342
|
+
}
|
|
343
|
+
return Object.fromEntries(
|
|
344
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, sortKeys(entry)])
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
function isRecord(value) {
|
|
348
|
+
return Boolean(value && typeof value === "object");
|
|
349
|
+
}
|
|
350
|
+
function asString(value) {
|
|
351
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
352
|
+
}
|
|
353
|
+
function asNumber(value) {
|
|
354
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
355
|
+
}
|
|
356
|
+
function asDid(value) {
|
|
357
|
+
return typeof value === "string" && value.startsWith("did:key:") ? value : void 0;
|
|
358
|
+
}
|
|
359
|
+
function asSchemaDefinitionStatus(value) {
|
|
360
|
+
return SCHEMA_STATUS_OPTIONS.some((option) => option.id === value) ? value : null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export {
|
|
364
|
+
SYSTEM_NAMESPACE_KINDS,
|
|
365
|
+
SchemaDefinitionSchema,
|
|
366
|
+
SchemaCompatibilitySchema,
|
|
367
|
+
SyncPolicySchema,
|
|
368
|
+
PresenceSummarySchema,
|
|
369
|
+
SYSTEM_SCHEMA_IRIS,
|
|
370
|
+
SYSTEM_SCHEMA_BASE_IRIS,
|
|
371
|
+
isSystemSchemaIri,
|
|
372
|
+
buildSystemNamespace,
|
|
373
|
+
buildSystemNodeId,
|
|
374
|
+
parseSystemNamespaceResource,
|
|
375
|
+
isSystemNamespaceResource,
|
|
376
|
+
resolveSchemaAuthority,
|
|
377
|
+
computeSchemaDefinitionContentHash,
|
|
378
|
+
createSchemaDefinitionSigningPayload,
|
|
379
|
+
validateSchemaDefinitionNode
|
|
380
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spaceCascadeAuthorization
|
|
3
|
+
} from "./chunk-6OPZJ7MG.js";
|
|
4
|
+
import {
|
|
5
|
+
date,
|
|
6
|
+
defineSchema,
|
|
7
|
+
person,
|
|
8
|
+
relation,
|
|
9
|
+
select,
|
|
10
|
+
text
|
|
11
|
+
} from "./chunk-O64EK5FK.js";
|
|
12
|
+
|
|
13
|
+
// src/schema/schemas/project.ts
|
|
14
|
+
var ProjectSchema = defineSchema({
|
|
15
|
+
name: "Project",
|
|
16
|
+
namespace: "xnet://xnet.fyi/",
|
|
17
|
+
properties: {
|
|
18
|
+
/** Project name */
|
|
19
|
+
name: text({ required: true, maxLength: 200 }),
|
|
20
|
+
/** Emoji or icon URL */
|
|
21
|
+
icon: text({ maxLength: 500 }),
|
|
22
|
+
/** Project lifecycle status */
|
|
23
|
+
status: select({
|
|
24
|
+
options: [
|
|
25
|
+
{ id: "planned", name: "Planned", color: "gray" },
|
|
26
|
+
{ id: "in-progress", name: "In Progress", color: "blue" },
|
|
27
|
+
{ id: "paused", name: "Paused", color: "yellow" },
|
|
28
|
+
{ id: "completed", name: "Completed", color: "green" },
|
|
29
|
+
{ id: "cancelled", name: "Cancelled", color: "red" }
|
|
30
|
+
],
|
|
31
|
+
default: "planned"
|
|
32
|
+
}),
|
|
33
|
+
/** Project lead */
|
|
34
|
+
lead: person({}),
|
|
35
|
+
/** Target completion date */
|
|
36
|
+
targetDate: date({}),
|
|
37
|
+
/** Canonical home; empty = Unfiled (exploration 0169) */
|
|
38
|
+
folder: relation({ target: "xnet://xnet.fyi/Folder@1.0.0" }),
|
|
39
|
+
/** Order among folder siblings — fractional index */
|
|
40
|
+
sortKey: text({ maxLength: 500 }),
|
|
41
|
+
/** Workspace-wide labels, referenced by id (exploration 0169) */
|
|
42
|
+
tags: relation({ target: "xnet://xnet.fyi/Tag@1.0.0", multiple: true }),
|
|
43
|
+
/** Canonical SECURITY home; empty = personal/private (exploration 0179) */
|
|
44
|
+
space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" }),
|
|
45
|
+
/** Per-node visibility; `inherit` defers to the Space (exploration 0179) */
|
|
46
|
+
visibility: select({
|
|
47
|
+
options: [
|
|
48
|
+
{ id: "inherit", name: "Inherit", color: "gray" },
|
|
49
|
+
{ id: "private", name: "Private", color: "gray" },
|
|
50
|
+
{ id: "unlisted", name: "Unlisted", color: "yellow" },
|
|
51
|
+
{ id: "public", name: "Public", color: "green" }
|
|
52
|
+
],
|
|
53
|
+
default: "inherit"
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
document: "yjs",
|
|
57
|
+
// Collaborative project brief
|
|
58
|
+
// Inherits access from its home Space (exploration 0181).
|
|
59
|
+
authorization: spaceCascadeAuthorization()
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
ProjectSchema
|
|
64
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spaceCascadeAuthorization
|
|
3
|
+
} from "./chunk-6OPZJ7MG.js";
|
|
4
|
+
import {
|
|
5
|
+
defineSchema,
|
|
6
|
+
json,
|
|
7
|
+
number,
|
|
8
|
+
relation,
|
|
9
|
+
text
|
|
10
|
+
} from "./chunk-O64EK5FK.js";
|
|
11
|
+
|
|
12
|
+
// src/schema/schemas/schema-extension.ts
|
|
13
|
+
var SCHEMA_EXTENSION_SCHEMA_IRI = "xnet://xnet.fyi/SchemaExtension@1.0.0";
|
|
14
|
+
var EXTENSION_FIELD_SCHEMA_IRI = "xnet://xnet.fyi/ExtensionField@1.0.0";
|
|
15
|
+
var SchemaExtensionSchema = defineSchema({
|
|
16
|
+
name: "SchemaExtension",
|
|
17
|
+
namespace: "xnet://xnet.fyi/",
|
|
18
|
+
properties: {
|
|
19
|
+
/** Versioned IRI of the schema being extended, e.g. 'xnet://xnet.fyi/Contact@1.0.0' */
|
|
20
|
+
targetSchema: text({ required: true, maxLength: 300 }),
|
|
21
|
+
/**
|
|
22
|
+
* Namespace authority that owns these fields — a Space id, a DID, or a
|
|
23
|
+
* domain. Becomes the `<authority>` segment of every `ext:` key, so two
|
|
24
|
+
* tenants extending the same schema never collide.
|
|
25
|
+
*/
|
|
26
|
+
authority: text({ required: true, maxLength: 200 }),
|
|
27
|
+
/** Human-readable label for the extension set (e.g. "Acme CRM fields") */
|
|
28
|
+
label: text({ maxLength: 200 }),
|
|
29
|
+
/** Optional owning Space — drives the authorization cascade for this overlay */
|
|
30
|
+
space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" })
|
|
31
|
+
},
|
|
32
|
+
authorization: spaceCascadeAuthorization()
|
|
33
|
+
});
|
|
34
|
+
var ExtensionFieldSchema = defineSchema({
|
|
35
|
+
name: "ExtensionField",
|
|
36
|
+
namespace: "xnet://xnet.fyi/",
|
|
37
|
+
properties: {
|
|
38
|
+
/** Parent SchemaExtension */
|
|
39
|
+
extension: relation({ target: SCHEMA_EXTENSION_SCHEMA_IRI, required: true }),
|
|
40
|
+
/** Field token — the `<field>` segment of the `ext:<authority>/<field>` key */
|
|
41
|
+
name: text({ required: true, maxLength: 200 }),
|
|
42
|
+
/** Field type (FieldType union, enforced in field-operations) */
|
|
43
|
+
type: text({ required: true, maxLength: 50 }),
|
|
44
|
+
/** Type-specific configuration (FieldConfig) */
|
|
45
|
+
config: json({}),
|
|
46
|
+
/** Fractional index for ordering among a schema's extension fields */
|
|
47
|
+
sortKey: text({ required: true }),
|
|
48
|
+
/** Default column width in pixels (table views) */
|
|
49
|
+
width: number({ min: 40, integer: true }),
|
|
50
|
+
/** Optional owning Space — mirrors the parent extension's cascade */
|
|
51
|
+
space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" })
|
|
52
|
+
},
|
|
53
|
+
authorization: spaceCascadeAuthorization()
|
|
54
|
+
});
|
|
55
|
+
function schemaExtensionId(authority, targetSchema) {
|
|
56
|
+
return `schemaext:${authority}:${targetSchema}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
SCHEMA_EXTENSION_SCHEMA_IRI,
|
|
61
|
+
EXTENSION_FIELD_SCHEMA_IRI,
|
|
62
|
+
SchemaExtensionSchema,
|
|
63
|
+
ExtensionFieldSchema,
|
|
64
|
+
schemaExtensionId
|
|
65
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/updates.ts
|
|
2
|
+
import { sign, verify, hashHex } from "@xnetjs/crypto";
|
|
3
|
+
import * as Y from "yjs";
|
|
4
|
+
function signUpdate(options) {
|
|
5
|
+
const { update, authorDID, signingKey, parentHash, vectorClock } = options;
|
|
6
|
+
const updateHash = hashHex(update);
|
|
7
|
+
const timestamp = Date.now();
|
|
8
|
+
const signaturePayload = new TextEncoder().encode(
|
|
9
|
+
JSON.stringify({ updateHash, parentHash, authorDID, timestamp })
|
|
10
|
+
);
|
|
11
|
+
const signature = sign(signaturePayload, signingKey);
|
|
12
|
+
return {
|
|
13
|
+
update,
|
|
14
|
+
parentHash,
|
|
15
|
+
updateHash,
|
|
16
|
+
authorDID,
|
|
17
|
+
signature,
|
|
18
|
+
timestamp,
|
|
19
|
+
vectorClock
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function verifyUpdate(update, getPublicKey) {
|
|
23
|
+
const publicKey = getPublicKey(update.authorDID);
|
|
24
|
+
if (!publicKey) return false;
|
|
25
|
+
const actualHash = hashHex(update.update);
|
|
26
|
+
if (actualHash !== update.updateHash) return false;
|
|
27
|
+
const signaturePayload = new TextEncoder().encode(
|
|
28
|
+
JSON.stringify({
|
|
29
|
+
updateHash: update.updateHash,
|
|
30
|
+
parentHash: update.parentHash,
|
|
31
|
+
authorDID: update.authorDID,
|
|
32
|
+
timestamp: update.timestamp
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
return verify(signaturePayload, update.signature, publicKey);
|
|
36
|
+
}
|
|
37
|
+
function applySignedUpdate(doc, update) {
|
|
38
|
+
Y.applyUpdate(doc, update.update);
|
|
39
|
+
}
|
|
40
|
+
function captureUpdate(doc, authorDID, signingKey, parentHash, vectorClock, callback) {
|
|
41
|
+
let capturedUpdate = null;
|
|
42
|
+
const handler = (update) => {
|
|
43
|
+
capturedUpdate = update;
|
|
44
|
+
};
|
|
45
|
+
doc.on("update", handler);
|
|
46
|
+
callback();
|
|
47
|
+
doc.off("update", handler);
|
|
48
|
+
if (!capturedUpdate) return null;
|
|
49
|
+
return signUpdate({
|
|
50
|
+
doc,
|
|
51
|
+
update: capturedUpdate,
|
|
52
|
+
authorDID,
|
|
53
|
+
signingKey,
|
|
54
|
+
parentHash,
|
|
55
|
+
vectorClock
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function mergeDocuments(target, sources) {
|
|
59
|
+
for (const source of sources) {
|
|
60
|
+
const state = Y.encodeStateAsUpdate(source);
|
|
61
|
+
Y.applyUpdate(target, state);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getMissingUpdates(doc, remoteStateVector) {
|
|
65
|
+
return Y.encodeStateAsUpdate(doc, remoteStateVector);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
signUpdate,
|
|
70
|
+
verifyUpdate,
|
|
71
|
+
applySignedUpdate,
|
|
72
|
+
captureUpdate,
|
|
73
|
+
mergeDocuments,
|
|
74
|
+
getMissingUpdates
|
|
75
|
+
};
|