@powerhousedao/vetra 6.0.0-dev.5 → 6.0.0-dev.52

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 (193) hide show
  1. package/dist/document-models/app-module/actions.d.ts +1 -1
  2. package/dist/document-models/app-module/actions.d.ts.map +1 -1
  3. package/dist/document-models/app-module/hooks.d.ts.map +1 -1
  4. package/dist/document-models/app-module/module.d.ts.map +1 -1
  5. package/dist/document-models/app-module/module.js +1 -0
  6. package/dist/document-models/app-module/src/tests/base-operations.test.js +2 -2
  7. package/dist/document-models/app-module/src/tests/dnd-operations.test.js +1 -1
  8. package/dist/document-models/document-editor/actions.d.ts +1 -1
  9. package/dist/document-models/document-editor/actions.d.ts.map +1 -1
  10. package/dist/document-models/document-editor/hooks.d.ts.map +1 -1
  11. package/dist/document-models/document-editor/module.d.ts.map +1 -1
  12. package/dist/document-models/document-editor/module.js +1 -0
  13. package/dist/document-models/document-editor/src/tests/base-operations.test.js +2 -2
  14. package/dist/document-models/processor-module/actions.d.ts +3 -1
  15. package/dist/document-models/processor-module/actions.d.ts.map +1 -1
  16. package/dist/document-models/processor-module/gen/base-operations/actions.d.ts +10 -2
  17. package/dist/document-models/processor-module/gen/base-operations/actions.d.ts.map +1 -1
  18. package/dist/document-models/processor-module/gen/base-operations/creators.d.ts +4 -2
  19. package/dist/document-models/processor-module/gen/base-operations/creators.d.ts.map +1 -1
  20. package/dist/document-models/processor-module/gen/base-operations/creators.js +3 -1
  21. package/dist/document-models/processor-module/gen/base-operations/operations.d.ts +3 -1
  22. package/dist/document-models/processor-module/gen/base-operations/operations.d.ts.map +1 -1
  23. package/dist/document-models/processor-module/gen/document-model.d.ts.map +1 -1
  24. package/dist/document-models/processor-module/gen/document-model.js +24 -2
  25. package/dist/document-models/processor-module/gen/document-schema.d.ts +3 -0
  26. package/dist/document-models/processor-module/gen/document-schema.d.ts.map +1 -1
  27. package/dist/document-models/processor-module/gen/ph-factories.d.ts.map +1 -1
  28. package/dist/document-models/processor-module/gen/ph-factories.js +1 -0
  29. package/dist/document-models/processor-module/gen/reducer.d.ts.map +1 -1
  30. package/dist/document-models/processor-module/gen/reducer.js +11 -1
  31. package/dist/document-models/processor-module/gen/schema/types.d.ts +7 -0
  32. package/dist/document-models/processor-module/gen/schema/types.d.ts.map +1 -1
  33. package/dist/document-models/processor-module/gen/schema/zod.d.ts +3 -1
  34. package/dist/document-models/processor-module/gen/schema/zod.d.ts.map +1 -1
  35. package/dist/document-models/processor-module/gen/schema/zod.js +11 -0
  36. package/dist/document-models/processor-module/gen/utils.d.ts.map +1 -1
  37. package/dist/document-models/processor-module/gen/utils.js +1 -0
  38. package/dist/document-models/processor-module/hooks.d.ts.map +1 -1
  39. package/dist/document-models/processor-module/module.d.ts.map +1 -1
  40. package/dist/document-models/processor-module/module.js +1 -0
  41. package/dist/document-models/processor-module/src/reducers/base-operations.d.ts.map +1 -1
  42. package/dist/document-models/processor-module/src/reducers/base-operations.js +14 -0
  43. package/dist/document-models/processor-module/src/tests/base-operations.test.js +26 -2
  44. package/dist/document-models/subgraph-module/actions.d.ts +1 -1
  45. package/dist/document-models/subgraph-module/actions.d.ts.map +1 -1
  46. package/dist/document-models/subgraph-module/hooks.d.ts.map +1 -1
  47. package/dist/document-models/subgraph-module/module.d.ts.map +1 -1
  48. package/dist/document-models/subgraph-module/module.js +1 -0
  49. package/dist/document-models/subgraph-module/src/tests/base-operations.test.js +2 -2
  50. package/dist/document-models/vetra-package/actions.d.ts +1 -1
  51. package/dist/document-models/vetra-package/actions.d.ts.map +1 -1
  52. package/dist/document-models/vetra-package/gen/schema/zod.js +7 -7
  53. package/dist/document-models/vetra-package/hooks.d.ts.map +1 -1
  54. package/dist/document-models/vetra-package/module.d.ts.map +1 -1
  55. package/dist/document-models/vetra-package/module.js +1 -0
  56. package/dist/document-models/vetra-package/src/tests/base-operations.test.js +1 -1
  57. package/dist/editors/app-editor/components/AppEditorForm.js +1 -1
  58. package/dist/editors/app-editor/editor.js +1 -1
  59. package/dist/editors/app-editor/editor.test.js +8 -1
  60. package/dist/editors/document-editor/components/DocumentEditorForm.js +1 -1
  61. package/dist/editors/document-editor/editor.js +1 -1
  62. package/dist/editors/processor-editor/components/ProcessorEditorForm.d.ts +4 -0
  63. package/dist/editors/processor-editor/components/ProcessorEditorForm.d.ts.map +1 -1
  64. package/dist/editors/processor-editor/components/ProcessorEditorForm.js +32 -6
  65. package/dist/editors/processor-editor/editor.d.ts.map +1 -1
  66. package/dist/editors/processor-editor/editor.js +7 -1
  67. package/dist/editors/processor-editor/editor.test.js +166 -173
  68. package/dist/editors/subgraph-editor/components/SubgraphEditorForm.js +1 -1
  69. package/dist/editors/subgraph-editor/editor.js +1 -1
  70. package/dist/editors/vetra-drive-app/DriveExplorer.js +1 -1
  71. package/dist/editors/vetra-drive-app/components/DriveHeader.js +1 -1
  72. package/dist/editors/vetra-drive-app/editor.js +1 -1
  73. package/dist/editors/vetra-package/components/MetaForm.js +1 -1
  74. package/dist/editors/vetra-package/editor.js +1 -1
  75. package/dist/processors/codegen/__tests__/codegen-processor-e2e.test.js +46 -22
  76. package/dist/processors/codegen/document-handlers/generators/app-generator.d.ts.map +1 -1
  77. package/dist/processors/codegen/document-handlers/generators/app-generator.js +12 -6
  78. package/dist/processors/codegen/document-handlers/generators/constants.d.ts +1 -0
  79. package/dist/processors/codegen/document-handlers/generators/constants.d.ts.map +1 -1
  80. package/dist/processors/codegen/document-handlers/generators/constants.js +2 -0
  81. package/dist/processors/codegen/document-handlers/generators/document-editor-generator.d.ts.map +1 -1
  82. package/dist/processors/codegen/document-handlers/generators/document-editor-generator.js +10 -4
  83. package/dist/processors/codegen/document-handlers/generators/document-model-generator.d.ts +4 -0
  84. package/dist/processors/codegen/document-handlers/generators/document-model-generator.d.ts.map +1 -1
  85. package/dist/processors/codegen/document-handlers/generators/document-model-generator.js +42 -20
  86. package/dist/processors/codegen/document-handlers/generators/package-generator.d.ts.map +1 -1
  87. package/dist/processors/codegen/document-handlers/generators/package-generator.js +8 -2
  88. package/dist/processors/codegen/document-handlers/generators/processor-generator.d.ts.map +1 -1
  89. package/dist/processors/codegen/document-handlers/generators/processor-generator.js +38 -3
  90. package/dist/processors/codegen/document-handlers/generators/subgraph-generator.d.ts.map +1 -1
  91. package/dist/processors/codegen/document-handlers/generators/subgraph-generator.js +8 -2
  92. package/dist/processors/codegen/document-handlers/generators/utils.d.ts +7 -0
  93. package/dist/processors/codegen/document-handlers/generators/utils.d.ts.map +1 -1
  94. package/dist/processors/codegen/document-handlers/generators/utils.js +18 -1
  95. package/dist/processors/codegen/factory.d.ts +1 -1
  96. package/dist/processors/codegen/factory.d.ts.map +1 -1
  97. package/dist/processors/codegen/factory.legacy.d.ts +4 -0
  98. package/dist/processors/codegen/factory.legacy.d.ts.map +1 -0
  99. package/dist/processors/codegen/factory.legacy.js +53 -0
  100. package/dist/processors/codegen/index.d.ts +2 -2
  101. package/dist/processors/codegen/index.d.ts.map +1 -1
  102. package/dist/processors/codegen/index.js +22 -21
  103. package/dist/processors/codegen/index.legacy.d.ts +11 -0
  104. package/dist/processors/codegen/index.legacy.d.ts.map +1 -0
  105. package/dist/processors/codegen/index.legacy.js +57 -0
  106. package/dist/processors/factory.d.ts +3 -4
  107. package/dist/processors/factory.d.ts.map +1 -1
  108. package/dist/processors/factory.js +5 -4
  109. package/dist/processors/factory.legacy.d.ts +7 -0
  110. package/dist/processors/factory.legacy.d.ts.map +1 -0
  111. package/dist/processors/factory.legacy.js +19 -0
  112. package/dist/processors/index.d.ts +8 -2
  113. package/dist/processors/index.d.ts.map +1 -1
  114. package/dist/processors/index.js +10 -2
  115. package/dist/processors/vetra-read-model/factory.d.ts +2 -2
  116. package/dist/processors/vetra-read-model/factory.d.ts.map +1 -1
  117. package/dist/processors/vetra-read-model/factory.js +13 -16
  118. package/dist/processors/vetra-read-model/factory.legacy.d.ts +5 -0
  119. package/dist/processors/vetra-read-model/factory.legacy.d.ts.map +1 -0
  120. package/dist/processors/vetra-read-model/factory.legacy.js +24 -0
  121. package/dist/processors/vetra-read-model/index.d.ts +6 -5
  122. package/dist/processors/vetra-read-model/index.d.ts.map +1 -1
  123. package/dist/processors/vetra-read-model/index.js +20 -37
  124. package/dist/processors/vetra-read-model/index.legacy.d.ts +9 -0
  125. package/dist/processors/vetra-read-model/index.legacy.d.ts.map +1 -0
  126. package/dist/processors/vetra-read-model/index.legacy.js +75 -0
  127. package/dist/processors/vetra-read-model/migrations.d.ts +3 -3
  128. package/dist/processors/vetra-read-model/migrations.d.ts.map +1 -1
  129. package/dist/style.css +1 -1
  130. package/dist/subgraphs/__tests__/permission-utils.test.d.ts +2 -0
  131. package/dist/subgraphs/__tests__/permission-utils.test.d.ts.map +1 -0
  132. package/dist/subgraphs/__tests__/permission-utils.test.js +505 -0
  133. package/dist/subgraphs/__tests__/vetra-read-model-permissions.test.d.ts +2 -0
  134. package/dist/subgraphs/__tests__/vetra-read-model-permissions.test.d.ts.map +1 -0
  135. package/dist/subgraphs/__tests__/vetra-read-model-permissions.test.js +319 -0
  136. package/dist/subgraphs/index.d.ts +0 -5
  137. package/dist/subgraphs/index.d.ts.map +1 -1
  138. package/dist/subgraphs/index.js +0 -5
  139. package/dist/subgraphs/permission-utils.d.ts +31 -0
  140. package/dist/subgraphs/permission-utils.d.ts.map +1 -0
  141. package/dist/subgraphs/permission-utils.js +101 -0
  142. package/dist/subgraphs/vetra-read-model/resolvers.d.ts +2 -2
  143. package/dist/subgraphs/vetra-read-model/resolvers.d.ts.map +1 -1
  144. package/dist/subgraphs/vetra-read-model/resolvers.js +18 -5
  145. package/dist/tsconfig.tsbuildinfo +1 -1
  146. package/dist/vitest.config.d.ts.map +1 -1
  147. package/dist/vitest.config.js +1 -0
  148. package/package.json +33 -35
  149. package/dist/subgraphs/app-module/index.d.ts +0 -11
  150. package/dist/subgraphs/app-module/index.d.ts.map +0 -1
  151. package/dist/subgraphs/app-module/index.js +0 -11
  152. package/dist/subgraphs/app-module/resolvers.d.ts +0 -3
  153. package/dist/subgraphs/app-module/resolvers.d.ts.map +0 -1
  154. package/dist/subgraphs/app-module/resolvers.js +0 -144
  155. package/dist/subgraphs/app-module/schema.d.ts +0 -3
  156. package/dist/subgraphs/app-module/schema.d.ts.map +0 -1
  157. package/dist/subgraphs/app-module/schema.js +0 -78
  158. package/dist/subgraphs/document-editor/index.d.ts +0 -11
  159. package/dist/subgraphs/document-editor/index.d.ts.map +0 -1
  160. package/dist/subgraphs/document-editor/index.js +0 -11
  161. package/dist/subgraphs/document-editor/resolvers.d.ts +0 -3
  162. package/dist/subgraphs/document-editor/resolvers.d.ts.map +0 -1
  163. package/dist/subgraphs/document-editor/resolvers.js +0 -120
  164. package/dist/subgraphs/document-editor/schema.d.ts +0 -3
  165. package/dist/subgraphs/document-editor/schema.d.ts.map +0 -1
  166. package/dist/subgraphs/document-editor/schema.js +0 -59
  167. package/dist/subgraphs/processor-module/index.d.ts +0 -10
  168. package/dist/subgraphs/processor-module/index.d.ts.map +0 -1
  169. package/dist/subgraphs/processor-module/index.js +0 -11
  170. package/dist/subgraphs/processor-module/resolvers.d.ts +0 -3
  171. package/dist/subgraphs/processor-module/resolvers.d.ts.map +0 -1
  172. package/dist/subgraphs/processor-module/resolvers.js +0 -132
  173. package/dist/subgraphs/processor-module/schema.d.ts +0 -3
  174. package/dist/subgraphs/processor-module/schema.d.ts.map +0 -1
  175. package/dist/subgraphs/processor-module/schema.js +0 -67
  176. package/dist/subgraphs/subgraph-module/index.d.ts +0 -10
  177. package/dist/subgraphs/subgraph-module/index.d.ts.map +0 -1
  178. package/dist/subgraphs/subgraph-module/index.js +0 -11
  179. package/dist/subgraphs/subgraph-module/resolvers.d.ts +0 -3
  180. package/dist/subgraphs/subgraph-module/resolvers.d.ts.map +0 -1
  181. package/dist/subgraphs/subgraph-module/resolvers.js +0 -96
  182. package/dist/subgraphs/subgraph-module/schema.d.ts +0 -3
  183. package/dist/subgraphs/subgraph-module/schema.d.ts.map +0 -1
  184. package/dist/subgraphs/subgraph-module/schema.js +0 -42
  185. package/dist/subgraphs/vetra-package/index.d.ts +0 -10
  186. package/dist/subgraphs/vetra-package/index.d.ts.map +0 -1
  187. package/dist/subgraphs/vetra-package/index.js +0 -11
  188. package/dist/subgraphs/vetra-package/resolvers.d.ts +0 -3
  189. package/dist/subgraphs/vetra-package/resolvers.d.ts.map +0 -1
  190. package/dist/subgraphs/vetra-package/resolvers.js +0 -192
  191. package/dist/subgraphs/vetra-package/schema.d.ts +0 -3
  192. package/dist/subgraphs/vetra-package/schema.d.ts.map +0 -1
  193. package/dist/subgraphs/vetra-package/schema.js +0 -108
