@nlabs/metropolisjs 0.1.1 → 0.2.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 (326) hide show
  1. package/.github/copilot-instructions.md +167 -0
  2. package/AGENTS.md +13 -0
  3. package/CHANGELOG.md +112 -0
  4. package/GRAPHQL_VALIDATION_RESULTS.md +158 -0
  5. package/PERMISSION_GRAPHQL_INTEGRATION.md +293 -0
  6. package/PERMISSION_IMPLEMENTATION_SUMMARY.md +243 -0
  7. package/README.md +1343 -0
  8. package/SIGNIN_GRAPHQL_VALIDATION.md +125 -0
  9. package/VAULT_GRAPHQL_API_FINDINGS.md +312 -0
  10. package/docs/ACTIONS.md +63 -0
  11. package/docs/COLLECTIONS.md +1038 -0
  12. package/docs/CONNECTIONS.md +1367 -0
  13. package/docs/CRUD_INTEGRATION.md +702 -0
  14. package/examples/connections-usage.tsx +909 -0
  15. package/examples/content-actions-usage.ts +144 -0
  16. package/examples/crud-usage.tsx +847 -0
  17. package/examples/extensibility-usage.tsx +889 -0
  18. package/examples/factory-pattern-usage.ts +283 -0
  19. package/examples/i18n-import-examples.ts +86 -0
  20. package/examples/i18n-namespace-usage.ts +118 -0
  21. package/examples/i18next-singleton-demo.ts +187 -0
  22. package/examples/permission-system-usage.tsx +415 -0
  23. package/examples/project-details.tsx +397 -0
  24. package/examples/shared-i18n-usage.tsx +203 -0
  25. package/examples/signup-error-handling.ts +100 -0
  26. package/examples/translation-types.ts +86 -0
  27. package/examples/translation-usage.tsx +336 -0
  28. package/factoryPatternGuide.md +244 -0
  29. package/index.js +1 -0
  30. package/jest.config.mjs +28 -0
  31. package/lex.config.mjs +26 -0
  32. package/lib/__tests__/e2e/helpers/testGraphQLServer.d.ts +22 -0
  33. package/lib/__tests__/e2e/helpers/testGraphQLServer.d.ts.map +1 -0
  34. package/lib/__tests__/e2e/helpers/testGraphQLServer.js +197 -0
  35. package/lib/__tests__/e2e/helpers/testGraphQLServerSimple.d.ts +3 -0
  36. package/lib/__tests__/e2e/helpers/testGraphQLServerSimple.d.ts.map +1 -0
  37. package/lib/__tests__/e2e/helpers/testGraphQLServerSimple.js +52 -0
  38. package/lib/actions/appActions/appActions.d.ts +29 -0
  39. package/lib/actions/appActions/appActions.d.ts.map +1 -0
  40. package/lib/actions/appActions/appActions.js +159 -0
  41. package/lib/actions/connectionActions/connectionActions.d.ts +19 -0
  42. package/lib/actions/connectionActions/connectionActions.d.ts.map +1 -0
  43. package/lib/actions/connectionActions/connectionActions.js +131 -0
  44. package/lib/actions/contentActions/contentActions.d.ts +34 -0
  45. package/lib/actions/contentActions/contentActions.d.ts.map +1 -0
  46. package/lib/actions/contentActions/contentActions.js +314 -0
  47. package/lib/actions/conversationActions/conversationActions.d.ts +30 -0
  48. package/lib/actions/conversationActions/conversationActions.d.ts.map +1 -0
  49. package/lib/actions/conversationActions/conversationActions.js +225 -0
  50. package/lib/actions/eventActions/eventActions.d.ts +65 -0
  51. package/lib/actions/eventActions/eventActions.d.ts.map +1 -0
  52. package/lib/actions/eventActions/eventActions.js +436 -0
  53. package/lib/actions/groupActions/groupActions.d.ts +30 -0
  54. package/lib/actions/groupActions/groupActions.d.ts.map +1 -0
  55. package/lib/actions/groupActions/groupActions.js +229 -0
  56. package/lib/actions/imageActions/imageActions.d.ts +62 -0
  57. package/lib/actions/imageActions/imageActions.d.ts.map +1 -0
  58. package/lib/actions/imageActions/imageActions.js +593 -0
  59. package/lib/actions/index.d.ts +21 -0
  60. package/lib/actions/index.d.ts.map +1 -0
  61. package/lib/actions/index.js +22 -0
  62. package/lib/actions/locationActions/locationActions.d.ts +60 -0
  63. package/lib/actions/locationActions/locationActions.d.ts.map +1 -0
  64. package/lib/actions/locationActions/locationActions.js +388 -0
  65. package/lib/actions/messageActions/messageActions.d.ts +38 -0
  66. package/lib/actions/messageActions/messageActions.d.ts.map +1 -0
  67. package/lib/actions/messageActions/messageActions.js +235 -0
  68. package/lib/actions/permissionActions/permissionActions.d.ts +34 -0
  69. package/lib/actions/permissionActions/permissionActions.d.ts.map +1 -0
  70. package/lib/actions/permissionActions/permissionActions.js +330 -0
  71. package/lib/actions/personaActions/personaActions.d.ts +65 -0
  72. package/lib/actions/personaActions/personaActions.d.ts.map +1 -0
  73. package/lib/actions/personaActions/personaActions.js +572 -0
  74. package/lib/actions/postActions/postActions.d.ts +36 -0
  75. package/lib/actions/postActions/postActions.d.ts.map +1 -0
  76. package/lib/actions/postActions/postActions.js +447 -0
  77. package/lib/actions/reactionActions/reactionActions.d.ts +38 -0
  78. package/lib/actions/reactionActions/reactionActions.d.ts.map +1 -0
  79. package/lib/actions/reactionActions/reactionActions.js +413 -0
  80. package/lib/actions/sseActions/sseActions.d.ts +38 -0
  81. package/lib/actions/sseActions/sseActions.d.ts.map +1 -0
  82. package/lib/actions/sseActions/sseActions.js +148 -0
  83. package/lib/actions/subscriptionActions/subscriptionActions.d.ts +24 -0
  84. package/lib/actions/subscriptionActions/subscriptionActions.d.ts.map +1 -0
  85. package/lib/actions/subscriptionActions/subscriptionActions.js +329 -0
  86. package/lib/actions/tagActions/tagActions.d.ts +37 -0
  87. package/lib/actions/tagActions/tagActions.d.ts.map +1 -0
  88. package/lib/actions/tagActions/tagActions.js +347 -0
  89. package/lib/actions/translationActions/translationActions.d.ts +38 -0
  90. package/lib/actions/translationActions/translationActions.d.ts.map +1 -0
  91. package/lib/actions/translationActions/translationActions.js +189 -0
  92. package/lib/actions/userActions/userActions.d.ts +78 -0
  93. package/lib/actions/userActions/userActions.d.ts.map +1 -0
  94. package/lib/actions/userActions/userActions.js +844 -0
  95. package/lib/actions/videoActions/videoActions.d.ts +41 -0
  96. package/lib/actions/videoActions/videoActions.d.ts.map +1 -0
  97. package/lib/actions/videoActions/videoActions.js +532 -0
  98. package/lib/actions/websocketActions/websocketActions.d.ts +30 -0
  99. package/lib/actions/websocketActions/websocketActions.d.ts.map +1 -0
  100. package/lib/actions/websocketActions/websocketActions.js +377 -0
  101. package/lib/adapters/appAdapter/appAdapter.d.ts +8 -0
  102. package/lib/adapters/appAdapter/appAdapter.d.ts.map +1 -0
  103. package/lib/adapters/appAdapter/appAdapter.js +90 -0
  104. package/lib/adapters/arangoAdapter/arangoAdapter.d.ts +21 -0
  105. package/lib/adapters/arangoAdapter/arangoAdapter.d.ts.map +1 -0
  106. package/lib/adapters/arangoAdapter/arangoAdapter.js +75 -0
  107. package/lib/adapters/contentAdapter/contentAdapter.d.ts +32 -0
  108. package/lib/adapters/contentAdapter/contentAdapter.d.ts.map +1 -0
  109. package/lib/adapters/contentAdapter/contentAdapter.js +107 -0
  110. package/lib/adapters/conversationAdapter/conversationAdapter.d.ts +24 -0
  111. package/lib/adapters/conversationAdapter/conversationAdapter.d.ts.map +1 -0
  112. package/lib/adapters/conversationAdapter/conversationAdapter.js +75 -0
  113. package/lib/adapters/eventAdapter/eventAdapter.d.ts +46 -0
  114. package/lib/adapters/eventAdapter/eventAdapter.d.ts.map +1 -0
  115. package/lib/adapters/eventAdapter/eventAdapter.js +162 -0
  116. package/lib/adapters/fileAdapter/fileAdapter.d.ts +30 -0
  117. package/lib/adapters/fileAdapter/fileAdapter.d.ts.map +1 -0
  118. package/lib/adapters/fileAdapter/fileAdapter.js +118 -0
  119. package/lib/adapters/groupAdapter/groupAdapter.d.ts +28 -0
  120. package/lib/adapters/groupAdapter/groupAdapter.d.ts.map +1 -0
  121. package/lib/adapters/groupAdapter/groupAdapter.js +66 -0
  122. package/lib/adapters/imageAdapter/imageAdapter.d.ts +38 -0
  123. package/lib/adapters/imageAdapter/imageAdapter.d.ts.map +1 -0
  124. package/lib/adapters/imageAdapter/imageAdapter.js +134 -0
  125. package/lib/adapters/index.d.ts +34 -0
  126. package/lib/adapters/index.d.ts.map +1 -0
  127. package/lib/adapters/index.js +19 -0
  128. package/lib/adapters/locationAdapter/locationAdapter.d.ts +28 -0
  129. package/lib/adapters/locationAdapter/locationAdapter.d.ts.map +1 -0
  130. package/lib/adapters/locationAdapter/locationAdapter.js +95 -0
  131. package/lib/adapters/messageAdapter/messageAdapter.d.ts +31 -0
  132. package/lib/adapters/messageAdapter/messageAdapter.d.ts.map +1 -0
  133. package/lib/adapters/messageAdapter/messageAdapter.js +82 -0
  134. package/lib/adapters/permissionAdapter/permissionAdapter.d.ts +41 -0
  135. package/lib/adapters/permissionAdapter/permissionAdapter.d.ts.map +1 -0
  136. package/lib/adapters/permissionAdapter/permissionAdapter.js +120 -0
  137. package/lib/adapters/personaAdapter/personaAdapter.d.ts +44 -0
  138. package/lib/adapters/personaAdapter/personaAdapter.d.ts.map +1 -0
  139. package/lib/adapters/personaAdapter/personaAdapter.js +161 -0
  140. package/lib/adapters/postAdapter/postAdapter.d.ts +33 -0
  141. package/lib/adapters/postAdapter/postAdapter.d.ts.map +1 -0
  142. package/lib/adapters/postAdapter/postAdapter.js +115 -0
  143. package/lib/adapters/reactionAdapter/reactionAdapter.d.ts +23 -0
  144. package/lib/adapters/reactionAdapter/reactionAdapter.d.ts.map +1 -0
  145. package/lib/adapters/reactionAdapter/reactionAdapter.js +81 -0
  146. package/lib/adapters/reaktorAdapter/reaktorAdapter.d.ts +17 -0
  147. package/lib/adapters/reaktorAdapter/reaktorAdapter.d.ts.map +1 -0
  148. package/lib/adapters/reaktorAdapter/reaktorAdapter.js +58 -0
  149. package/lib/adapters/sessionAdapter/sessionAdapter.d.ts +24 -0
  150. package/lib/adapters/sessionAdapter/sessionAdapter.d.ts.map +1 -0
  151. package/lib/adapters/sessionAdapter/sessionAdapter.js +80 -0
  152. package/lib/adapters/tagAdapter/tagAdapter.d.ts +21 -0
  153. package/lib/adapters/tagAdapter/tagAdapter.d.ts.map +1 -0
  154. package/lib/adapters/tagAdapter/tagAdapter.js +98 -0
  155. package/lib/adapters/translationAdapter/translationAdapter.d.ts +21 -0
  156. package/lib/adapters/translationAdapter/translationAdapter.d.ts.map +1 -0
  157. package/lib/adapters/translationAdapter/translationAdapter.js +55 -0
  158. package/lib/adapters/userAdapter/userAdapter.d.ts +67 -0
  159. package/lib/adapters/userAdapter/userAdapter.d.ts.map +1 -0
  160. package/lib/adapters/userAdapter/userAdapter.js +302 -0
  161. package/lib/adapters/videoAdapter/videoAdapter.d.ts +8 -0
  162. package/lib/adapters/videoAdapter/videoAdapter.d.ts.map +1 -0
  163. package/lib/adapters/videoAdapter/videoAdapter.js +194 -0
  164. package/lib/config/index.d.ts +44 -0
  165. package/lib/config/index.d.ts.map +1 -0
  166. package/lib/config/index.js +97 -0
  167. package/lib/constants/Collections.d.ts +44 -0
  168. package/lib/constants/Collections.d.ts.map +1 -0
  169. package/lib/constants/Collections.js +40 -0
  170. package/lib/constants/MetropolisConstants.d.ts +15 -0
  171. package/lib/constants/MetropolisConstants.d.ts.map +1 -0
  172. package/lib/constants/MetropolisConstants.js +15 -0
  173. package/lib/graphql/message.d.ts +3 -0
  174. package/lib/graphql/message.d.ts.map +1 -0
  175. package/lib/graphql/message.js +20 -0
  176. package/lib/graphql/notification.d.ts +3 -0
  177. package/lib/graphql/notification.d.ts.map +1 -0
  178. package/lib/graphql/notification.js +20 -0
  179. package/lib/graphql/session.d.ts +2 -0
  180. package/lib/graphql/session.d.ts.map +1 -0
  181. package/lib/graphql/session.js +17 -0
  182. package/lib/index.d.ts +52 -0
  183. package/lib/index.d.ts.map +1 -0
  184. package/lib/index.js +248 -0
  185. package/lib/stores/appStore.d.ts +23 -0
  186. package/lib/stores/appStore.d.ts.map +1 -0
  187. package/lib/stores/appStore.js +23 -0
  188. package/lib/stores/contentStore.d.ts +36 -0
  189. package/lib/stores/contentStore.d.ts.map +1 -0
  190. package/lib/stores/contentStore.js +139 -0
  191. package/lib/stores/conversationStore.d.ts +38 -0
  192. package/lib/stores/conversationStore.d.ts.map +1 -0
  193. package/lib/stores/conversationStore.js +117 -0
  194. package/lib/stores/eventStore.d.ts +37 -0
  195. package/lib/stores/eventStore.d.ts.map +1 -0
  196. package/lib/stores/eventStore.js +47 -0
  197. package/lib/stores/groupStore.d.ts +37 -0
  198. package/lib/stores/groupStore.d.ts.map +1 -0
  199. package/lib/stores/groupStore.js +68 -0
  200. package/lib/stores/imageStore.d.ts +40 -0
  201. package/lib/stores/imageStore.d.ts.map +1 -0
  202. package/lib/stores/imageStore.js +53 -0
  203. package/lib/stores/index.d.ts +24 -0
  204. package/lib/stores/index.d.ts.map +1 -0
  205. package/lib/stores/index.js +24 -0
  206. package/lib/stores/locationStore.d.ts +34 -0
  207. package/lib/stores/locationStore.d.ts.map +1 -0
  208. package/lib/stores/locationStore.js +42 -0
  209. package/lib/stores/messageStore.d.ts +51 -0
  210. package/lib/stores/messageStore.d.ts.map +1 -0
  211. package/lib/stores/messageStore.js +137 -0
  212. package/lib/stores/notificationStore.d.ts +33 -0
  213. package/lib/stores/notificationStore.d.ts.map +1 -0
  214. package/lib/stores/notificationStore.js +55 -0
  215. package/lib/stores/permissionStore.d.ts +37 -0
  216. package/lib/stores/permissionStore.d.ts.map +1 -0
  217. package/lib/stores/permissionStore.js +117 -0
  218. package/lib/stores/personaStore.d.ts +26 -0
  219. package/lib/stores/personaStore.d.ts.map +1 -0
  220. package/lib/stores/personaStore.js +118 -0
  221. package/lib/stores/postStore.d.ts +34 -0
  222. package/lib/stores/postStore.d.ts.map +1 -0
  223. package/lib/stores/postStore.js +53 -0
  224. package/lib/stores/reactionStore.d.ts +25 -0
  225. package/lib/stores/reactionStore.d.ts.map +1 -0
  226. package/lib/stores/reactionStore.js +28 -0
  227. package/lib/stores/subscriptionStore.d.ts +41 -0
  228. package/lib/stores/subscriptionStore.d.ts.map +1 -0
  229. package/lib/stores/subscriptionStore.js +85 -0
  230. package/lib/stores/tagStore.d.ts +34 -0
  231. package/lib/stores/tagStore.d.ts.map +1 -0
  232. package/lib/stores/tagStore.js +87 -0
  233. package/lib/stores/translationStore.d.ts +42 -0
  234. package/lib/stores/translationStore.d.ts.map +1 -0
  235. package/lib/stores/translationStore.js +122 -0
  236. package/lib/stores/userStore.d.ts +74 -0
  237. package/lib/stores/userStore.d.ts.map +1 -0
  238. package/lib/stores/userStore.js +319 -0
  239. package/lib/stores/videoStore.d.ts +39 -0
  240. package/lib/stores/videoStore.d.ts.map +1 -0
  241. package/lib/stores/videoStore.js +132 -0
  242. package/lib/stores/websocketStore.d.ts +24 -0
  243. package/lib/stores/websocketStore.d.ts.map +1 -0
  244. package/lib/stores/websocketStore.js +51 -0
  245. package/lib/types/apps.types.d.ts +17 -0
  246. package/lib/types/apps.types.d.ts.map +1 -0
  247. package/lib/types/apps.types.js +6 -0
  248. package/lib/types/common.types.d.ts +31 -0
  249. package/lib/types/common.types.d.ts.map +1 -0
  250. package/lib/types/common.types.js +6 -0
  251. package/lib/types/conversations.types.d.ts +17 -0
  252. package/lib/types/conversations.types.d.ts.map +1 -0
  253. package/lib/types/conversations.types.js +6 -0
  254. package/lib/types/edges.types.d.ts +38 -0
  255. package/lib/types/edges.types.d.ts.map +1 -0
  256. package/lib/types/edges.types.js +6 -0
  257. package/lib/types/groups.types.d.ts +19 -0
  258. package/lib/types/groups.types.d.ts.map +1 -0
  259. package/lib/types/groups.types.js +6 -0
  260. package/lib/types/index.d.ts +12 -0
  261. package/lib/types/index.d.ts.map +1 -0
  262. package/lib/types/index.js +12 -0
  263. package/lib/types/subscriptions.types.d.ts +32 -0
  264. package/lib/types/subscriptions.types.d.ts.map +1 -0
  265. package/lib/types/subscriptions.types.js +6 -0
  266. package/lib/types/videos.types.d.ts +61 -0
  267. package/lib/types/videos.types.d.ts.map +1 -0
  268. package/lib/types/videos.types.js +6 -0
  269. package/lib/utils/MetropolisProvider.d.ts +58 -0
  270. package/lib/utils/MetropolisProvider.d.ts.map +1 -0
  271. package/lib/utils/MetropolisProvider.js +31 -0
  272. package/lib/utils/actionFactory.d.ts +24 -0
  273. package/lib/utils/actionFactory.d.ts.map +1 -0
  274. package/lib/utils/actionFactory.js +95 -0
  275. package/lib/utils/api.d.ts +44 -0
  276. package/lib/utils/api.d.ts.map +1 -0
  277. package/lib/utils/api.js +253 -0
  278. package/lib/utils/app.d.ts +7 -0
  279. package/lib/utils/app.d.ts.map +1 -0
  280. package/lib/utils/app.js +15 -0
  281. package/lib/utils/baseActionFactory.d.ts +16 -0
  282. package/lib/utils/baseActionFactory.d.ts.map +1 -0
  283. package/lib/utils/baseActionFactory.js +72 -0
  284. package/lib/utils/configUtils.d.ts +11 -0
  285. package/lib/utils/configUtils.d.ts.map +1 -0
  286. package/lib/utils/configUtils.js +20 -0
  287. package/lib/utils/dateUtils.d.ts +3 -0
  288. package/lib/utils/dateUtils.d.ts.map +1 -0
  289. package/lib/utils/dateUtils.js +11 -0
  290. package/lib/utils/file.d.ts +3 -0
  291. package/lib/utils/file.d.ts.map +1 -0
  292. package/lib/utils/file.js +156 -0
  293. package/lib/utils/i18n.d.ts +7 -0
  294. package/lib/utils/i18n.d.ts.map +1 -0
  295. package/lib/utils/i18n.js +96 -0
  296. package/lib/utils/index.d.ts +20 -0
  297. package/lib/utils/index.d.ts.map +1 -0
  298. package/lib/utils/index.js +26 -0
  299. package/lib/utils/location.d.ts +10 -0
  300. package/lib/utils/location.d.ts.map +1 -0
  301. package/lib/utils/location.js +82 -0
  302. package/lib/utils/permissionUtils.d.ts +22 -0
  303. package/lib/utils/permissionUtils.d.ts.map +1 -0
  304. package/lib/utils/permissionUtils.js +72 -0
  305. package/lib/utils/requestCache.d.ts +8 -0
  306. package/lib/utils/requestCache.d.ts.map +1 -0
  307. package/lib/utils/requestCache.js +53 -0
  308. package/lib/utils/session.d.ts +14 -0
  309. package/lib/utils/session.d.ts.map +1 -0
  310. package/lib/utils/session.js +107 -0
  311. package/lib/utils/useConversationTyping.d.ts +26 -0
  312. package/lib/utils/useConversationTyping.d.ts.map +1 -0
  313. package/lib/utils/useConversationTyping.js +117 -0
  314. package/lib/utils/useMetropolis.d.ts +67 -0
  315. package/lib/utils/useMetropolis.d.ts.map +1 -0
  316. package/lib/utils/useMetropolis.js +311 -0
  317. package/lib/utils/useTranslations.d.ts +16 -0
  318. package/lib/utils/useTranslations.d.ts.map +1 -0
  319. package/lib/utils/useTranslations.js +105 -0
  320. package/lib/utils/validatorFactory.d.ts +49 -0
  321. package/lib/utils/validatorFactory.d.ts.map +1 -0
  322. package/lib/utils/validatorFactory.js +95 -0
  323. package/package.json +75 -28
  324. package/temp_signin.ts +73 -0
  325. package/tsconfig.lint.json +25 -0
  326. package/tsconfig.test.json +14 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Copyright (c) 2025-Present, Nitrogen Labs, Inc.
