@undefineds.co/xpod 0.1.6 → 0.2.0-preview.2

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 (307) hide show
  1. package/README.md +141 -2
  2. package/config/cli.json +9 -71
  3. package/config/cloud.json +34 -7
  4. package/config/local.json +6 -2
  5. package/config/resolver.json +11 -49
  6. package/config/runtime-open.json +22 -0
  7. package/config/xpod.base.json +32 -0
  8. package/config/xpod.cluster.json +2 -44
  9. package/config/xpod.json +5 -2
  10. package/dist/api/ApiServer.js +1 -1
  11. package/dist/api/ApiServer.js.map +1 -1
  12. package/dist/api/auth/AuthContext.d.ts +12 -1
  13. package/dist/api/auth/AuthContext.js +18 -1
  14. package/dist/api/auth/AuthContext.js.map +1 -1
  15. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
  16. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  17. package/dist/api/auth/ServiceTokenAuthenticator.d.ts +18 -0
  18. package/dist/api/auth/ServiceTokenAuthenticator.js +50 -0
  19. package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -0
  20. package/dist/api/auth/index.d.ts +1 -0
  21. package/dist/api/auth/index.js +1 -0
  22. package/dist/api/auth/index.js.map +1 -1
  23. package/dist/api/chatkit/ai-provider.d.ts +0 -10
  24. package/dist/api/chatkit/ai-provider.js +11 -120
  25. package/dist/api/chatkit/ai-provider.js.map +1 -1
  26. package/dist/api/chatkit/default-agent.js +11 -8
  27. package/dist/api/chatkit/default-agent.js.map +1 -1
  28. package/dist/api/chatkit/pod-store.js +19 -3
  29. package/dist/api/chatkit/pod-store.js.map +1 -1
  30. package/dist/api/chatkit/schema.d.ts +9 -3
  31. package/dist/api/chatkit/schema.js +14 -6
  32. package/dist/api/chatkit/schema.js.map +1 -1
  33. package/dist/api/container/business-token.d.ts +9 -0
  34. package/dist/api/container/business-token.js +32 -0
  35. package/dist/api/container/business-token.js.map +1 -0
  36. package/dist/api/container/cloud.js +36 -12
  37. package/dist/api/container/cloud.js.map +1 -1
  38. package/dist/api/container/common.js +12 -5
  39. package/dist/api/container/common.js.map +1 -1
  40. package/dist/api/container/index.js +94 -14
  41. package/dist/api/container/index.js.map +1 -1
  42. package/dist/api/container/local.js +2 -1
  43. package/dist/api/container/local.js.map +1 -1
  44. package/dist/api/container/routes.js +81 -15
  45. package/dist/api/container/routes.js.map +1 -1
  46. package/dist/api/container/types.d.ts +8 -6
  47. package/dist/api/container/types.js.map +1 -1
  48. package/dist/api/handlers/AdminHandler.js +9 -9
  49. package/dist/api/handlers/AdminHandler.js.map +1 -1
  50. package/dist/api/handlers/ApiKeyHandler.js +0 -6
  51. package/dist/api/handlers/ApiKeyHandler.js.map +1 -1
  52. package/dist/api/handlers/EdgeNodeSignalHandler.d.ts +17 -0
  53. package/dist/api/handlers/EdgeNodeSignalHandler.js +171 -0
  54. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -0
  55. package/dist/api/handlers/PodManagementHandler.d.ts +5 -4
  56. package/dist/api/handlers/PodManagementHandler.js +11 -10
  57. package/dist/api/handlers/PodManagementHandler.js.map +1 -1
  58. package/dist/api/handlers/ProvisionHandler.d.ts +42 -0
  59. package/dist/api/handlers/ProvisionHandler.js +161 -0
  60. package/dist/api/handlers/ProvisionHandler.js.map +1 -0
  61. package/dist/api/handlers/QuotaHandler.d.ts +7 -7
  62. package/dist/api/handlers/QuotaHandler.js +143 -73
  63. package/dist/api/handlers/QuotaHandler.js.map +1 -1
  64. package/dist/api/handlers/SubdomainClientHandler.js +2 -2
  65. package/dist/api/handlers/SubdomainClientHandler.js.map +1 -1
  66. package/dist/api/handlers/SubdomainHandler.js +13 -8
  67. package/dist/api/handlers/SubdomainHandler.js.map +1 -1
  68. package/dist/api/handlers/UsageHandler.d.ts +14 -0
  69. package/dist/api/handlers/UsageHandler.js +123 -0
  70. package/dist/api/handlers/UsageHandler.js.map +1 -0
  71. package/dist/api/handlers/index.d.ts +3 -1
  72. package/dist/api/handlers/index.js +3 -1
  73. package/dist/api/handlers/index.js.map +1 -1
  74. package/dist/api/main.js +18 -0
  75. package/dist/api/main.js.map +1 -1
  76. package/dist/api/middleware/OpenAuthMiddleware.d.ts +12 -0
  77. package/dist/api/middleware/OpenAuthMiddleware.js +27 -0
  78. package/dist/api/middleware/OpenAuthMiddleware.js.map +1 -0
  79. package/dist/api/runtime.d.ts +15 -0
  80. package/dist/api/runtime.js +104 -0
  81. package/dist/api/runtime.js.map +1 -0
  82. package/dist/api/service/VercelChatService.d.ts +16 -7
  83. package/dist/api/service/VercelChatService.js +98 -178
  84. package/dist/api/service/VercelChatService.js.map +1 -1
  85. package/dist/api/store/DrizzleClientCredentialsStore.d.ts +6 -11
  86. package/dist/api/store/DrizzleClientCredentialsStore.js +9 -39
  87. package/dist/api/store/DrizzleClientCredentialsStore.js.map +1 -1
  88. package/dist/authorization/AuthModeSelector.d.ts +10 -0
  89. package/dist/authorization/AuthModeSelector.js +27 -0
  90. package/dist/authorization/AuthModeSelector.js.map +1 -0
  91. package/dist/authorization/AuthModeSelector.jsonld +81 -0
  92. package/dist/cli/commands/account.d.ts +6 -0
  93. package/dist/cli/commands/account.js +119 -0
  94. package/dist/cli/commands/account.js.map +1 -0
  95. package/dist/cli/commands/auth.js +20 -29
  96. package/dist/cli/commands/auth.js.map +1 -1
  97. package/dist/cli/commands/backup.d.ts +15 -0
  98. package/dist/cli/commands/backup.js +286 -0
  99. package/dist/cli/commands/backup.js.map +1 -0
  100. package/dist/cli/commands/config.d.ts +34 -3
  101. package/dist/cli/commands/config.js +195 -258
  102. package/dist/cli/commands/config.js.map +1 -1
  103. package/dist/cli/commands/doctor.d.ts +6 -0
  104. package/dist/cli/commands/doctor.js +94 -0
  105. package/dist/cli/commands/doctor.js.map +1 -0
  106. package/dist/cli/commands/pod.d.ts +6 -0
  107. package/dist/cli/commands/pod.js +124 -0
  108. package/dist/cli/commands/pod.js.map +1 -0
  109. package/dist/cli/commands/start.js +28 -5
  110. package/dist/cli/commands/start.js.map +1 -1
  111. package/dist/cli/index.js +9 -0
  112. package/dist/cli/index.js.map +1 -1
  113. package/dist/cli/lib/credentials-store.d.ts +17 -0
  114. package/dist/cli/lib/credentials-store.js +73 -0
  115. package/dist/cli/lib/credentials-store.js.map +1 -0
  116. package/dist/cli/lib/css-account.d.ts +17 -0
  117. package/dist/cli/lib/css-account.js +56 -0
  118. package/dist/cli/lib/css-account.js.map +1 -1
  119. package/dist/cli/lib/pod-thread-store.d.ts +57 -0
  120. package/dist/cli/lib/pod-thread-store.js +310 -0
  121. package/dist/cli/lib/pod-thread-store.js.map +1 -0
  122. package/dist/cli/lib/solid-auth.d.ts +20 -0
  123. package/dist/cli/lib/solid-auth.js +70 -0
  124. package/dist/cli/lib/solid-auth.js.map +1 -0
  125. package/dist/components/components.jsonld +5 -8
  126. package/dist/components/context.jsonld +114 -244
  127. package/dist/edge/EdgeNodeAgent.js +2 -2
  128. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  129. package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -7
  130. package/dist/edge/EdgeNodeDnsCoordinator.js +31 -41
  131. package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
  132. package/dist/edge/EdgeNodeDnsCoordinator.jsonld +1 -27
  133. package/dist/edge/EdgeNodeModeDetector.d.ts +1 -1
  134. package/dist/edge/EdgeNodeModeDetector.js +9 -11
  135. package/dist/edge/EdgeNodeModeDetector.js.map +1 -1
  136. package/dist/http/ClusterIngressRouter.js +3 -3
  137. package/dist/http/ClusterIngressRouter.js.map +1 -1
  138. package/dist/http/ClusterWebSocketConfigurator.js +2 -2
  139. package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
  140. package/dist/http/PodRoutingHttpHandler.js +2 -2
  141. package/dist/http/PodRoutingHttpHandler.js.map +1 -1
  142. package/dist/http/cluster/PodMigrationHttpHandler.d.ts +1 -1
  143. package/dist/http/cluster/PodMigrationHttpHandler.js +1 -1
  144. package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
  145. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +37 -4
  146. package/dist/identity/drizzle/EdgeNodeRepository.js +120 -128
  147. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  148. package/dist/identity/drizzle/ServiceTokenRepository.d.ts +52 -0
  149. package/dist/identity/drizzle/ServiceTokenRepository.js +143 -0
  150. package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -0
  151. package/dist/identity/drizzle/db.d.ts +9 -0
  152. package/dist/identity/drizzle/db.js +208 -1
  153. package/dist/identity/drizzle/db.js.map +1 -1
  154. package/dist/identity/drizzle/schema.pg.d.ts +5 -0
  155. package/dist/identity/drizzle/schema.pg.js +49 -20
  156. package/dist/identity/drizzle/schema.pg.js.map +1 -1
  157. package/dist/identity/drizzle/schema.sqlite.d.ts +332 -57
  158. package/dist/identity/drizzle/schema.sqlite.js +48 -18
  159. package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
  160. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +6 -4
  161. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
  162. package/dist/index.d.ts +6 -9
  163. package/dist/index.js +12 -14
  164. package/dist/index.js.map +1 -1
  165. package/dist/main.js +25 -8
  166. package/dist/main.js.map +1 -1
  167. package/dist/provision/ProvisionCodeCodec.d.ts +39 -0
  168. package/dist/provision/ProvisionCodeCodec.js +65 -0
  169. package/dist/provision/ProvisionCodeCodec.js.map +1 -0
  170. package/dist/provision/ProvisionCodeCodec.jsonld +47 -0
  171. package/dist/provision/ProvisionPodCreator.d.ts +20 -0
  172. package/dist/provision/ProvisionPodCreator.js +84 -0
  173. package/dist/provision/ProvisionPodCreator.js.map +1 -0
  174. package/dist/provision/ProvisionPodCreator.jsonld +118 -0
  175. package/dist/quota/DrizzleQuotaService.d.ts +17 -3
  176. package/dist/quota/DrizzleQuotaService.js +108 -8
  177. package/dist/quota/DrizzleQuotaService.js.map +1 -1
  178. package/dist/quota/DrizzleQuotaService.jsonld +33 -22
  179. package/dist/quota/NoopQuotaService.d.ts +7 -1
  180. package/dist/quota/NoopQuotaService.js +12 -0
  181. package/dist/quota/NoopQuotaService.js.map +1 -1
  182. package/dist/quota/NoopQuotaService.jsonld +24 -0
  183. package/dist/quota/QuotaService.d.ts +17 -0
  184. package/dist/quota/QuotaService.js +5 -0
  185. package/dist/quota/QuotaService.js.map +1 -1
  186. package/dist/quota/QuotaService.jsonld +50 -0
  187. package/dist/runtime/Proxy.d.ts +22 -4
  188. package/dist/runtime/Proxy.js +154 -35
  189. package/dist/runtime/Proxy.js.map +1 -1
  190. package/dist/runtime/XpodRuntime.d.ts +49 -0
  191. package/dist/runtime/XpodRuntime.js +374 -0
  192. package/dist/runtime/XpodRuntime.js.map +1 -0
  193. package/dist/runtime/env-utils.d.ts +2 -0
  194. package/dist/runtime/env-utils.js +55 -0
  195. package/dist/runtime/env-utils.js.map +1 -0
  196. package/dist/runtime/index.d.ts +4 -0
  197. package/dist/runtime/index.js +8 -1
  198. package/dist/runtime/index.js.map +1 -1
  199. package/dist/runtime/socket-fetch.d.ts +1 -0
  200. package/dist/runtime/socket-fetch.js +72 -0
  201. package/dist/runtime/socket-fetch.js.map +1 -0
  202. package/dist/runtime/socket-http.d.ts +1 -0
  203. package/dist/runtime/socket-http.js +142 -0
  204. package/dist/runtime/socket-http.js.map +1 -0
  205. package/dist/runtime/socket-utils.d.ts +2 -0
  206. package/dist/runtime/socket-utils.js +34 -0
  207. package/dist/runtime/socket-utils.js.map +1 -0
  208. package/dist/service/{EdgeNodeHeartbeatService.d.ts → EdgeNodeSignalClient.d.ts} +3 -3
  209. package/dist/service/{EdgeNodeHeartbeatService.js → EdgeNodeSignalClient.js} +4 -4
  210. package/dist/service/EdgeNodeSignalClient.js.map +1 -0
  211. package/dist/service/PodMigrationService.d.ts +1 -2
  212. package/dist/service/PodMigrationService.js +1 -2
  213. package/dist/service/PodMigrationService.js.map +1 -1
  214. package/dist/storage/SparqlUpdateResourceStore.js +1 -1
  215. package/dist/storage/SparqlUpdateResourceStore.js.map +1 -1
  216. package/dist/storage/accessors/MinioDataAccessor.d.ts +6 -0
  217. package/dist/storage/accessors/MinioDataAccessor.js +10 -0
  218. package/dist/storage/accessors/MinioDataAccessor.js.map +1 -1
  219. package/dist/storage/accessors/MinioDataAccessor.jsonld +4 -0
  220. package/dist/storage/accessors/MixDataAccessor.d.ts +2 -1
  221. package/dist/storage/accessors/MixDataAccessor.js +12 -1
  222. package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
  223. package/dist/storage/accessors/MixDataAccessor.jsonld +19 -0
  224. package/dist/storage/locking/UrlAwareRedisLocker.d.ts +18 -0
  225. package/dist/storage/locking/UrlAwareRedisLocker.js +60 -0
  226. package/dist/storage/locking/UrlAwareRedisLocker.js.map +1 -0
  227. package/dist/storage/locking/UrlAwareRedisLocker.jsonld +123 -0
  228. package/dist/storage/quota/UsageRepository.d.ts +41 -8
  229. package/dist/storage/quota/UsageRepository.js +252 -50
  230. package/dist/storage/quota/UsageRepository.js.map +1 -1
  231. package/dist/storage/sparql/ComunicaQuintEngine.d.ts +9 -0
  232. package/dist/storage/sparql/ComunicaQuintEngine.js +50 -9
  233. package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
  234. package/dist/storage/sparql/QueryOptimizer.js +13 -1
  235. package/dist/storage/sparql/QueryOptimizer.js.map +1 -1
  236. package/dist/storage/sparql/QuintQuerySource.d.ts +14 -0
  237. package/dist/storage/sparql/QuintQuerySource.js +152 -1
  238. package/dist/storage/sparql/QuintQuerySource.js.map +1 -1
  239. package/dist/storage/sparql/SubgraphQueryEngine.d.ts +1 -0
  240. package/dist/storage/sparql/SubgraphQueryEngine.js +6 -2
  241. package/dist/storage/sparql/SubgraphQueryEngine.js.map +1 -1
  242. package/dist/storage/sparql/SubgraphQueryEngine.jsonld +4 -0
  243. package/dist/subdomain/SubdomainClient.d.ts +3 -3
  244. package/dist/subdomain/SubdomainClient.js +1 -1
  245. package/dist/subdomain/SubdomainClient.js.map +1 -1
  246. package/dist/subdomain/SubdomainService.d.ts +15 -16
  247. package/dist/subdomain/SubdomainService.js +80 -54
  248. package/dist/subdomain/SubdomainService.js.map +1 -1
  249. package/dist/subdomain/SubdomainService.jsonld +22 -26
  250. package/dist/supervisor/Supervisor.d.ts +7 -2
  251. package/dist/supervisor/Supervisor.js +33 -1
  252. package/dist/supervisor/Supervisor.js.map +1 -1
  253. package/dist/test-utils/index.d.ts +4 -0
  254. package/dist/test-utils/index.js +8 -0
  255. package/dist/test-utils/index.js.map +1 -0
  256. package/dist/test-utils/no-auth-xpod.d.ts +11 -0
  257. package/dist/test-utils/no-auth-xpod.js +25 -0
  258. package/dist/test-utils/no-auth-xpod.js.map +1 -0
  259. package/dist/test-utils/seed-pod.d.ts +5 -0
  260. package/dist/test-utils/seed-pod.js +61 -0
  261. package/dist/test-utils/seed-pod.js.map +1 -0
  262. package/package.json +23 -5
  263. package/templates/identity/account/create-pod.html.ejs +110 -0
  264. package/templates/main.html.ejs +10 -0
  265. package/dist/api/handlers/DevHandler.d.ts +0 -18
  266. package/dist/api/handlers/DevHandler.js +0 -276
  267. package/dist/api/handlers/DevHandler.js.map +0 -1
  268. package/dist/api/handlers/SignalHandler.d.ts +0 -13
  269. package/dist/api/handlers/SignalHandler.js +0 -122
  270. package/dist/api/handlers/SignalHandler.js.map +0 -1
  271. package/dist/gateway/Proxy.d.ts +0 -24
  272. package/dist/gateway/Proxy.js +0 -209
  273. package/dist/gateway/Proxy.js.map +0 -1
  274. package/dist/gateway/Supervisor.d.ts +0 -2
  275. package/dist/gateway/Supervisor.js +0 -7
  276. package/dist/gateway/Supervisor.js.map +0 -1
  277. package/dist/gateway/port-finder.d.ts +0 -4
  278. package/dist/gateway/port-finder.js +0 -15
  279. package/dist/gateway/port-finder.js.map +0 -1
  280. package/dist/gateway/types.d.ts +0 -1
  281. package/dist/gateway/types.js +0 -3
  282. package/dist/gateway/types.js.map +0 -1
  283. package/dist/http/SignalInterceptHttpHandler.d.ts +0 -24
  284. package/dist/http/SignalInterceptHttpHandler.js +0 -47
  285. package/dist/http/SignalInterceptHttpHandler.js.map +0 -1
  286. package/dist/http/SignalInterceptHttpHandler.jsonld +0 -103
  287. package/dist/http/admin/EdgeNodeSignalHttpHandler.d.ts +0 -71
  288. package/dist/http/admin/EdgeNodeSignalHttpHandler.js +0 -674
  289. package/dist/http/admin/EdgeNodeSignalHttpHandler.js.map +0 -1
  290. package/dist/http/admin/EdgeNodeSignalHttpHandler.jsonld +0 -406
  291. package/dist/http/cluster/PodMigrationHttpHandler.jsonld +0 -169
  292. package/dist/quota/DefaultQuotaService.d.ts +0 -16
  293. package/dist/quota/DefaultQuotaService.js +0 -37
  294. package/dist/quota/DefaultQuotaService.js.map +0 -1
  295. package/dist/quota/DefaultQuotaService.jsonld +0 -85
  296. package/dist/service/EdgeNodeHeartbeatService.js.map +0 -1
  297. package/dist/service/PodMigrationService.jsonld +0 -76
  298. package/dist/storage/MigratableDataAccessor.d.ts +0 -63
  299. package/dist/storage/MigratableDataAccessor.js +0 -11
  300. package/dist/storage/MigratableDataAccessor.js.map +0 -1
  301. package/dist/storage/MigratableDataAccessor.jsonld +0 -60
  302. package/dist/storage/accessors/TieredMinioDataAccessor.d.ts +0 -150
  303. package/dist/storage/accessors/TieredMinioDataAccessor.js +0 -582
  304. package/dist/storage/accessors/TieredMinioDataAccessor.js.map +0 -1
  305. package/dist/storage/accessors/TieredMinioDataAccessor.jsonld +0 -333
  306. package/static/app/assets/index.css +0 -1
  307. package/static/app/assets/main.js +0 -11