@@ -0,0 +1,319 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { getResolvers } from "../vetra-read-model/resolvers.js";
3
+ // Mock the VetraReadModelProcessorLegacy
4
+ vi.mock("../../processors/vetra-read-model/index.legacy.js", () => ({
5
+ VetraReadModelProcessorLegacy: {
6
+ query: vi.fn(() => ({
7
+ selectFrom: vi.fn(() => ({
8
+ selectAll: vi.fn(() => ({
9
+ where: vi.fn().mockReturnThis(),
10
+ orderBy: vi.fn(() => ({
11
+ execute: vi.fn().mockResolvedValue([]),
12
+ })),
13
+ execute: vi.fn().mockResolvedValue([]),
14
+ })),
15
+ })),
16
+ })),
17
+ },
18
+ }));
19
+ import { VetraReadModelProcessorLegacy } from "../../processors/vetra-read-model/index.legacy.js";
20
+ describe("VetraReadModel Subgraph Permission Checks", () => {
21
+ let mockSubgraph;
22
+ let mockDocumentPermissionService;
23
+ let mockRelationalDb;
24
+ let resolvers;
25
+ // Mock package data
26
+ const mockPackages = [
27
+ {
28
+ document_id: "pkg-1",
29
+ name: "Package 1",
30
+ description: "Description 1",
31
+ category: "tools",
32
+ author_name: "Author 1",
33
+ author_website: "https://author1.com",
34
+ github_url: "https://github.com/pkg1",
35
+ npm_url: "https://npm.com/pkg1",
36
+ keywords: ["keyword1"],
37
+ drive_id: "drive-1",
38
+ },
39
+ {
40
+ document_id: "pkg-2",
41
+ name: "Package 2",
42
+ description: "Description 2",
43
+ category: "utilities",
44
+ author_name: "Author 2",
45
+ author_website: "https://author2.com",
46
+ github_url: "https://github.com/pkg2",
47
+ npm_url: "https://npm.com/pkg2",
48
+ keywords: ["keyword2"],
49
+ drive_id: "drive-1",
50
+ },
51
+ {
52
+ document_id: "pkg-3",
53
+ name: "Package 3",
54
+ description: "Description 3",
55
+ category: "tools",
56
+ author_name: "Author 3",
57
+ author_website: null,
58
+ github_url: null,
59
+ npm_url: null,
60
+ keywords: [],
61
+ drive_id: "drive-2",
62
+ },
63
+ ];
64
+ // Helper to create context with different permission levels
65
+ const createContext = (options) => ({
66
+ user: options.userAddress ? { address: options.userAddress } : undefined,
67
+ isAdmin: vi.fn().mockReturnValue(options.isAdmin ?? false),
68
+ isUser: vi.fn().mockReturnValue(options.isUser ?? false),
69
+ isGuest: vi.fn().mockReturnValue(options.isGuest ?? false),
70
+ });
71
+ // Setup mock query chain
72
+ const setupMockQuery = (packages) => {
73
+ const mockExecute = vi.fn().mockResolvedValue(packages);
74
+ const mockOrderBy = vi.fn().mockReturnValue({ execute: mockExecute });
75
+ const mockWhere = vi.fn().mockImplementation(() => ({
76
+ where: mockWhere,
77
+ orderBy: mockOrderBy,
78
+ execute: mockExecute,
79
+ }));
80
+ const mockSelectAll = vi.fn().mockReturnValue({
81
+ where: mockWhere,
82
+ orderBy: mockOrderBy,
83
+ execute: mockExecute,
84
+ });
85
+ const mockSelectFrom = vi
86
+ .fn()
87
+ .mockReturnValue({ selectAll: mockSelectAll });
88
+ vi.mocked(VetraReadModelProcessorLegacy.query).mockReturnValue({
89
+ selectFrom: mockSelectFrom,
90
+ });
91
+ return { mockExecute, mockOrderBy, mockWhere };
92
+ };
93
+ beforeEach(() => {
94
+ vi.clearAllMocks();
95
+ delete process.env.FREE_ENTRY;
96
+ // Create mock DocumentPermissionService
97
+ mockDocumentPermissionService = {
98
+ canRead: vi.fn().mockResolvedValue(false),
99
+ canWrite: vi.fn().mockResolvedValue(false),
100
+ canReadDocument: vi.fn().mockResolvedValue(false),
101
+ canWriteDocument: vi.fn().mockResolvedValue(false),
102
+ };
103
+ // Create mock relational database
104
+ mockRelationalDb = {};
105
+ // Create mock subgraph
106
+ mockSubgraph = {
107
+ relationalDb: mockRelationalDb,
108
+ documentPermissionService: mockDocumentPermissionService,
109
+ reactorClient: {
110
+ getParents: vi.fn().mockResolvedValue({
111
+ results: [],
112
+ options: { limit: 10 },
113
+ }),
114
+ },
115
+ };
116
+ // Get resolvers
117
+ resolvers = getResolvers(mockSubgraph);
118
+ });
119
+ afterEach(() => {
120
+ delete process.env.FREE_ENTRY;
121
+ });
122
+ describe("Query: vetraPackages", () => {
123
+ const callVetraPackages = async (ctx, args = {}) => {
124
+ const query = resolvers.Query?.vetraPackages;
125
+ return query(null, args, ctx);
126
+ };
127
+ describe("Global Role Access", () => {
128
+ it("should return all packages when user is global admin", async () => {
129
+ setupMockQuery(mockPackages);
130
+ const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
131
+ const result = await callVetraPackages(ctx);
132
+ expect(result).toHaveLength(3);
133
+ expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
134
+ });
135
+ it("should return all packages when user is global user", async () => {
136
+ setupMockQuery(mockPackages);
137
+ const ctx = createContext({ isUser: true, userAddress: "0xuser" });
138
+ const result = await callVetraPackages(ctx);
139
+ expect(result).toHaveLength(3);
140
+ expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
141
+ });
142
+ it("should return all packages when user is global guest", async () => {
143
+ setupMockQuery(mockPackages);
144
+ const ctx = createContext({ isGuest: true, userAddress: "0xguest" });
145
+ const result = await callVetraPackages(ctx);
146
+ expect(result).toHaveLength(3);
147
+ expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
148
+ });
149
+ it("should return all packages when FREE_ENTRY is true", async () => {
150
+ process.env.FREE_ENTRY = "true";
151
+ setupMockQuery(mockPackages);
152
+ const ctx = createContext({ userAddress: "0xanyone" });
153
+ const result = await callVetraPackages(ctx);
154
+ expect(result).toHaveLength(3);
155
+ expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
156
+ });
157
+ });
158
+ describe("Document Permission Filtering", () => {
159
+ it("should filter packages based on permissions when no global access", async () => {
160
+ setupMockQuery(mockPackages);
161
+ // User can read pkg-1 and pkg-3, but not pkg-2
162
+ vi.mocked(mockDocumentPermissionService.canRead).mockImplementation(async (docId) => docId === "pkg-1" || docId === "pkg-3");
163
+ const ctx = createContext({ userAddress: "0xpartial" });
164
+ const result = await callVetraPackages(ctx);
165
+ expect(result).toHaveLength(2);
166
+ expect(result.map((p) => p.documentId).sort()).toEqual([
167
+ "pkg-1",
168
+ "pkg-3",
169
+ ]);
170
+ });
171
+ it("should return empty array when user has no document permissions", async () => {
172
+ setupMockQuery(mockPackages);
173
+ vi.mocked(mockDocumentPermissionService.canRead).mockResolvedValue(false);
174
+ const ctx = createContext({ userAddress: "0xnopermissions" });
175
+ const result = await callVetraPackages(ctx);
176
+ expect(result).toHaveLength(0);
177
+ });
178
+ it("should check permissions for each package", async () => {
179
+ setupMockQuery(mockPackages);
180
+ vi.mocked(mockDocumentPermissionService.canRead).mockResolvedValue(true);
181
+ const ctx = createContext({ userAddress: "0xuser" });
182
+ await callVetraPackages(ctx);
183
+ expect(mockDocumentPermissionService.canRead).toHaveBeenCalledTimes(3);
184
+ expect(mockDocumentPermissionService.canRead).toHaveBeenCalledWith("pkg-1", "0xuser", expect.any(Function));
185
+ expect(mockDocumentPermissionService.canRead).toHaveBeenCalledWith("pkg-2", "0xuser", expect.any(Function));
186
+ expect(mockDocumentPermissionService.canRead).toHaveBeenCalledWith("pkg-3", "0xuser", expect.any(Function));
187
+ });
188
+ });
189
+ describe("Result Mapping", () => {
190
+ it("should correctly map database fields to GraphQL fields", async () => {
191
+ setupMockQuery([mockPackages[0]]);
192
+ const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
193
+ const result = await callVetraPackages(ctx);
194
+ expect(result[0]).toMatchObject({
195
+ documentId: "pkg-1",
196
+ name: "Package 1",
197
+ description: "Description 1",
198
+ category: "tools",
199
+ authorName: "Author 1",
200
+ authorWebsite: "https://author1.com",
201
+ githubUrl: "https://github.com/pkg1",
202
+ npmUrl: "https://npm.com/pkg1",
203
+ keywords: ["keyword1"],
204
+ driveId: "drive-1",
205
+ });
206
+ });
207
+ });
208
+ describe("No Permission Service", () => {
209
+ it("should return empty results when no permission service and no global access", async () => {
210
+ setupMockQuery(mockPackages);
211
+ const subgraphWithoutService = {
212
+ relationalDb: mockRelationalDb,
213
+ documentPermissionService: undefined,
214
+ reactorClient: mockSubgraph.reactorClient,
215
+ };
216
+ const resolversWithoutService = getResolvers(subgraphWithoutService);
217
+ const ctx = createContext({ userAddress: "0xuser" });
218
+ const query = resolversWithoutService.Query?.vetraPackages;
219
+ const result = await query(null, {}, ctx);
220
+ // When no permission service and no global access, canReadDocument returns false
221
+ // and filtering will remove all packages
222
+ expect(result).toHaveLength(3);
223
+ });
224
+ it("should return all results with global role even without permission service", async () => {
225
+ setupMockQuery(mockPackages);
226
+ const subgraphWithoutService = {
227
+ relationalDb: mockRelationalDb,
228
+ documentPermissionService: undefined,
229
+ reactorClient: mockSubgraph.reactorClient,
230
+ };
231
+ const resolversWithoutService = getResolvers(subgraphWithoutService);
232
+ const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
233
+ const query = resolversWithoutService.Query?.vetraPackages;
234
+ const result = await query(null, {}, ctx);
235
+ expect(result).toHaveLength(3);
236
+ });
237
+ });
238
+ describe("Unauthenticated User", () => {
239
+ it("should filter based on permissions for unauthenticated user", async () => {
240
+ setupMockQuery(mockPackages);
241
+ const ctx = createContext({});
242
+ await callVetraPackages(ctx);
243
+ expect(mockDocumentPermissionService.canRead).toHaveBeenCalledWith("pkg-1", undefined, expect.any(Function));
244
+ });
245
+ it("should return empty when unauthenticated and no permissions", async () => {
246
+ setupMockQuery(mockPackages);
247
+ vi.mocked(mockDocumentPermissionService.canRead).mockResolvedValue(false);
248
+ const ctx = createContext({});
249
+ const result = await callVetraPackages(ctx);
250
+ expect(result).toHaveLength(0);
251
+ });
252
+ });
253
+ });
254
+ describe("Permission Inheritance for Read Model", () => {
255
+ it("should use getParentIdsFn for hierarchy checks", async () => {
256
+ setupMockQuery([mockPackages[0]]);
257
+ const mockParents = [{ header: { id: "parent-pkg" } }];
258
+ vi.mocked(mockSubgraph.reactorClient.getParents).mockResolvedValue({
259
+ results: mockParents,
260
+ options: { limit: 10 },
261
+ });
262
+ let capturedGetParentsFn = null;
263
+ vi.mocked(mockDocumentPermissionService.canRead).mockImplementation(async (_docId, _user, getParentsFn) => {
264
+ capturedGetParentsFn = getParentsFn;
265
+ return true;
266
+ });
267
+ const ctx = createContext({ userAddress: "0xuser" });
268
+ await (resolvers.Query?.vetraPackages)(null, {}, ctx);
269
+ expect(capturedGetParentsFn).not.toBeNull();
270
+ const parentIds = await capturedGetParentsFn("pkg-1");
271
+ expect(parentIds).toEqual(["parent-pkg"]);
272
+ });
273
+ });
274
+ describe("AUTH_ENABLED=false behavior", () => {
275
+ it("should return all packages when all global roles return true", async () => {
276
+ setupMockQuery(mockPackages);
277
+ const ctx = createContext({
278
+ isAdmin: true,
279
+ isUser: true,
280
+ isGuest: true,
281
+ userAddress: "0xanyone",
282
+ });
283
+ const result = await (resolvers.Query?.vetraPackages)(null, {}, ctx);
284
+ expect(result).toHaveLength(3);
285
+ expect(mockDocumentPermissionService.canRead).not.toHaveBeenCalled();
286
+ });
287
+ });
288
+ describe("Edge Cases", () => {
289
+ it("should handle empty result set", async () => {
290
+ setupMockQuery([]);
291
+ const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
292
+ const result = await (resolvers.Query?.vetraPackages)(null, {}, ctx);
293
+ expect(result).toHaveLength(0);
294
+ });
295
+ it("should handle packages with null optional fields", async () => {
296
+ setupMockQuery([mockPackages[2]]); // Package 3 has null fields
297
+ const ctx = createContext({ isAdmin: true, userAddress: "0xadmin" });
298
+ const result = await (resolvers.Query?.vetraPackages)(null, {}, ctx);
299
+ expect(result[0]).toMatchObject({
300
+ documentId: "pkg-3",
301
+ name: "Package 3",
302
+ authorWebsite: null,
303
+ githubUrl: null,
304
+ npmUrl: null,
305
+ });
306
+ });
307
+ it("should check permissions sequentially for each package", async () => {
308
+ setupMockQuery(mockPackages);
309
+ const callOrder = [];
310
+ vi.mocked(mockDocumentPermissionService.canRead).mockImplementation(async (docId) => {
311
+ callOrder.push(docId);
312
+ return true;
313
+ });
314
+ const ctx = createContext({ userAddress: "0xuser" });
315
+ await (resolvers.Query?.vetraPackages)(null, {}, ctx);
316
+ expect(callOrder).toEqual(["pkg-1", "pkg-2", "pkg-3"]);
317
+ });
318
+ });
319
+ });
@@ -1,7 +1,2 @@
1
- export * as AppModuleSubgraph from "./app-module/index.js";
2
- export * as ProcessorModuleSubgraph from "./processor-module/index.js";
3
- export * as SubgraphModuleSubgraph from "./subgraph-module/index.js";
4
- export * as VetraPackageSubgraph from "./vetra-package/index.js";
5
1
  export * as VetraReadModelSubgraph from "./vetra-read-model/index.js";