3
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
+ */
5
+
6
+ // Define the types directly to avoid JSX compilation issues
7
+ type SimpleTranslations = Record<string, string>;
8
+
9
+ interface ComplexTranslation {
10
+ readonly value: string;
11
+ readonly locale: string;
12
+ readonly namespace?: string;
13
+ }
14
+
15
+ type ComplexTranslations = Record<string, ComplexTranslation>;
16
+
17
+ // Test simple translations type
18
+ const simpleTranslations: SimpleTranslations = {
19
+ 'welcome': 'Welcome to MetropolisJS!',
20
+ 'hello': 'Hello {{name}}!',
21
+ 'goodbye': 'Goodbye'
22
+ };
23
+
24
+ // Test complex translations type
25
+ const complexTranslations: ComplexTranslations = {
26
+ 'welcome:en': {
27
+ value: 'Welcome to MetropolisJS!',
28
+ locale: 'en',
29
+ namespace: 'common'
30
+ },
31
+ 'hello:en': {
32
+ value: 'Hello {{name}}!',
33
+ locale: 'en',
34
+ namespace: 'common'
35
+ },
36
+ 'save:en': {
37
+ value: 'Save',
38
+ locale: 'en',
39
+ namespace: 'buttons'
40
+ }
41
+ };
42
+
43
+ // Test individual complex translation type
44
+ const singleTranslation: ComplexTranslation = {
45
+ value: 'Test translation',
46
+ locale: 'en',
47
+ namespace: 'test'
48
+ };
49
+
50
+ // Function to demonstrate type safety
51
+ function processTranslations(translations: SimpleTranslations | ComplexTranslations): void {
52
+ if (Object.keys(translations).length === 0) {
53
+ console.log('No translations provided');
54
+ return;
55
+ }
56
+
57
+ const firstKey = Object.keys(translations)[0];
58
+ const firstValue = translations[firstKey];
59
+
60
+ if (typeof firstValue === 'string') {
61
+ // TypeScript knows this is SimpleTranslations
62
+ console.log('Processing simple translations');
63
+ Object.entries(translations as SimpleTranslations).forEach(([key, value]) => {
64
+ console.log(`${key}: ${value}`);
65
+ });
66
+ } else if (typeof firstValue === 'object' && firstValue !== null && 'value' in firstValue) {
67
+ // TypeScript knows this is ComplexTranslations
68
+ console.log('Processing complex translations');
69
+ Object.entries(translations as ComplexTranslations).forEach(([key, translation]) => {
70
+ console.log(`${key}: ${translation.value} (${translation.locale}${translation.namespace ? `, ${translation.namespace}` : ''})`);
71
+ });
72
+ }
73
+ }
74
+
75
+ // Test the function with both types
76
+ console.log('Testing simple translations:');
77
+ processTranslations(simpleTranslations);
78
+
79
+ console.log('\nTesting complex translations:');
80
+ processTranslations(complexTranslations);
81
+
82
+ console.log('\nType test completed successfully!');
83
+
84
+ // Export for use in other files
85
+ export { complexTranslations, processTranslations, simpleTranslations, singleTranslation };
86
+ export type { ComplexTranslation, ComplexTranslations, SimpleTranslations };
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Copyright (c) 2025-Present, Nitrogen Labs, Inc.
3
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
+ */
5
+
6
+ import React from 'react';
7
+ import { ComplexTranslations, Metropolis, SimpleTranslations } from '../src/index';
8
+ import { i18n } from '../src/utils/i18n';
9
+
10
+ // Example translations object with simple key-value pairs
11
+ const simpleTranslations: SimpleTranslations = {
12
+ 'welcome': 'Welcome to MetropolisJS!',
13
+ 'hello': 'Hello {{name}}!',
14
+ 'goodbye': 'Goodbye',
15
+ 'loading': 'Loading...',
16
+ 'error': 'An error occurred',
17
+ 'success': 'Operation completed successfully',
18
+ 'cancel': 'Cancel',
19
+ 'save': 'Save',
20
+ 'delete': 'Delete',
21
+ 'edit': 'Edit',
22
+ 'create': 'Create',
23
+ 'update': 'Update',
24
+ 'search': 'Search',
25
+ 'filter': 'Filter',
26
+ 'sort': 'Sort',
27
+ 'refresh': 'Refresh',
28
+ 'back': 'Back',
29
+ 'next': 'Next',
30
+ 'previous': 'Previous',
31
+ 'submit': 'Submit',
32
+ 'reset': 'Reset',
33
+ 'confirm': 'Confirm',
34
+ 'yes': 'Yes',
35
+ 'no': 'No',
36
+ 'ok': 'OK',
37
+ 'close': 'Close',
38
+ 'open': 'Open',
39
+ 'show': 'Show',
40
+ 'hide': 'Hide',
41
+ 'expand': 'Expand',
42
+ 'collapse': 'Collapse',
43
+ 'minimize': 'Minimize',
44
+ 'maximize': 'Maximize',
45
+ 'fullscreen': 'Fullscreen',
46
+ 'settings': 'Settings',
47
+ 'profile': 'Profile',
48
+ 'account': 'Account',
49
+ 'logout': 'Logout',
50
+ 'login': 'Login',
51
+ 'register': 'Register',
52
+ 'forgot_password': 'Forgot Password?',
53
+ 'remember_me': 'Remember Me',
54
+ 'email': 'Email',
55
+ 'password': 'Password',
56
+ 'username': 'Username',
57
+ 'first_name': 'First Name',
58
+ 'last_name': 'Last Name',
59
+ 'phone': 'Phone',
60
+ 'address': 'Address',
61
+ 'city': 'City',
62
+ 'state': 'State',
63
+ 'country': 'Country',
64
+ 'zip_code': 'ZIP Code',
65
+ 'birth_date': 'Birth Date',
66
+ 'gender': 'Gender',
67
+ 'male': 'Male',
68
+ 'female': 'Female',
69
+ 'other': 'Other',
70
+ 'prefer_not_to_say': 'Prefer not to say'
71
+ };
72
+
73
+ // Example complex translations with locale and namespace support
74
+ const complexTranslations: ComplexTranslations = {
75
+ 'welcome:en': {
76
+ value: 'Welcome to MetropolisJS!',
77
+ locale: 'en',
78
+ namespace: 'common'
79
+ },
80
+ 'hello:en': {
81
+ value: 'Hello {{name}}!',
82
+ locale: 'en',
83
+ namespace: 'common'
84
+ },
85
+ 'save:en': {
86
+ value: 'Save',
87
+ locale: 'en',
88
+ namespace: 'buttons'
89
+ },
90
+ 'cancel:en': {
91
+ value: 'Cancel',
92
+ locale: 'en',
93
+ namespace: 'buttons'
94
+ },
95
+ 'welcome:es': {
96
+ value: '¡Bienvenido a MetropolisJS!',
97
+ locale: 'es',
98
+ namespace: 'common'
99
+ },
100
+ 'hello:es': {
101
+ value: '¡Hola {{name}}!',
102
+ locale: 'es',
103
+ namespace: 'common'
104
+ },
105
+ 'save:es': {
106
+ value: 'Guardar',
107
+ locale: 'es',
108
+ namespace: 'buttons'
109
+ }
110
+ };
111
+
112
+ // Example React component using Metropolis with simple translations
113
+ const SimpleTranslationApp: React.FC = () => {
114
+ return (
115
+ <Metropolis
116
+ translations={simpleTranslations}
117
+ config={{
118
+ development: {
119
+ app: {
120
+ api: {
121
+ url: 'https://api.example.com'
122
+ },
123
+ urls: {
124
+ websocket: 'wss://ws.example.com'
125
+ }
126
+ }
127
+ }
128
+ }}
129
+ >
130
+ <div>
131
+ <h1>{i18n('welcome')}</h1>
132
+ <p>{i18n('hello', {name: 'John'})}</p>
133
+ <button>{i18n('save')}</button>
134
+ <button>{i18n('cancel')}</button>
135
+ <p>{i18n('loading')}</p>
136
+ </div>
137
+ </Metropolis>
138
+ );
139
+ };
140
+
141
+ // Example React component using Metropolis with complex translations
142
+ const ComplexTranslationApp: React.FC = () => {
143
+ return (
144
+ <Metropolis
145
+ translations={complexTranslations}
146
+ config={{
147
+ development: {
148
+ app: {
149
+ api: {
150
+ url: 'https://api.example.com'
151
+ },
152
+ urls: {
153
+ websocket: 'wss://ws.example.com'
154
+ }
155
+ }
156
+ }
157
+ }}
158
+ >
159
+ <div>
160
+ <h1>{i18n('welcome', {}, 'en', 'common')}</h1>
161
+ <p>{i18n('hello', {name: 'John'}, 'en', 'common')}</p>
162
+ <button>{i18n('save', {}, 'en', 'buttons')}</button>
163
+ <button>{i18n('cancel', {}, 'en', 'buttons')}</button>
164
+ <p>{i18n('loading')}</p>
165
+ </div>
166
+ </Metropolis>
167
+ );
168
+ };
169
+
170
+ // Example of using translations in a component
171
+ const UserProfile: React.FC = () => {
172
+ const [isLoading, setIsLoading] = React.useState(false);
173
+ const [user, setUser] = React.useState({name: 'John Doe'});
174
+
175
+ return (
176
+ <div>
177
+ <h2>{i18n('profile')}</h2>
178
+ {isLoading ? (
179
+ <p>{i18n('loading')}</p>
180
+ ) : (
181
+ <div>
182
+ <p>{i18n('hello', {name: user.name})}</p>
183
+ <button>{i18n('edit')}</button>
184
+ <button>{i18n('delete')}</button>
185
+ </div>
186
+ )}
187
+ </div>
188
+ );
189
+ };
190
+
191
+ // Example of a form component with translations
192
+ const LoginForm: React.FC = () => {
193
+ const [formData, setFormData] = React.useState({
194
+ email: '',
195
+ password: '',
196
+ rememberMe: false
197
+ });
198
+
199
+ const handleSubmit = (e: React.FormEvent) => {
200
+ e.preventDefault();
201
+ // Handle form submission
202
+ };
203
+
204
+ return (
205
+ <form onSubmit={handleSubmit}>
206
+ <h2>{i18n('login')}</h2>
207
+
208
+ <div>
209
+ <label htmlFor="email">{i18n('email')}:</label>
210
+ <input
211
+ id="email"
212
+ type="email"
213
+ value={formData.email}
214
+ onChange={(e) => setFormData({...formData, email: e.target.value})}
215
+ required
216
+ />
217
+ </div>
218
+
219
+ <div>
220
+ <label htmlFor="password">{i18n('password')}:</label>
221
+ <input
222
+ id="password"
223
+ type="password"
224
+ value={formData.password}
225
+ onChange={(e) => setFormData({...formData, password: e.target.value})}
226
+ required
227
+ />
228
+ </div>
229
+
230
+ <div>
231
+ <label>
232
+ <input
233
+ type="checkbox"
234
+ checked={formData.rememberMe}
235
+ onChange={(e) => setFormData({...formData, rememberMe: e.target.checked})}
236
+ />
237
+ {i18n('remember_me')}
238
+ </label>
239
+ </div>
240
+
241
+ <div>
242
+ <button type="submit">{i18n('login')}</button>
243
+ <button type="button">{i18n('cancel')}</button>
244
+ </div>
245
+
246
+ <div>
247
+ <a href="#forgot-password">{i18n('forgot_password')}</a>
248
+ </div>
249
+ </form>
250
+ );
251
+ };
252
+
253
+ // Example of a multilingual app that can switch between simple and complex translations
254
+ const MultilingualApp: React.FC = () => {
255
+ const [useComplexTranslations, setUseComplexTranslations] = React.useState(false);
256
+ const [currentLocale, setCurrentLocale] = React.useState<'en' | 'es'>('en');
257
+
258
+ const selectedTranslations = useComplexTranslations ? complexTranslations : simpleTranslations;
259
+
260
+ return (
261
+ <Metropolis
262
+ translations={selectedTranslations}
263
+ config={{
264
+ development: {
265
+ app: {
266
+ api: {
267
+ url: 'https://api.example.com'
268
+ },
269
+ urls: {
270
+ websocket: 'wss://ws.example.com'
271
+ }
272
+ }
273
+ }
274
+ }}
275
+ >
276
+ <div>
277
+ <div style={{marginBottom: '20px'}}>
278
+ <label>
279
+ <input
280
+ type="checkbox"
281
+ checked={useComplexTranslations}
282
+ onChange={(e) => setUseComplexTranslations(e.target.checked)}
283
+ />
284
+ Use Complex Translations
285
+ </label>
286
+
287
+ {useComplexTranslations && (
288
+ <div style={{marginLeft: '20px'}}>
289
+ <label>
290
+ Locale:
291
+ <select value={currentLocale} onChange={(e) => setCurrentLocale(e.target.value as 'en' | 'es')}>
292
+ <option value="en">English</option>
293
+ <option value="es">Español</option>
294
+ </select>
295
+ </label>
296
+ </div>
297
+ )}
298
+ </div>
299
+
300
+ <h1>
301
+ {useComplexTranslations
302
+ ? i18n('welcome', {}, currentLocale, 'common')
303
+ : i18n('welcome')
304
+ }
305
+ </h1>
306
+
307
+ <p>
308
+ {useComplexTranslations
309
+ ? i18n('hello', {name: 'World'}, currentLocale, 'common')
310
+ : i18n('hello', {name: 'World'})
311
+ }
312
+ </p>
313
+
314
+ <div>
315
+ <button>
316
+ {useComplexTranslations
317
+ ? i18n('save', {}, currentLocale, 'buttons')
318
+ : i18n('save')
319
+ }
320
+ </button>
321
+ <button>
322
+ {useComplexTranslations
323
+ ? i18n('cancel', {}, currentLocale, 'buttons')
324
+ : i18n('cancel')
325
+ }
326
+ </button>
327
+ </div>
328
+
329
+ <UserProfile />
330
+ <LoginForm />
331
+ </div>
332
+ </Metropolis>
333
+ );
334
+ };
335
+
336
+ export default MultilingualApp;
@@ -0,0 +1,244 @@
1
+ # MetropolisJS Factory Pattern Guide
2
+
3
+ ## Overview
4
+
5
+ MetropolisJS has been refactored to use a **factory function pattern** for actions instead of class-based approaches. This provides better functional programming practices, improved testability, and enhanced flexibility through dependency injection.
6
+
7
+ ## Key Benefits
8
+
9
+ 1. **Functional Programming**: Pure functions instead of classes with side effects
10
+ 2. **Better Testability**: Easier to mock and test individual functions
11
+ 3. **Composability**: Actions can be easily combined and extended
12
+ 4. **Dependency Injection**: Custom adapters can be injected and merged with defaults
13
+ 5. **Backward Compatibility**: Legacy class wrappers maintain existing API compatibility
14
+
15
+ ## Basic Usage
16
+
17
+ ### Before (Class-based)
18
+
19
+ ```typescript
20
+ import {userActions} from '../actions/userActions';
21
+
22
+ const userActions = new userActions(flux);
23
+ const user = await userActions.add(userData);
24
+ ```
25
+
26
+ ### After (Factory Pattern)
27
+
28
+ ```typescript
29
+ import {createUserActions} from '../actions/userActions';
30
+
31
+ const userActions = createUserActions(flux);
32
+ const user = await userActions.add(userData);
33
+ ```
34
+
35
+ ## Advanced Usage with Custom Adapters
36
+
37
+ ### Custom Validation Adapter
38
+
39
+ ```typescript
40
+ // Custom adapter that extends default behavior
41
+ const customUserAdapter = (input: unknown, options?: UserAdapterOptions) => {
42
+ // input is already validated by default adapter
43
+ const user = input as any;
44
+
45
+ // Add business-specific validation
46
+ if (user.email && !user.email.includes('@company.com')) {
47
+ throw new Error('Only company emails allowed');
48
+ }
49
+
50
+ // Add computed fields
51
+ return {
52
+ ...user,
53
+ fullName: `${user.firstName || ''} ${user.lastName || ''}`.trim(),
54
+ isAdmin: user.userAccess >= 3
55
+ };
56
+ };
57
+
58
+ const userActions = createUserActions(flux, {
59
+ userAdapter: customUserAdapter
60
+ });
61
+ ```
62
+
63
+ ### Configuration-based Adapters
64
+
65
+ ```typescript
66
+ const userActions = createUserActions(flux, {
67
+ userAdapterOptions: {
68
+ strict: true,
69
+ environment: 'production',
70
+ customValidation: (input) => {
71
+ // Additional validation logic
72
+ return input;
73
+ }
74
+ }
75
+ });
76
+ ```
77
+
78
+ ### Runtime Adapter Updates
79
+
80
+ ```typescript
81
+ const userActions = createUserActions(flux);
82
+
83
+ // Update adapter at runtime
84
+ userActions.updateUserAdapter(customUserAdapter);
85
+
86
+ // Update options at runtime
87
+ userActions.updateUserAdapterOptions({
88
+ strict: true,
89
+ environment: 'production'
90
+ });
91
+ ```
92
+
93
+ ## Available Actions
94
+
95
+ All action files now export factory functions:
96
+
97
+ - `createUserActions(flux, options?)` - User management
98
+ - `createPostActions(flux, options?)` - Post management
99
+ - `createEventActions(flux, options?)` - Event management
100
+ - `createMessageActions(flux, options?)` - Messaging
101
+ - `createImageActions(flux, options?)` - Image handling
102
+ - `createLocationActions(flux, options?)` - Location services
103
+ - `createReactionActions(flux, options?)` - Reactions
104
+ - `createTagActions(flux, options?)` - Tag management
105
+ - `createWebsocketActions(flux)` - WebSocket connections
106
+
107
+ ## Adapter Options Interface
108
+
109
+ All adapters support the same options interface:
110
+
111
+ ```typescript
112
+ interface AdapterOptions {
113
+ strict?: boolean; // Enable strict validation
114
+ allowPartial?: boolean; // Allow partial data
115
+ environment?: 'development' | 'production' | 'test';
116
+ customValidation?: (input: unknown) => unknown;
117
+ }
118
+ ```
119
+
120
+ ## Migration Guide
121
+
122
+ ### Step 1: Update Imports
123
+
124
+ ```typescript
125
+ // Old
126
+ import {userActions} from '../actions/userActions';
127
+
128
+ // New
129
+ import {createUserActions} from '../actions/userActions';
130
+ ```
131
+
132
+ ### Step 2: Update Instantiation
133
+
134
+ ```typescript
135
+ // Old
136
+ const userActions = new userActions(flux, customAdapter);
137
+
138
+ // New
139
+ const userActions = createUserActions(flux, {
140
+ userAdapter: customAdapter
141
+ });
142
+ ```
143
+
144
+ ### Step 3: Update useMetropolis Hook
145
+
146
+ The `useMetropolis` hook has been updated to use the factory pattern:
147
+
148
+ ```typescript
149
+ // Old
150
+ return useMemo(() => ({
151
+ userActions: new userActions(flux, UserAdapter),
152
+ postActions: new PostActions(flux, PostAdapter),
153
+ // ...
154
+ }), [flux, UserAdapter, PostAdapter]);
155
+
156
+ // New
157
+ return useMemo(() => ({
158
+ userActions: createUserActions(flux, {
159
+ userAdapter: UserAdapter
160
+ }),
161
+ postActions: createPostActions(flux, {
162
+ postAdapter: PostAdapter
163
+ }),
164
+ // ...
165
+ }), [flux, UserAdapter, PostAdapter]);
166
+ ```
167
+
168
+ ## Backward Compatibility
169
+
170
+ Legacy class wrappers are provided for backward compatibility:
171
+
172
+ ```typescript
173
+ // Still works
174
+ import {userActionsClass} from '../actions/userActions';
175
+ const userActions = new userActionsClass(flux, options);
176
+ ```
177
+
178
+ ## Testing Examples
179
+
180
+ ### Unit Testing Actions
181
+
182
+ ```typescript
183
+ import {createUserActions} from '../actions/userActions';
184
+
185
+ describe('userActions', () => {
186
+ let flux: FluxFramework;
187
+ let userActions: userActions;
188
+
189
+ beforeEach(() => {
190
+ flux = createMockFlux();
191
+ userActions = createUserActions(flux);
192
+ });
193
+
194
+ it('should add user with validation', async () => {
195
+ const userData = {username: 'test', email: 'test@example.com'};
196
+ const result = await userActions.add(userData);
197
+ expect(result).toBeDefined();
198
+ });
199
+ });
200
+ ```
201
+
202
+ ### Testing with Custom Adapters
203
+
204
+ ```typescript
205
+ const mockAdapter = jest.fn((input) => ({
206
+ ...input,
207
+ validated: true
208
+ }));
209
+
210
+ const userActions = createUserActions(flux, {
211
+ userAdapter: mockAdapter
212
+ });
213
+
214
+ expect(mockAdapter).toHaveBeenCalled();
215
+ ```
216
+
217
+ ## Best Practices
218
+
219
+ 1. **Use Factory Functions**: Prefer `createXxxActions()` over class constructors
220
+ 2. **Leverage Adapter Injection**: Use custom adapters for business logic
221
+ 3. **Runtime Updates**: Use update methods for dynamic behavior changes
222
+ 4. **Type Safety**: Always use TypeScript interfaces for better type checking
223
+ 5. **Error Handling**: Custom adapters should throw meaningful errors
224
+
225
+ ## Performance Considerations
226
+
227
+ - Factory functions are lightweight and create minimal overhead
228
+ - Adapter validation is only performed when needed
229
+ - Options are merged efficiently without deep cloning
230
+ - Legacy wrappers have minimal performance impact
231
+
232
+ ## Future Enhancements
233
+
234
+ The factory pattern enables future enhancements:
235
+
236
+ - **Middleware Support**: Chain multiple adapters
237
+ - **Plugin System**: Load adapters dynamically
238
+ - **Caching**: Cache validated results
239
+ - **Async Adapters**: Support async validation logic
240
+ - **Schema Evolution**: Handle multiple adapter versions
241
+
242
+ ## Conclusion
243
+
244
+ The factory pattern provides a more functional, testable, and flexible approach to action management in MetropolisJS. While maintaining backward compatibility, it opens up new possibilities for customization and extension.
package/index.js CHANGED
@@ -2,4 +2,5 @@
2
2
  * Copyright (c) 2018-Present, Nitrogen Labs, Inc.
