@xnetjs/data 2.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +5 -0
  2. package/dist/auth/index.d.ts +4 -4
  3. package/dist/{chunk-56EXPOJ4.js → chunk-ANMAR3KY.js} +34 -19
  4. package/dist/chunk-GJAOMEQO.js +114 -0
  5. package/dist/chunk-KY62NUOI.js +67 -0
  6. package/dist/chunk-N37U6ZPI.js +789 -0
  7. package/dist/chunk-NFPEFD2I.js +42 -0
  8. package/dist/{chunk-EGGRKSRH.js → chunk-QKVIKVAL.js} +70 -31
  9. package/dist/{chunk-CY6EJUNV.js → chunk-S7SEN4LH.js} +5 -1
  10. package/dist/{chunk-4HJZJMPI.js → chunk-TN7LGV66.js} +1 -1
  11. package/dist/{chunk-YRFCXKJC.js → chunk-UQ2VFHU3.js} +2 -2
  12. package/dist/chunk-VZSEZNYA.js +83 -0
  13. package/dist/chunk-WSINZHSR.js +90 -0
  14. package/dist/{chunk-CNGEXCIH.js → chunk-YCBJDT23.js} +69 -55
  15. package/dist/{clone-BsoRt89i.d.ts → clone-ywzFVkID.d.ts} +16 -6
  16. package/dist/course-C5DXOIVO.js +24 -0
  17. package/dist/database/index.d.ts +17 -5
  18. package/dist/database/index.js +7 -7
  19. package/dist/event-VW3FBN4F.js +20 -0
  20. package/dist/{form-types-Dkqm9UO2.d.ts → form-types-BrHYdpi0.d.ts} +1 -1
  21. package/dist/{game-NEQP7OFX.js → game-7AXW7XKS.js} +1 -1
  22. package/dist/{grant-expiration-cleaner-BwdmRFua.d.ts → grant-expiration-cleaner-CRS9OyhV.d.ts} +3 -3
  23. package/dist/index.d.ts +10 -320
  24. package/dist/index.js +522 -1097
  25. package/dist/{moderation-HRMMBFVH.js → moderation-5Z4LKOKN.js} +5 -3
  26. package/dist/{page-RUS2BKMX.js → page-W4G6433H.js} +1 -1
  27. package/dist/portability/index.d.ts +330 -0
  28. package/dist/portability/index.js +34 -0
  29. package/dist/post-7IOO64MD.js +14 -0
  30. package/dist/{profile-5H6XWCK4.js → profile-KLHCAQY2.js} +1 -1
  31. package/dist/publication-3MN3WWKP.js +10 -0
  32. package/dist/{query-ast-DYGcKxoa.d.ts → query-ast-DQJO7yRV.d.ts} +1 -1
  33. package/dist/{registry-D_3b1Lg6.d.ts → registry-C9IRLLeR.d.ts} +1 -1
  34. package/dist/schema/index.d.ts +375 -26
  35. package/dist/schema/index.js +213 -167
  36. package/dist/space-GBTEHZOK.js +1 -1
  37. package/dist/store/index.d.ts +4 -4
  38. package/dist/store/index.js +4 -4
  39. package/dist/{store-BMnhBWzM.d.ts → store-D1oiFQuF.d.ts} +19 -2
  40. package/dist/{types-cycApzib.d.ts → types-gws1tSf-.d.ts} +1 -1
  41. package/package.json +27 -25
  42. package/dist/chunk-Y2XWK7AA.js +0 -50
package/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Schema system, NodeStore, and Yjs CRDT engine for xNet. This is the central data package -- it defines how structured data and rich text documents are created, stored, and synced.
4
4
 