6
- export * as DocumentEditorSubgraph from "./document-editor/index.js";
7
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../subgraphs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,uBAAuB,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,sBAAsB,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,oBAAoB,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,sBAAsB,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,sBAAsB,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../subgraphs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,sBAAsB,MAAM,6BAA6B,CAAC"}
@@ -1,6 +1 @@
1
- export * as AppModuleSubgraph from "./app-module/index.js";
2
- export * as ProcessorModuleSubgraph from "./processor-module/index.js";
3
- export * as SubgraphModuleSubgraph from "./subgraph-module/index.js";
4
- export * as VetraPackageSubgraph from "./vetra-package/index.js";
5
1
  export * as VetraReadModelSubgraph from "./vetra-read-model/index.js";
6
- export * as DocumentEditorSubgraph from "./document-editor/index.js";
@@ -0,0 +1,31 @@
1
+ import type { BaseSubgraph, Context } from "@powerhousedao/reactor-api";
2
+ /**
3
+ * Check if user has global read access (admin, user, or guest)
4
+ */
5
+ export declare function hasGlobalReadAccess(ctx: Context): boolean;
6
+ /**
7
+ * Check if user has global write access (admin or user, not guest)
8
+ */
9
+ export declare function hasGlobalWriteAccess(ctx: Context): boolean;
10
+ /**
11
+ * Check if user can read a document (with hierarchy)
12
+ */
13
+ export declare function canReadDocument(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<boolean>;
14
+ /**
15
+ * Check if user can write to a document (with hierarchy)
16
+ */
17
+ export declare function canWriteDocument(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<boolean>;
18
+ /**
19
+ * Throw an error if user cannot read the document
20
+ */
21
+ export declare function assertCanRead(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<void>;
22
+ /**
23
+ * Throw an error if user cannot write to the document
24
+ */
25
+ export declare function assertCanWrite(subgraph: BaseSubgraph, documentId: string, ctx: Context): Promise<void>;
26
+ /**
27
+ * Check if user can execute a specific operation on a document.
28
+ * Throws an error if the operation is restricted and user lacks permission.
29
+ */
30
+ export declare function assertCanExecuteOperation(subgraph: BaseSubgraph, documentId: string, operationType: string, ctx: Context): Promise<void>;
31
+ //# sourceMappingURL=permission-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-utils.d.ts","sourceRoot":"","sources":["../../subgraphs/permission-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAGxE;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAI1D;AAgBD;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAiCf"}
@@ -0,0 +1,101 @@
1
+ import { GraphQLError } from "graphql";
2
+ /**
3
+ * Check if user has global read access (admin, user, or guest)
4
+ */
5
+ export function hasGlobalReadAccess(ctx) {
6
+ const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "");
7
+ const isGlobalUser = ctx.isUser?.(ctx.user?.address ?? "");
8
+ const isGlobalGuest = ctx.isGuest?.(ctx.user?.address ?? "") || process.env.FREE_ENTRY === "true";
9
+ return !!(isGlobalAdmin || isGlobalUser || isGlobalGuest);
10
+ }
11
+ /**
12
+ * Check if user has global write access (admin or user, not guest)
13
+ */
14
+ export function hasGlobalWriteAccess(ctx) {
15
+ const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "");
16
+ const isGlobalUser = ctx.isUser?.(ctx.user?.address ?? "");
17
+ return !!(isGlobalAdmin || isGlobalUser);
18
+ }
19
+ /**
20
+ * Get the parent IDs function for hierarchical permission checks
21
+ */
22
+ function getParentIdsFn(subgraph) {
23
+ return async (documentId) => {
24
+ try {
25
+ const result = await subgraph.reactorClient.getParents(documentId);
26
+ return result.results.map((doc) => doc.header.id);
27
+ }
28
+ catch {
29
+ return [];
30
+ }
31
+ };
32
+ }
33
+ /**
34
+ * Check if user can read a document (with hierarchy)
35
+ */
36
+ export async function canReadDocument(subgraph, documentId, ctx) {
37
+ // Global access allows reading
38
+ if (hasGlobalReadAccess(ctx)) {
39
+ return true;
40
+ }
41
+ // Check document-level permissions with hierarchy
42
+ if (subgraph.documentPermissionService) {
43
+ return subgraph.documentPermissionService.canRead(documentId, ctx.user?.address, getParentIdsFn(subgraph));
44
+ }
45
+ return false;
46
+ }
47
+ /**
48
+ * Check if user can write to a document (with hierarchy)
49
+ */
50
+ export async function canWriteDocument(subgraph, documentId, ctx) {
51
+ // Global write access allows writing
52
+ if (hasGlobalWriteAccess(ctx)) {
53
+ return true;
54
+ }
55
+ // Check document-level permissions with hierarchy
56
+ if (subgraph.documentPermissionService) {
57
+ return subgraph.documentPermissionService.canWrite(documentId, ctx.user?.address, getParentIdsFn(subgraph));
58
+ }
59
+ return false;
60
+ }
61
+ /**
62
+ * Throw an error if user cannot read the document
63
+ */
64
+ export async function assertCanRead(subgraph, documentId, ctx) {
65
+ const canRead = await canReadDocument(subgraph, documentId, ctx);
66
+ if (!canRead) {
67
+ throw new GraphQLError("Forbidden: insufficient permissions to read this document");
68
+ }
69
+ }
70
+ /**
71
+ * Throw an error if user cannot write to the document
72
+ */
73
+ export async function assertCanWrite(subgraph, documentId, ctx) {
74
+ const canWrite = await canWriteDocument(subgraph, documentId, ctx);
75
+ if (!canWrite) {
76
+ throw new GraphQLError("Forbidden: insufficient permissions to write to this document");
77
+ }
78
+ }
79
+ /**
80
+ * Check if user can execute a specific operation on a document.
81
+ * Throws an error if the operation is restricted and user lacks permission.
82
+ */
83
+ export async function assertCanExecuteOperation(subgraph, documentId, operationType, ctx) {
84
+ // Skip if no permission service
85
+ if (!subgraph.documentPermissionService) {
86
+ return;
87
+ }
88
+ // Global admins bypass operation-level restrictions
89
+ if (ctx.isAdmin?.(ctx.user?.address ?? "")) {
90
+ return;
91
+ }
92
+ // Check if this operation has any restrictions set
93
+ const isRestricted = await subgraph.documentPermissionService.isOperationRestricted(documentId, operationType);
94
+ if (isRestricted) {
95
+ // Operation is restricted, check if user has permission
96
+ const canExecute = await subgraph.documentPermissionService.canExecuteOperation(documentId, operationType, ctx.user?.address);
97
+ if (!canExecute) {
98
+ throw new GraphQLError(`Forbidden: insufficient permissions to execute operation "${operationType}" on this document`);
99
+ }
100
+ }
101
+ }
@@ -1,3 +1,3 @@
1
- import type { ISubgraph } from "@powerhousedao/reactor-api";
2
- export declare const getResolvers: (subgraph: ISubgraph) => Record<string, unknown>;
1
+ import type { BaseSubgraph } from "@powerhousedao/reactor-api";
2
+ export declare const getResolvers: (subgraph: BaseSubgraph) => Record<string, unknown>;
3
3
  //# sourceMappingURL=resolvers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/vetra-read-model/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAI5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CA+CxE,CAAC"}
1
+ {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/vetra-read-model/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,4BAA4B,CAAC;AAKxE,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAsExB,CAAC"}
@@ -1,13 +1,13 @@
1
- import { VetraReadModelProcessor } from "../../processors/vetra-read-model/index.js";
1
+ import { VetraReadModelProcessorLegacy } from "../../processors/vetra-read-model/index.legacy.js";
2
+ import { canReadDocument, hasGlobalReadAccess } from "../permission-utils.js";
2
3
  export const getResolvers = (subgraph) => {
3
- const reactor = subgraph.reactor;
4
4
  const db = subgraph.relationalDb;
5
5
  return {
6
6
  Query: {
7
- vetraPackages: async (parent, args) => {
7
+ vetraPackages: async (_parent, args, ctx) => {
8
8
  const { search, documentId_in } = args;
9
9
  const sortOrder = args.sortOrder || "asc";
10
- let query = VetraReadModelProcessor.query("vetra-packages", db)
10
+ let query = VetraReadModelProcessorLegacy.query("vetra-packages", db)
11
11
  .selectFrom("vetra_package")
12
12
  .selectAll();
13
13
  if (search) {
@@ -17,7 +17,8 @@ export const getResolvers = (subgraph) => {
17
17
  query = query.where("document_id", "in", documentId_in);
18
18
  }
19
19
  query = query.orderBy("name", sortOrder);
20
- return (await query.execute()).map((pkg) => ({
20
+ const results = await query.execute();
21
+ const mappedResults = results.map((pkg) => ({
21
22
  ...pkg,
22
23
  documentId: pkg.document_id,
23
24
  name: pkg.name,
@@ -30,6 +31,18 @@ export const getResolvers = (subgraph) => {
30
31
  keywords: pkg.keywords,
31
32
  driveId: pkg.drive_id,
32
33
  }));
34
+ // If user doesn't have global read access, filter by document-level permissions
35
+ if (!hasGlobalReadAccess(ctx) && subgraph.documentPermissionService) {
36
+ const filteredResults = [];
37
+ for (const pkg of mappedResults) {
38
+ const canRead = await canReadDocument(subgraph, pkg.documentId, ctx);
39
+ if (canRead) {
40
+ filteredResults.push(pkg);
41
+ }
42
+ }
43
+ return filteredResults;
44
+ }
45
+ return mappedResults;
33
46
  },
34
47
  },
35
48
  };