3
3
  * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
4
  */
5
+ export { i18n, initI18n, updateI18nResources } from './src/utils/i18n';
5
6
  module.exports = require('./lib');
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Jest configuration for MetropolisJS tests.
3
+ * Pulls the default Lex Jest config (which includes TS/Babel transforms)
4
+ * and overlays the project-specific settings from lex.config.mjs.
5
+ */
6
+ import lexConfig from './lex.config.mjs';
7
+ import baseConfig from './node_modules/@nlabs/lex/jest.config.mjs';
8
+
9
+ const projectJest = lexConfig?.jest || {};
10
+
11
+ export default {
12
+ ...baseConfig,
13
+ ...projectJest,
14
+ testPathIgnorePatterns: [
15
+ 'lib/__tests__/e2e/helpers/testGraphQLServer.d.ts',
16
+ 'lib/__tests__/e2e/helpers/testGraphQLServerSimple.d.ts'
17
+ ],
18
+ transformIgnorePatterns: [
19
+ '/node_modules/(?!(?:@nlabs/utils|@nlabs/rip-hunter)/)'
20
+ ],
21
+ moduleNameMapper: {
22
+ ...baseConfig.moduleNameMapper,
23
+ ...projectJest.moduleNameMapper
24
+ },
25
+ transform: {
26
+ ...baseConfig.transform
27
+ }
28
+ };