@@ -1,582 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TieredMinioDataAccessor = void 0;
4
- const node_fs_1 = require("node:fs");
5
- const global_logger_factory_1 = require("global-logger-factory");
6
- const node_path_1 = require("node:path");
7
- const minio_1 = require("minio");
8
- const community_server_1 = require("@solid/community-server");
9
- /**
10
- * TieredMinioDataAccessor extends MinioDataAccessor with a local cache layer.
11
- *
12
- * Read: Check local cache first, if miss then fetch from COS and cache locally.
13
- * Write: Write to COS first (ensure durability), then cache locally.
14
- *
15
- * Uses LRU eviction when cache exceeds maxSize.
16
- *
17
- * Supports cross-region migration via MigratableDataAccessor interface.
18
- */
19
- class TieredMinioDataAccessor {
20
- constructor(config) {
21
- this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
22
- // LRU tracking: Map<cacheFilePath, lastAccessTime>
23
- this.cacheEntries = new Map();
24
- this.currentCacheSize = 0;
25
- // Active sync subscriptions for migration
26
- this.activeSyncs = new Map();
27
- this.resourceMapper = config.resourceMapper;
28
- this.client = new minio_1.Client({
29
- accessKey: config.accessKey,
30
- secretKey: config.secretKey,
31
- endPoint: config.endpoint,
32
- useSSL: true,
33
- });
34
- this.bucketName = config.bucketName;
35
- this.cachePath = config.cachePath;
36
- this.cacheMaxSize = config.cacheMaxSize;
37
- this.region = config.region;
38
- this.regionBuckets = config.regionBuckets ?? {};
39
- // Ensure cache directory exists
40
- if (!(0, node_fs_1.existsSync)(this.cachePath)) {
41
- (0, node_fs_1.mkdirSync)(this.cachePath, { recursive: true });
42
- }
43
- // Initialize cache tracking from existing files
44
- this.initializeCacheTracking();
45
- this.logger.info(`TieredMinioDataAccessor initialized with endpoint: ${config.endpoint}, cache: ${config.cachePath}, maxSize: ${this.formatBytes(config.cacheMaxSize)}`);
46
- }
47
- /**
48
- * Scan existing cache directory and populate cache entries map.
49
- */
50
- initializeCacheTracking() {
51
- try {
52
- this.scanCacheDir(this.cachePath);
53
- this.logger.info(`Cache initialized: ${this.cacheEntries.size} files, ${this.formatBytes(this.currentCacheSize)}`);
54
- }
55
- catch (error) {
56
- this.logger.warn(`Failed to scan cache directory: ${error}`);
57
- }
58
- }
59
- scanCacheDir(dir) {
60
- if (!(0, node_fs_1.existsSync)(dir))
61
- return;
62
- const entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true });
63
- for (const entry of entries) {
64
- const fullPath = (0, node_path_1.join)(dir, entry.name);
65
- if (entry.isDirectory()) {
66
- this.scanCacheDir(fullPath);
67
- }
68
- else if (entry.isFile()) {
69
- try {
70
- const stats = (0, node_fs_1.statSync)(fullPath);
71
- this.cacheEntries.set(fullPath, {
72
- path: fullPath,
73
- size: stats.size,
74
- lastAccess: stats.atimeMs,
75
- });
76
- this.currentCacheSize += stats.size;
77
- }
78
- catch {
79
- // File may have been deleted
80
- }
81
- }
82
- }
83
- }
84
- /**
85
- * Convert URL path to local cache file path.
86
- */
87
- getCacheFilePath(identifier) {
88
- const url = new URL(identifier.path);
89
- // Remove leading slash and encode special chars
90
- const relativePath = url.pathname.slice(1).replace(/[<>:"|?*]/g, '_');
91
- return (0, node_path_1.join)(this.cachePath, relativePath);
92
- }
93
- /**
94
- * Check if file exists in local cache.
95
- */
96
- isCached(cacheFilePath) {
97
- return (0, node_fs_1.existsSync)(cacheFilePath);
98
- }
99
- /**
100
- * Update LRU access time for a cache entry.
101
- */
102
- touchCache(cacheFilePath) {
103
- const entry = this.cacheEntries.get(cacheFilePath);
104
- if (entry) {
105
- entry.lastAccess = Date.now();
106
- }
107
- }
108
- /**
109
- * Add file to cache tracking.
110
- */
111
- addToCacheTracking(cacheFilePath, size) {
112
- this.cacheEntries.set(cacheFilePath, {
113
- path: cacheFilePath,
114
- size,
115
- lastAccess: Date.now(),
116
- });
117
- this.currentCacheSize += size;
118
- // Trigger eviction if needed
119
- this.evictIfNeeded();
120
- }
121
- /**
122
- * Remove file from cache tracking.
123
- */
124
- removeFromCacheTracking(cacheFilePath) {
125
- const entry = this.cacheEntries.get(cacheFilePath);
126
- if (entry) {
127
- this.currentCacheSize -= entry.size;
128
- this.cacheEntries.delete(cacheFilePath);
129
- }
130
- }
131
- /**
132
- * Evict least recently used files until cache is under maxSize.
133
- */
134
- evictIfNeeded() {
135
- if (this.currentCacheSize <= this.cacheMaxSize) {
136
- return;
137
- }
138
- // Sort by lastAccess (oldest first)
139
- const sortedEntries = [...this.cacheEntries.entries()]
140
- .sort((a, b) => a[1].lastAccess - b[1].lastAccess);
141
- for (const [path, entry] of sortedEntries) {
142
- if (this.currentCacheSize <= this.cacheMaxSize * 0.8) {
143
- // Evict until 80% of max to avoid frequent evictions
144
- break;
145
- }
146
- try {
147
- (0, node_fs_1.unlinkSync)(path);
148
- this.currentCacheSize -= entry.size;
149
- this.cacheEntries.delete(path);
150
- this.logger.debug(`Evicted from cache: ${path}`);
151
- }
152
- catch (error) {
153
- this.logger.warn(`Failed to evict cache file ${path}: ${error}`);
154
- }
155
- }
156
- }
157
- formatBytes(bytes) {
158
- if (bytes >= 1024 * 1024 * 1024) {
159
- return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
160
- }
161
- if (bytes >= 1024 * 1024) {
162
- return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
163
- }
164
- if (bytes >= 1024) {
165
- return `${(bytes / 1024).toFixed(2)} KB`;
166
- }
167
- return `${bytes} B`;
168
- }
169
- // ============== DataAccessor Interface ==============
170
- async canHandle(representation) {
171
- if (!representation.binary) {
172
- throw new community_server_1.UnsupportedMediaTypeHttpError('Only binary data is supported.');
173
- }
174
- }
175
- /**
176
- * Get data with cache-first strategy and cross-region fallback.
177
- *
178
- * Read order:
179
- * 1. Local cache (fastest)
180
- * 2. Local bucket (current region)
181
- * 3. Fallback buckets (other regions) - enables instant migration
182
- *
183
- * When reading from fallback bucket, optionally copy to local bucket (lazy migration).
184
- */
185
- async getData(identifier) {
186
- const cacheFilePath = this.getCacheFilePath(identifier);
187
- const url = new URL(identifier.path);
188
- // 1. Check local cache first
189
- if (this.isCached(cacheFilePath)) {
190
- this.logger.debug(`Cache hit: ${identifier.path}`);
191
- this.touchCache(cacheFilePath);
192
- const stream = (0, node_fs_1.createReadStream)(cacheFilePath);
193
- return (0, community_server_1.guardStream)(stream);
194
- }
195
- // 2. Try local bucket first
196
- this.logger.debug(`Cache miss: ${identifier.path}`);
197
- let data = null;
198
- let sourceLocation = 'local';
199
- try {
200
- data = await this.fetchFromBucket(this.bucketName, url.pathname);
201
- }
202
- catch (error) {
203
- // Local bucket failed, try fallback buckets
204
- if (this.supportsMigration()) {
205
- const fallbackResult = await this.fetchFromFallbackBuckets(url.pathname);
206
- if (fallbackResult) {
207
- data = fallbackResult.data;
208
- sourceLocation = fallbackResult.bucket;
209
- this.logger.debug(`Fallback read from ${sourceLocation}: ${identifier.path}`);
210
- }
211
- }
212
- }
213
- if (!data) {
214
- throw new community_server_1.NotFoundHttpError(`Resource not found: ${identifier.path}`);
215
- }
216
- // 3. Write to local cache
217
- await this.writeToCache(cacheFilePath, data, identifier.path);
218
- // 4. Lazy copy to local bucket if read from fallback
219
- if (sourceLocation !== 'local' && sourceLocation !== this.bucketName) {
220
- this.lazyCopyToLocalBucket(url.pathname, data).catch(err => {
221
- this.logger.warn(`Lazy copy failed for ${identifier.path}: ${err.message}`);
222
- });
223
- }
224
- // Return data as stream
225
- const { Readable } = require('node:stream');
226
- const readable = Readable.from(data);
227
- return (0, community_server_1.guardStream)(readable);
228
- }
229
- /**
230
- * Fetch data from a specific bucket.
231
- */
232
- async fetchFromBucket(bucket, path) {
233
- const stream = await this.client.getObject(bucket, path);
234
- const chunks = [];
235
- for await (const chunk of stream) {
236
- chunks.push(chunk);
237
- }
238
- return Buffer.concat(chunks);
239
- }
240
- /**
241
- * Try to fetch data from fallback buckets (other regions).
242
- * Returns the data and source bucket name, or null if not found.
243
- */
244
- async fetchFromFallbackBuckets(path) {
245
- // Try each region bucket except the current one
246
- for (const [region, bucket] of Object.entries(this.regionBuckets)) {
247
- if (bucket === this.bucketName) {
248
- continue; // Skip local bucket
249
- }
250
- try {
251
- this.logger.debug(`Trying fallback bucket: ${bucket} (region: ${region})`);
252
- const data = await this.fetchFromBucket(bucket, path);
253
- return { data, bucket };
254
- }
255
- catch {
256
- // Not found in this bucket, try next
257
- continue;
258
- }
259
- }
260
- return null;
261
- }
262
- /**
263
- * Write data to local cache.
264
- */
265
- async writeToCache(cacheFilePath, data, path) {
266
- try {
267
- const cacheDir = (0, node_path_1.dirname)(cacheFilePath);
268
- if (!(0, node_fs_1.existsSync)(cacheDir)) {
269
- (0, node_fs_1.mkdirSync)(cacheDir, { recursive: true });
270
- }
271
- const writeStream = (0, node_fs_1.createWriteStream)(cacheFilePath);
272
- await new Promise((resolve, reject) => {
273
- writeStream.write(data, (err) => {
274
- if (err)
275
- reject(err);
276
- else {
277
- writeStream.end();
278
- resolve();
279
- }
280
- });
281
- });
282
- this.addToCacheTracking(cacheFilePath, data.length);
283
- }
284
- catch (error) {
285
- this.logger.warn(`Failed to cache ${path}: ${error}`);
286
- }
287
- }
288
- /**
289
- * Lazily copy data to local bucket for future reads.
290
- * This runs in the background and doesn't block the read.
291
- */
292
- async lazyCopyToLocalBucket(path, data) {
293
- const { Readable } = require('node:stream');
294
- const stream = Readable.from(data);
295
- await this.client.putObject(this.bucketName, path, stream, data.length);
296
- this.logger.debug(`Lazy copied to local bucket: ${path}`);
297
- }
298
- async getMetadata(identifier) {
299
- const url = new URL(identifier.path);
300
- const link = await this.resourceMapper.mapUrlToFilePath(identifier, false);
301
- const isDirectory = identifier.path.endsWith('/');
302
- const objectName = isDirectory ? `${url.pathname}/.container` : url.pathname;
303
- let stats;
304
- try {
305
- stats = await this.client.statObject(this.bucketName, objectName);
306
- }
307
- catch (error) {
308
- throw new community_server_1.NotFoundHttpError();
309
- }
310
- if (!(0, community_server_1.isContainerIdentifier)(identifier) && !isDirectory) {
311
- return this.getFileMetadata(link, stats);
312
- }
313
- if ((0, community_server_1.isContainerIdentifier)(identifier) && isDirectory) {
314
- return this.getDirectoryMetadata(link, stats);
315
- }
316
- throw new community_server_1.NotFoundHttpError();
317
- }
318
- async *getChildren(identifier) {
319
- const url = new URL(identifier.path);
320
- const objects = this.client.listObjectsV2(this.bucketName, url.pathname);
321
- for await (const object of objects) {
322
- const metadata = await this.getMetadata(object);
323
- yield metadata;
324
- }
325
- }
326
- /**
327
- * Write document: COS first (durability), then cache.
328
- */
329
- async writeDocument(identifier, data, metadata) {
330
- const url = new URL(identifier.path);
331
- const link = await this.resourceMapper.mapUrlToFilePath(identifier, false);
332
- const itemMetadata = this.encodeMetadata(link, metadata);
333
- // Collect data for both COS and cache
334
- const chunks = [];
335
- for await (const chunk of data) {
336
- chunks.push(chunk);
337
- }
338
- const buffer = Buffer.concat(chunks);
339
- // Write to COS first (ensure durability)
340
- try {
341
- const { Readable } = require('node:stream');
342
- const cosStream = Readable.from(buffer);
343
- await this.client.putObject(this.bucketName, url.pathname, cosStream, buffer.length, itemMetadata || undefined);
344
- }
345
- catch (error) {
346
- this.logger.error(`Error writing to COS: ${identifier.path} ${error}`);
347
- throw error;
348
- }
349
- // Write to local cache
350
- const cacheFilePath = this.getCacheFilePath(identifier);
351
- try {
352
- const cacheDir = (0, node_path_1.dirname)(cacheFilePath);
353
- if (!(0, node_fs_1.existsSync)(cacheDir)) {
354
- (0, node_fs_1.mkdirSync)(cacheDir, { recursive: true });
355
- }
356
- // Remove old cache entry if exists
357
- if (this.isCached(cacheFilePath)) {
358
- this.removeFromCacheTracking(cacheFilePath);
359
- }
360
- const writeStream = (0, node_fs_1.createWriteStream)(cacheFilePath);
361
- await new Promise((resolve, reject) => {
362
- writeStream.write(buffer, (err) => {
363
- if (err)
364
- reject(err);
365
- else {
366
- writeStream.end();
367
- resolve();
368
- }
369
- });
370
- });
371
- this.addToCacheTracking(cacheFilePath, buffer.length);
372
- }
373
- catch (error) {
374
- this.logger.warn(`Failed to write cache ${identifier.path}: ${error}`);
375
- // Cache failure is non-fatal
376
- }
377
- // Replicate to sync targets (for active migrations)
378
- const syncTargets = this.getActiveSyncTargets(url.pathname);
379
- for (const targetBucket of syncTargets) {
380
- try {
381
- const { Readable } = require('node:stream');
382
- const syncStream = Readable.from(buffer);
383
- await this.client.putObject(targetBucket, url.pathname, syncStream, buffer.length, itemMetadata || undefined);
384
- this.logger.debug(`Synced to ${targetBucket}: ${url.pathname}`);
385
- }
386
- catch (error) {
387
- this.logger.warn(`Failed to sync ${url.pathname} to ${targetBucket}: ${error}`);
388
- // Sync failure is non-fatal, migration will catch up
389
- }
390
- }
391
- }
392
- async writeContainer(identifier, metadata) {
393
- const url = new URL(identifier.path);
394
- const link = await this.resourceMapper.mapUrlToFilePath(identifier, false);
395
- await this.client.putObject(this.bucketName, `${url.pathname}/.container`, Buffer.from(''), 0, this.encodeMetadata(link, metadata) || undefined);
396
- }
397
- async writeMetadata(identifier, metadata) {
398
- throw new Error('TieredMinioDataAccessor does not support writing metadata for a resource.');
399
- }
400
- async deleteResource(identifier) {
401
- const url = new URL(identifier.path);
402
- // Delete from COS
403
- await this.client.removeObject(this.bucketName, url.pathname);
404
- // Delete from cache
405
- const cacheFilePath = this.getCacheFilePath(identifier);
406
- if (this.isCached(cacheFilePath)) {
407
- try {
408
- (0, node_fs_1.unlinkSync)(cacheFilePath);
409
- this.removeFromCacheTracking(cacheFilePath);
410
- }
411
- catch (error) {
412
- this.logger.warn(`Failed to delete cache ${identifier.path}: ${error}`);
413
- }
414
- }
415
- // Sync delete to migration targets
416
- const syncTargets = this.getActiveSyncTargets(url.pathname);
417
- for (const targetBucket of syncTargets) {
418
- try {
419
- await this.client.removeObject(targetBucket, url.pathname);
420
- this.logger.debug(`Synced delete to ${targetBucket}: ${url.pathname}`);
421
- }
422
- catch (error) {
423
- this.logger.warn(`Failed to sync delete ${url.pathname} to ${targetBucket}: ${error}`);
424
- }
425
- }
426
- }
427
- // ============== Metadata Helpers ==============
428
- async getFileMetadata(link, stats) {
429
- const metadata = await this.getBaseMetadata(link, stats, false);
430
- if (typeof metadata.contentType === 'undefined') {
431
- metadata.set(community_server_1.CONTENT_TYPE_TERM, link.contentType);
432
- }
433
- return metadata;
434
- }
435
- async getDirectoryMetadata(link, stats) {
436
- return this.getBaseMetadata(link, stats, true);
437
- }
438
- async getBaseMetadata(link, stats, isContainer) {
439
- const metadata = this.decodeMetadata(link, stats.metaData);
440
- (0, community_server_1.addResourceMetadata)(metadata, isContainer);
441
- this.addPosixMetadata(metadata, stats, isContainer);
442
- return metadata;
443
- }
444
- addPosixMetadata(metadata, stats, isDirectory) {
445
- (0, community_server_1.updateModifiedDate)(metadata, stats.lastModified);
446
- metadata.add(community_server_1.POSIX.terms.mtime, (0, community_server_1.toLiteral)(Math.floor(stats.lastModified.getTime() / 1000), community_server_1.XSD.terms.integer), community_server_1.SOLID_META.terms.ResponseMetadata);
447
- if (!isDirectory) {
448
- metadata.add(community_server_1.POSIX.terms.size, (0, community_server_1.toLiteral)(stats.size, community_server_1.XSD.terms.integer), community_server_1.SOLID_META.terms.ResponseMetadata);
449
- }
450
- }
451
- encodeMetadata(link, metadata) {
452
- metadata.remove(community_server_1.RDF.terms.type, community_server_1.LDP.terms.Resource);
453
- metadata.remove(community_server_1.RDF.terms.type, community_server_1.LDP.terms.Container);
454
- metadata.remove(community_server_1.RDF.terms.type, community_server_1.LDP.terms.BasicContainer);
455
- metadata.removeAll(community_server_1.DC.terms.modified);
456
- if ((0, community_server_1.isContainerPath)(link.filePath) || typeof metadata.contentType !== 'undefined') {
457
- metadata.removeAll(community_server_1.CONTENT_TYPE_TERM);
458
- }
459
- const contentTypeObject = metadata.contentTypeObject;
460
- if (contentTypeObject === undefined || Object.keys(contentTypeObject.parameters).length === 0) {
461
- return null;
462
- }
463
- return contentTypeObject.parameters;
464
- }
465
- decodeMetadata(link, metadata) {
466
- return new community_server_1.RepresentationMetadata(link.identifier, metadata);
467
- }
468
- // ============== Cache Stats (for monitoring) ==============
469
- getCacheStats() {
470
- return {
471
- entries: this.cacheEntries.size,
472
- size: this.currentCacheSize,
473
- maxSize: this.cacheMaxSize,
474
- };
475
- }
476
- // ============== MigratableDataAccessor Implementation ==============
477
- /**
478
- * Check if migration is supported.
479
- * Migration requires region configuration and region-to-bucket mapping.
480
- */
481
- supportsMigration() {
482
- return this.region !== undefined && Object.keys(this.regionBuckets).length > 0;
483
- }
484
- /**
485
- * Migrate all objects under the given prefix to a target region's bucket.
486
- * Uses Minio server-side copy for efficiency (data doesn't pass through this node).
487
- */
488
- async migrateToRegion(prefix, targetRegion, onProgress) {
489
- if (!this.supportsMigration()) {
490
- throw new Error('Migration not supported: region configuration missing');
491
- }
492
- const targetBucket = this.regionBuckets[targetRegion];
493
- if (!targetBucket) {
494
- throw new Error(`Unknown target region: ${targetRegion}. Available regions: ${Object.keys(this.regionBuckets).join(', ')}`);
495
- }
496
- if (targetBucket === this.bucketName) {
497
- this.logger.info(`Source and target bucket are the same (${this.bucketName}), skipping migration`);
498
- onProgress?.({ copied: 0, total: 0, bytesTransferred: 0 });
499
- return;
500
- }
501
- this.logger.info(`Starting migration: prefix=${prefix}, source=${this.bucketName}, target=${targetBucket}`);
502
- // Normalize prefix (remove leading slash for Minio)
503
- const objectPrefix = prefix.startsWith('/') ? prefix.slice(1) : prefix;
504
- // 1. List all objects to migrate
505
- const objects = [];
506
- const stream = this.client.listObjectsV2(this.bucketName, objectPrefix, true);
507
- for await (const obj of stream) {
508
- if (obj.name) {
509
- objects.push({ name: obj.name, size: obj.size ?? 0 });
510
- }
511
- }
512
- this.logger.info(`Found ${objects.length} objects to migrate`);
513
- if (objects.length === 0) {
514
- onProgress?.({ copied: 0, total: 0, bytesTransferred: 0 });
515
- return;
516
- }
517
- // 2. Copy each object using server-side copy
518
- let copied = 0;
519
- let bytesTransferred = 0;
520
- for (const obj of objects) {
521
- try {
522
- // Minio copyObject uses server-side copy when source and target are on same cluster
523
- const copySource = `/${this.bucketName}/${obj.name}`;
524
- await this.client.copyObject(targetBucket, obj.name, copySource, new minio_1.CopyConditions());
525
- copied++;
526
- bytesTransferred += obj.size;
527
- onProgress?.({
528
- copied,
529
- total: objects.length,
530
- bytesTransferred,
531
- });
532
- this.logger.debug(`Copied: ${obj.name} (${copied}/${objects.length})`);
533
- }
534
- catch (error) {
535
- this.logger.error(`Failed to copy object ${obj.name}: ${error.message}`);
536
- throw error;
537
- }
538
- }
539
- this.logger.info(`Migration completed: ${copied} objects, ${this.formatBytes(bytesTransferred)}`);
540
- }
541
- /**
542
- * Set up real-time sync during migration.
543
- *
544
- * Note: Full implementation would use Minio Bucket Notifications to replicate
545
- * new writes to the target bucket. For now, we track active syncs and replicate
546
- * writes in writeDocument().
547
- */
548
- async setupRealtimeSync(prefix, targetRegion) {
549
- if (!this.supportsMigration()) {
550
- throw new Error('Migration not supported: region configuration missing');
551
- }
552
- const targetBucket = this.regionBuckets[targetRegion];
553
- if (!targetBucket) {
554
- throw new Error(`Unknown target region: ${targetRegion}`);
555
- }
556
- const syncKey = `${prefix}:${targetRegion}`;
557
- this.activeSyncs.set(syncKey, { prefix, targetBucket });
558
- this.logger.info(`Real-time sync enabled: prefix=${prefix}, target=${targetBucket}`);
559
- }
560
- /**
561
- * Stop real-time sync after migration completes.
562
- */
563
- async stopRealtimeSync(prefix, targetRegion) {
564
- const syncKey = `${prefix}:${targetRegion}`;
565
- this.activeSyncs.delete(syncKey);
566
- this.logger.info(`Real-time sync disabled: prefix=${prefix}, targetRegion=${targetRegion}`);
567
- }
568
- /**
569
- * Check if a write should be replicated to sync targets.
570
- */
571
- getActiveSyncTargets(path) {
572
- const targets = [];
573
- for (const [, sync] of this.activeSyncs) {
574
- if (path.startsWith(sync.prefix)) {
575
- targets.push(sync.targetBucket);
576
- }
577
- }
578
- return targets;
579
- }
580
- }
581
- exports.TieredMinioDataAccessor = TieredMinioDataAccessor;
582
- //# sourceMappingURL=TieredMinioDataAccessor.js.map