5
+ > **Alpha software.** xNet is released but early: this package is on npm and
6
+ > usable today, but its API can change between releases, sometimes without a
7
+ > migration path. Pin your version. See the
8
+ > [project README](https://github.com/crs48/xNet#readme) for what alpha means here.
9
+
5
10
  > **Status:** Mixed
6
11
  > Stable entrypoints: `@xnetjs/data/schema`, `@xnetjs/data/store`, `@xnetjs/data/updates`, `@xnetjs/data/awareness`
7
12
  > Experimental entrypoints: `@xnetjs/data/database`, `@xnetjs/data/auth`
@@ -1,12 +1,12 @@
1
1
  import { AllowExpr, DenyExpr, AuthExpression, AndExpr, OrExpr, NotExpr, PublicExpr, AuthenticatedExpr, CreatorRoleResolver, PropertyRoleResolver, RelationRoleResolver, MembershipRoleResolver, AuthorizationDefinition, SerializedAuthorization, SerializedAuthExpression, DID } from '@xnetjs/core';
2
2
  export { AUTH_ACTIONS, AllowExpr, AndExpr, AuthAction, AuthCheckInput, AuthDecision, AuthDenyReason, AuthExpression, AuthTrace, AuthTraceStep, AuthenticatedExpr, AuthorizationDefinition, CreatorRoleResolver, DenyExpr, MembershipRoleResolver, NotExpr, OrExpr, PolicyEvaluator, PropertyRoleResolver, PublicExpr, RelationRoleResolver, RoleRefExpr, RoleResolver, SerializedAuthExpression, SerializedAuthorization, SerializedRoleResolver } from '@xnetjs/core';
3
- import { y as PropertyDefinition, a as Schema, h as NodeState, S as SchemaIRI } from '../types-cycApzib.js';
4
- export { H as DEFAULT_OFFLINE_POLICY, z as GRANT_SCHEMA_IRI, $ as Grant, x as GrantIndex, F as GrantIndexOptions, E as GrantIndexStore, _ as GrantInput, B as GrantNode, Q as GrantRateLimiter, V as GrantRateLimiterOptions, O as OfflineAuthPolicy, K as RevocationConfig, R as RevocationConsistency, W as StoreAuth, e as StoreAuthAPI, a0 as StoreAuthError, a1 as StoreAuthErrorCode, Z as StoreAuthKeyManager, X as StoreAuthOptions, Y as StoreAuthStore, A as isGrantActive, J as mergeOfflinePolicy } from '../types-cycApzib.js';
5
- export { A as ActionPermission, m as AuthDecisionEvent, D as DecisionCache, g as DecisionCacheOptions, c as DefaultPolicyEvaluator, j as DefaultPolicyEvaluatorOptions, a as DefaultRoleResolver, i as DefaultRoleResolverOptions, k as DefinedSchemaLike, G as GrantExpirationCleaner, o as GrantExpirationCleanerOptions, n as GrantExpirationStore, H as HubPolicy, N as NodeStoreReader, P as PermissionMatrix, l as RevalidationEvent, R as RoleSummary, S as SchemaRegistryReader, b as buildPermissionMatrix, f as createPolicyEvaluator, d as describeRoleResolver, e as evaluateExpression, h as hubActionsForSpaceRole, s as schemaToHubPolicy } from '../grant-expiration-cleaner-BwdmRFua.js';
3
+ import { F as PropertyDefinition, a as Schema, h as NodeState, S as SchemaIRI } from '../types-gws1tSf-.js';
4
+ export { a_ as DEFAULT_OFFLINE_POLICY, aY as GRANT_SCHEMA_IRI, b4 as Grant, x as GrantIndex, b9 as GrantIndexOptions, b8 as GrantIndexStore, b3 as GrantInput, b7 as GrantNode, aX as GrantRateLimiter, bc as GrantRateLimiterOptions, O as OfflineAuthPolicy, bb as RevocationConfig, ba as RevocationConsistency, aW as StoreAuth, e as StoreAuthAPI, b5 as StoreAuthError, b6 as StoreAuthErrorCode, b2 as StoreAuthKeyManager, b0 as StoreAuthOptions, b1 as StoreAuthStore, aZ as isGrantActive, a$ as mergeOfflinePolicy } from '../types-gws1tSf-.js';
5
+ export { A as ActionPermission, m as AuthDecisionEvent, b as DecisionCache, g as DecisionCacheOptions, D as DefaultPolicyEvaluator, j as DefaultPolicyEvaluatorOptions, a as DefaultRoleResolver, i as DefaultRoleResolverOptions, k as DefinedSchemaLike, G as GrantExpirationCleaner, o as GrantExpirationCleanerOptions, n as GrantExpirationStore, H as HubPolicy, N as NodeStoreReader, P as PermissionMatrix, l as RevalidationEvent, R as RoleSummary, S as SchemaRegistryReader, c as buildPermissionMatrix, f as createPolicyEvaluator, d as describeRoleResolver, e as evaluateExpression, h as hubActionsForSpaceRole, s as schemaToHubPolicy } from '../grant-expiration-cleaner-CRS9OyhV.js';
6
6
  import { PublicKeyResolver } from '@xnetjs/crypto';
7
7
  import '@xnetjs/sqlite';
8
8
  import '@xnetjs/sync';
9
- import '../registry-D_3b1Lg6.js';
9
+ import '../registry-C9IRLLeR.js';
10
10
 
11
11
  /**
12
12
  * Authorization expression builders for schema definitions.
@@ -1,6 +1,18 @@
1
1
  import {
2
2
  extKey
3
3
  } from "./chunk-PMUQACPY.js";
4
+ import {
5
+ DatabaseRowSchema
6
+ } from "./chunk-AKKBG7HW.js";
7
+ import {
8
+ DatabaseFieldSchema
9
+ } from "./chunk-IKREWSCX.js";
10
+ import {
11
+ DatabaseSelectOptionSchema
12
+ } from "./chunk-XP3VSSXD.js";
13
+ import {
14
+ DatabaseViewSchema
15
+ } from "./chunk-UFV5QWCL.js";
4
16
  import {
5
17
  ExtensionFieldSchema,
6
18
  SchemaExtensionSchema,
@@ -14,18 +26,6 @@ import {
14
26
  needsRebalancing,
15
27
  rebalanceSortKeys
16
28
  } from "./chunk-QWFTRZQT.js";
17
- import {
18
- DatabaseRowSchema
19
- } from "./chunk-AKKBG7HW.js";
20
- import {
21
- DatabaseFieldSchema
22
- } from "./chunk-IKREWSCX.js";
23
- import {
24
- DatabaseSelectOptionSchema
25
- } from "./chunk-XP3VSSXD.js";
26
- import {
27
- DatabaseViewSchema
28
- } from "./chunk-UFV5QWCL.js";
29
29
  import {
30
30
  createNodeQueryDescriptor
31
31
  } from "./chunk-WOEFYS7Y.js";
@@ -3384,6 +3384,18 @@ function evaluateUnary(operator, operand, context) {
3384
3384
  }
3385
3385
  }
3386
3386
  function evaluateCall(name, args, context) {
3387
+ if (name === "RELATED") {
3388
+ const evaluated = args.map((arg) => evaluate(arg, context));
3389
+ const relationColumnId = String(evaluated[0] ?? "");
3390
+ const targetColumnId = evaluated[1] === void 0 ? void 0 : String(evaluated[1]);
3391
+ return context.getRelatedValues?.(relationColumnId, targetColumnId) ?? [];
3392
+ }
3393
+ if (name === "NODE") {
3394
+ const evaluated = args.map((arg) => evaluate(arg, context));
3395
+ const nodeId = String(evaluated[0] ?? "");
3396
+ const property = String(evaluated[1] ?? "");
3397
+ return context.getNodeProperty?.(nodeId, property) ?? null;
3398
+ }
3387
3399
  const fn = FUNCTIONS[name];
3388
3400
  if (!fn) {
3389
3401
  throw new Error(`Unknown function: ${name}`);
@@ -3585,15 +3597,16 @@ var FormulaService = class {
3585
3597
  * const value = service.compute(row, formulaColumn, columns)
3586
3598
  * ```
3587
3599
  */
3588
- compute(row, column, columns) {
3600
+ compute(row, column, columns, scope) {
3589
3601
  if (column.type !== "formula") {
3590
3602
  throw new Error(`Column ${column.id} is not a formula column`);
3591
3603
  }
3592
3604
  const config = column.config;
3605
+ const usesScope = /\b(RELATED|NODE)\s*\(/.test(config.expression);
3593
3606
  const cacheKey = `${row.id}:${column.id}`;
3594
3607
  const deps = config.dependencies ?? extractDependencies(config.expression);
3595
3608
  const hash = this.computeHash(row, deps);
3596
- const cached = this.valueCache.get(cacheKey);
3609
+ const cached = usesScope ? void 0 : this.valueCache.get(cacheKey);
3597
3610
  if (cached && cached.hash === hash) {
3598
3611
  return cached.value;
3599
3612
  }
@@ -3606,16 +3619,18 @@ var FormulaService = class {
3606
3619
  getValue: (columnId) => {
3607
3620
  const depColumn = columns.find((c) => c.id === columnId);
3608
3621
  if (depColumn?.type === "formula") {
3609
- return this.compute(row, depColumn, columns);
3622
+ return this.compute(row, depColumn, columns, scope);
3610
3623
  }
3611
3624
  return row.cells[columnId];
3612
3625
  },
3613
- getColumn: (columnId) => columns.find((c) => c.id === columnId)
3626
+ getColumn: (columnId) => columns.find((c) => c.id === columnId),
3627
+ getRelatedValues: scope?.getRelatedValues,
3628
+ getNodeProperty: scope?.getNodeProperty
3614
3629
  };
3615
3630
  try {
3616
3631
  const value = evaluate(ast, context);
3617
3632
  const coerced = this.coerceResult(value, config.resultType);
3618
- this.valueCache.set(cacheKey, { value: coerced, hash });
3633
+ if (!usesScope) this.valueCache.set(cacheKey, { value: coerced, hash });
3619
3634
  return coerced;
3620
3635
  } catch (error) {
3621
3636
  console.error(`Formula error in ${column.name}:`, error);
@@ -3625,10 +3640,10 @@ var FormulaService = class {
3625
3640
  /**
3626
3641
  * Batch compute formulas for multiple rows.
3627
3642
  */
3628
- batchCompute(rows, column, columns) {
3643
+ batchCompute(rows, column, columns, scope) {
3629
3644
  const results = /* @__PURE__ */ new Map();
3630
3645
  for (const row of rows) {
3631
- const value = this.compute(row, column, columns);
3646
+ const value = this.compute(row, column, columns, scope);
3632
3647
  results.set(row.id, value);
3633
3648
  }
3634
3649
  return results;
@@ -0,0 +1,114 @@
1
+ import {
2
+ spaceCascadeAuthorization
3
+ } from "./chunk-RLRAWZJE.js";
4
+ import {
5
+ checkbox,
6
+ created,
7
+ createdBy,
8
+ defineSchema,
9
+ number,
10
+ relation,
11
+ text
12
+ } from "./chunk-NH6OLO3X.js";
13
+ import {
14
+ allow,
15
+ role
16
+ } from "./chunk-D4LUUZYD.js";
17
+
18
+ // src/schema/schemas/course.ts
19
+ var COURSE_SCHEMA_IRI = "xnet://xnet.fyi/Course@1.0.0";
20
+ var LESSON_SCHEMA_IRI = "xnet://xnet.fyi/Lesson@1.0.0";
21
+ var LESSON_PROGRESS_SCHEMA_IRI = "xnet://xnet.fyi/LessonProgress@1.0.0";
22
+ var CourseSchema = defineSchema({
23
+ name: "Course",
24
+ namespace: "xnet://xnet.fyi/",
25
+ properties: {
26
+ title: text({ required: true, maxLength: 300 }),
27
+ description: text({ maxLength: 2e3 }),
28
+ space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" }),
29
+ icon: text({ maxLength: 500 }),
30
+ /** Order among sibling courses — fractional index, code-unit compare. */
31
+ sortKey: text({ maxLength: 500 }),
32
+ /** Unpublished courses are visible to space admins only. */
33
+ published: checkbox({ default: false }),
34
+ createdAt: created(),
35
+ createdBy: createdBy()
36
+ },
37
+ document: void 0,
38
+ authorization: spaceCascadeAuthorization()
39
+ });
40
+ var LessonSchema = defineSchema({
41
+ name: "Lesson",
42
+ namespace: "xnet://xnet.fyi/",
43
+ properties: {
44
+ title: text({ required: true, maxLength: 300 }),
45
+ course: relation({ target: "xnet://xnet.fyi/Course@1.0.0", required: true }),
46
+ space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" }),
47
+ /** Order within the course — fractional index. */
48
+ sortKey: text({ maxLength: 500 }),
49
+ /** Optional estimated minutes, shown as guidance, never as a timer. */
50
+ estimatedMinutes: number({ min: 0 }),
51
+ createdAt: created(),
52
+ createdBy: createdBy()
53
+ },
54
+ // Lesson bodies are documents, same editor surface as Page/Post.
55
+ document: "yjs",
56
+ authorization: spaceCascadeAuthorization()
57
+ });
58
+ var LessonProgressSchema = defineSchema({
59
+ name: "LessonProgress",
60
+ namespace: "xnet://xnet.fyi/",
61
+ properties: {
62
+ lesson: relation({ target: "xnet://xnet.fyi/Lesson@1.0.0", required: true }),
63
+ course: relation({ target: "xnet://xnet.fyi/Course@1.0.0" }),
64
+ space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" }),
65
+ /** ms since epoch; absent = started but not finished. */
66
+ completedAt: number({}),
67
+ createdAt: created(),
68
+ createdBy: createdBy()
69
+ },
70
+ document: void 0,
71
+ // Written out rather than derived from the space cascade, because the whole
72
+ // point is that it does NOT cascade read to the membership.
73
+ authorization: {
74
+ roles: {
75
+ owner: role.creator(),
76
+ spaceOwner: role.relation("space", "spaceOwner"),
77
+ spaceAdmin: role.relation("space", "spaceAdmin"),
78
+ spaceMember: role.relation("space", "spaceMember")
79
+ },
80
+ actions: {
81
+ // Private by construction: the learner and the space's stewards, and
82
+ // nobody else. There is no membership-wide read of someone's progress.
83
+ read: allow("owner", "spaceOwner", "spaceAdmin"),
84
+ create: allow("spaceOwner", "spaceAdmin", "spaceMember"),
85
+ update: allow("owner"),
86
+ delete: allow("owner", "spaceOwner", "spaceAdmin"),
87
+ write: allow("owner"),
88
+ share: allow("owner")
89
+ }
90
+ }
91
+ });
92
+ function lessonProgressId(lessonId, learnerDid) {
93
+ return `lessonprogress:${lessonId}:${learnerDid}`;
94
+ }
95
+ var courseCompletion = (lessons, ownProgress) => {
96
+ const done = new Set(
97
+ ownProgress.filter((p) => typeof p.completedAt === "number" && p.completedAt > 0).map((p) => p.lesson)
98
+ );
99
+ return {
100
+ completed: lessons.filter((l) => done.has(l.id)).length,
101
+ total: lessons.length
102
+ };
103
+ };
104
+
105
+ export {
106
+ COURSE_SCHEMA_IRI,
107
+ LESSON_SCHEMA_IRI,
108
+ LESSON_PROGRESS_SCHEMA_IRI,
109
+ CourseSchema,
110
+ LessonSchema,
111
+ LessonProgressSchema,
112
+ lessonProgressId,
113
+ courseCompletion
114
+ };
@@ -0,0 +1,67 @@
1
+ import {
2
+ spaceContributorAuthorization
3
+ } from "./chunk-RLRAWZJE.js";
4
+ import {
5
+ checkbox,
6
+ created,
7
+ createdBy,
8
+ defineSchema,
9
+ relation,
10
+ select,
11
+ text
12
+ } from "./chunk-NH6OLO3X.js";
13
+
14
+ // src/schema/schemas/post.ts
15
+ var POST_SCHEMA_IRI = "xnet://xnet.fyi/Post@1.0.0";
16
+ var PostSchema = defineSchema({
17
+ name: "Post",
18
+ namespace: "xnet://xnet.fyi/",
19
+ properties: {
20
+ /** Topic title — what the discussion is about. */
21
+ title: text({ required: true, maxLength: 300 }),
22
+ /** Canonical SECURITY home. A Post without a Space is a personal draft. */
23
+ space: relation({ target: "xnet://xnet.fyi/Space@1.0.0" }),
24
+ /**
25
+ * Optional grouping within a community — "Announcements", "Wins",
26
+ * "Questions". A plain label rather than a relation: communities rename
27
+ * and merge these constantly, and a Tag relation would make the common
28
+ * case (one host, five categories) a two-node write.
29
+ */
30
+ category: text({ maxLength: 80 }),
31
+ /**
32
+ * Pinned topics sort above the rest. This is the ONE ordering lever, and
33
+ * it is an explicit editorial act by a space admin — not an engagement
34
+ * score, and not computed from any behavioural signal.
35
+ */
36
+ pinned: checkbox({ default: false }),
37
+ /** Locked topics accept no new replies; existing ones stay readable. */
38
+ locked: checkbox({ default: false }),
39
+ /** Per-node visibility; `inherit` defers to the Space (exploration 0179). */
40
+ visibility: select({
41
+ options: [
42
+ { id: "inherit", name: "Inherit", color: "gray" },
43
+ { id: "private", name: "Private", color: "gray" },
44
+ { id: "unlisted", name: "Unlisted", color: "yellow" },
45
+ { id: "public", name: "Public", color: "green" }
46
+ ],
47
+ default: "inherit"
48
+ }),
49
+ createdAt: created(),
50
+ createdBy: createdBy()
51
+ },
52
+ // Rich body, same editor surface as Page (BlockNote over a Y.Doc).
53
+ document: "yjs",
54
+ authorization: spaceContributorAuthorization()
55
+ });
56
+ var comparePostsForFeed = (a, b) => {
57
+ const aPinned = a.pinned === true;
58
+ const bPinned = b.pinned === true;
59
+ if (aPinned !== bPinned) return aPinned ? -1 : 1;
60
+ return (b.createdAt ?? 0) - (a.createdAt ?? 0);
61
+ };
62
+
63
+ export {
64
+ POST_SCHEMA_IRI,
65
+ PostSchema,
66
+ comparePostsForFeed
67
+ };