@plures/praxis 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 (263) hide show
  1. package/FRAMEWORK.md +420 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1310 -0
  4. package/dist/adapters/cli.d.ts +43 -0
  5. package/dist/adapters/cli.d.ts.map +1 -0
  6. package/dist/adapters/cli.js +126 -0
  7. package/dist/adapters/cli.js.map +1 -0
  8. package/dist/cli/commands/auth.d.ts +26 -0
  9. package/dist/cli/commands/auth.d.ts.map +1 -0
  10. package/dist/cli/commands/auth.js +233 -0
  11. package/dist/cli/commands/auth.js.map +1 -0
  12. package/dist/cli/commands/cloud.d.ts +27 -0
  13. package/dist/cli/commands/cloud.d.ts.map +1 -0
  14. package/dist/cli/commands/cloud.js +232 -0
  15. package/dist/cli/commands/cloud.js.map +1 -0
  16. package/dist/cli/commands/generate.d.ts +25 -0
  17. package/dist/cli/commands/generate.d.ts.map +1 -0
  18. package/dist/cli/commands/generate.js +168 -0
  19. package/dist/cli/commands/generate.js.map +1 -0
  20. package/dist/cli/index.d.ts +8 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +179 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/cloud/auth.d.ts +51 -0
  25. package/dist/cloud/auth.d.ts.map +1 -0
  26. package/dist/cloud/auth.js +194 -0
  27. package/dist/cloud/auth.js.map +1 -0
  28. package/dist/cloud/billing.d.ts +184 -0
  29. package/dist/cloud/billing.d.ts.map +1 -0
  30. package/dist/cloud/billing.js +179 -0
  31. package/dist/cloud/billing.js.map +1 -0
  32. package/dist/cloud/client.d.ts +39 -0
  33. package/dist/cloud/client.d.ts.map +1 -0
  34. package/dist/cloud/client.js +176 -0
  35. package/dist/cloud/client.js.map +1 -0
  36. package/dist/cloud/index.d.ts +44 -0
  37. package/dist/cloud/index.d.ts.map +1 -0
  38. package/dist/cloud/index.js +44 -0
  39. package/dist/cloud/index.js.map +1 -0
  40. package/dist/cloud/marketplace.d.ts +166 -0
  41. package/dist/cloud/marketplace.d.ts.map +1 -0
  42. package/dist/cloud/marketplace.js +159 -0
  43. package/dist/cloud/marketplace.js.map +1 -0
  44. package/dist/cloud/provisioning.d.ts +110 -0
  45. package/dist/cloud/provisioning.d.ts.map +1 -0
  46. package/dist/cloud/provisioning.js +148 -0
  47. package/dist/cloud/provisioning.js.map +1 -0
  48. package/dist/cloud/relay/endpoints.d.ts +62 -0
  49. package/dist/cloud/relay/endpoints.d.ts.map +1 -0
  50. package/dist/cloud/relay/endpoints.js +217 -0
  51. package/dist/cloud/relay/endpoints.js.map +1 -0
  52. package/dist/cloud/relay/health/index.d.ts +5 -0
  53. package/dist/cloud/relay/health/index.d.ts.map +1 -0
  54. package/dist/cloud/relay/health/index.js +9 -0
  55. package/dist/cloud/relay/health/index.js.map +1 -0
  56. package/dist/cloud/relay/stats/index.d.ts +5 -0
  57. package/dist/cloud/relay/stats/index.d.ts.map +1 -0
  58. package/dist/cloud/relay/stats/index.js +9 -0
  59. package/dist/cloud/relay/stats/index.js.map +1 -0
  60. package/dist/cloud/relay/sync/index.d.ts +5 -0
  61. package/dist/cloud/relay/sync/index.d.ts.map +1 -0
  62. package/dist/cloud/relay/sync/index.js +9 -0
  63. package/dist/cloud/relay/sync/index.js.map +1 -0
  64. package/dist/cloud/relay/usage/index.d.ts +5 -0
  65. package/dist/cloud/relay/usage/index.d.ts.map +1 -0
  66. package/dist/cloud/relay/usage/index.js +9 -0
  67. package/dist/cloud/relay/usage/index.js.map +1 -0
  68. package/dist/cloud/sponsors.d.ts +81 -0
  69. package/dist/cloud/sponsors.d.ts.map +1 -0
  70. package/dist/cloud/sponsors.js +130 -0
  71. package/dist/cloud/sponsors.js.map +1 -0
  72. package/dist/cloud/types.d.ts +169 -0
  73. package/dist/cloud/types.d.ts.map +1 -0
  74. package/dist/cloud/types.js +7 -0
  75. package/dist/cloud/types.js.map +1 -0
  76. package/dist/components/index.d.ts +43 -0
  77. package/dist/components/index.d.ts.map +1 -0
  78. package/dist/components/index.js +17 -0
  79. package/dist/components/index.js.map +1 -0
  80. package/dist/core/actors.d.ts +95 -0
  81. package/dist/core/actors.d.ts.map +1 -0
  82. package/dist/core/actors.js +158 -0
  83. package/dist/core/actors.js.map +1 -0
  84. package/dist/core/component/generator.d.ts +122 -0
  85. package/dist/core/component/generator.d.ts.map +1 -0
  86. package/dist/core/component/generator.js +307 -0
  87. package/dist/core/component/generator.js.map +1 -0
  88. package/dist/core/engine.d.ts +92 -0
  89. package/dist/core/engine.d.ts.map +1 -0
  90. package/dist/core/engine.js +199 -0
  91. package/dist/core/engine.js.map +1 -0
  92. package/dist/core/introspection.d.ts +141 -0
  93. package/dist/core/introspection.d.ts.map +1 -0
  94. package/dist/core/introspection.js +208 -0
  95. package/dist/core/introspection.js.map +1 -0
  96. package/dist/core/logic/generator.d.ts +76 -0
  97. package/dist/core/logic/generator.d.ts.map +1 -0
  98. package/dist/core/logic/generator.js +339 -0
  99. package/dist/core/logic/generator.js.map +1 -0
  100. package/dist/core/pluresdb/generator.d.ts +58 -0
  101. package/dist/core/pluresdb/generator.d.ts.map +1 -0
  102. package/dist/core/pluresdb/generator.js +162 -0
  103. package/dist/core/pluresdb/generator.js.map +1 -0
  104. package/dist/core/protocol.d.ts +121 -0
  105. package/dist/core/protocol.d.ts.map +1 -0
  106. package/dist/core/protocol.js +46 -0
  107. package/dist/core/protocol.js.map +1 -0
  108. package/dist/core/rules.d.ts +120 -0
  109. package/dist/core/rules.d.ts.map +1 -0
  110. package/dist/core/rules.js +81 -0
  111. package/dist/core/rules.js.map +1 -0
  112. package/dist/core/schema/loader.d.ts +47 -0
  113. package/dist/core/schema/loader.d.ts.map +1 -0
  114. package/dist/core/schema/loader.js +189 -0
  115. package/dist/core/schema/loader.js.map +1 -0
  116. package/dist/core/schema/normalize.d.ts +72 -0
  117. package/dist/core/schema/normalize.d.ts.map +1 -0
  118. package/dist/core/schema/normalize.js +190 -0
  119. package/dist/core/schema/normalize.js.map +1 -0
  120. package/dist/core/schema/types.d.ts +370 -0
  121. package/dist/core/schema/types.d.ts.map +1 -0
  122. package/dist/core/schema/types.js +161 -0
  123. package/dist/core/schema/types.js.map +1 -0
  124. package/dist/dsl/index.d.ts +152 -0
  125. package/dist/dsl/index.d.ts.map +1 -0
  126. package/dist/dsl/index.js +132 -0
  127. package/dist/dsl/index.js.map +1 -0
  128. package/dist/dsl.d.ts +124 -0
  129. package/dist/dsl.d.ts.map +1 -0
  130. package/dist/dsl.js +130 -0
  131. package/dist/dsl.js.map +1 -0
  132. package/dist/examples/advanced-todo/index.d.ts +55 -0
  133. package/dist/examples/advanced-todo/index.d.ts.map +1 -0
  134. package/dist/examples/advanced-todo/index.js +222 -0
  135. package/dist/examples/advanced-todo/index.js.map +1 -0
  136. package/dist/examples/auth-basic/index.d.ts +17 -0
  137. package/dist/examples/auth-basic/index.d.ts.map +1 -0
  138. package/dist/examples/auth-basic/index.js +122 -0
  139. package/dist/examples/auth-basic/index.js.map +1 -0
  140. package/dist/examples/cart/index.d.ts +19 -0
  141. package/dist/examples/cart/index.d.ts.map +1 -0
  142. package/dist/examples/cart/index.js +202 -0
  143. package/dist/examples/cart/index.js.map +1 -0
  144. package/dist/examples/hero-ecommerce/index.d.ts +39 -0
  145. package/dist/examples/hero-ecommerce/index.d.ts.map +1 -0
  146. package/dist/examples/hero-ecommerce/index.js +506 -0
  147. package/dist/examples/hero-ecommerce/index.js.map +1 -0
  148. package/dist/examples/svelte-counter/index.d.ts +31 -0
  149. package/dist/examples/svelte-counter/index.d.ts.map +1 -0
  150. package/dist/examples/svelte-counter/index.js +123 -0
  151. package/dist/examples/svelte-counter/index.js.map +1 -0
  152. package/dist/flows.d.ts +125 -0
  153. package/dist/flows.d.ts.map +1 -0
  154. package/dist/flows.js +160 -0
  155. package/dist/flows.js.map +1 -0
  156. package/dist/index.d.ts +67 -0
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +59 -0
  159. package/dist/index.js.map +1 -0
  160. package/dist/integrations/pluresdb.d.ts +56 -0
  161. package/dist/integrations/pluresdb.d.ts.map +1 -0
  162. package/dist/integrations/pluresdb.js +46 -0
  163. package/dist/integrations/pluresdb.js.map +1 -0
  164. package/dist/integrations/svelte.d.ts +306 -0
  165. package/dist/integrations/svelte.d.ts.map +1 -0
  166. package/dist/integrations/svelte.js +447 -0
  167. package/dist/integrations/svelte.js.map +1 -0
  168. package/dist/registry.d.ts +94 -0
  169. package/dist/registry.d.ts.map +1 -0
  170. package/dist/registry.js +181 -0
  171. package/dist/registry.js.map +1 -0
  172. package/dist/runtime/terminal-adapter.d.ts +105 -0
  173. package/dist/runtime/terminal-adapter.d.ts.map +1 -0
  174. package/dist/runtime/terminal-adapter.js +113 -0
  175. package/dist/runtime/terminal-adapter.js.map +1 -0
  176. package/dist/step.d.ts +34 -0
  177. package/dist/step.d.ts.map +1 -0
  178. package/dist/step.js +111 -0
  179. package/dist/step.js.map +1 -0
  180. package/dist/types.d.ts +63 -0
  181. package/dist/types.d.ts.map +1 -0
  182. package/dist/types.js +6 -0
  183. package/dist/types.js.map +1 -0
  184. package/docs/MONETIZATION.md +394 -0
  185. package/docs/TERMINAL_NODE.md +588 -0
  186. package/docs/guides/canvas.md +389 -0
  187. package/docs/guides/getting-started.md +347 -0
  188. package/docs/guides/history-state-pattern.md +618 -0
  189. package/docs/guides/orchestration.md +617 -0
  190. package/docs/guides/parallel-state-pattern.md +767 -0
  191. package/docs/guides/svelte-integration.md +691 -0
  192. package/package.json +96 -0
  193. package/src/__tests__/actors.test.ts +270 -0
  194. package/src/__tests__/billing.test.ts +175 -0
  195. package/src/__tests__/cloud.test.ts +247 -0
  196. package/src/__tests__/dsl.test.ts +154 -0
  197. package/src/__tests__/edge-cases.test.ts +475 -0
  198. package/src/__tests__/engine.test.ts +137 -0
  199. package/src/__tests__/generators.test.ts +270 -0
  200. package/src/__tests__/introspection.test.ts +321 -0
  201. package/src/__tests__/protocol.test.ts +40 -0
  202. package/src/__tests__/provisioning.test.ts +162 -0
  203. package/src/__tests__/schema.test.ts +241 -0
  204. package/src/__tests__/svelte-integration.test.ts +431 -0
  205. package/src/__tests__/terminal-node.test.ts +352 -0
  206. package/src/adapters/cli.ts +175 -0
  207. package/src/cli/commands/auth.ts +271 -0
  208. package/src/cli/commands/cloud.ts +281 -0
  209. package/src/cli/commands/generate.ts +225 -0
  210. package/src/cli/index.ts +190 -0
  211. package/src/cloud/README.md +383 -0
  212. package/src/cloud/auth.ts +245 -0
  213. package/src/cloud/billing.ts +336 -0
  214. package/src/cloud/client.ts +221 -0
  215. package/src/cloud/index.ts +121 -0
  216. package/src/cloud/marketplace.ts +303 -0
  217. package/src/cloud/provisioning.ts +254 -0
  218. package/src/cloud/relay/endpoints.ts +307 -0
  219. package/src/cloud/relay/health/function.json +17 -0
  220. package/src/cloud/relay/health/index.ts +10 -0
  221. package/src/cloud/relay/host.json +15 -0
  222. package/src/cloud/relay/local.settings.json +8 -0
  223. package/src/cloud/relay/stats/function.json +17 -0
  224. package/src/cloud/relay/stats/index.ts +10 -0
  225. package/src/cloud/relay/sync/function.json +17 -0
  226. package/src/cloud/relay/sync/index.ts +10 -0
  227. package/src/cloud/relay/usage/function.json +17 -0
  228. package/src/cloud/relay/usage/index.ts +10 -0
  229. package/src/cloud/sponsors.ts +213 -0
  230. package/src/cloud/types.ts +198 -0
  231. package/src/components/README.md +125 -0
  232. package/src/components/TerminalNode.svelte +457 -0
  233. package/src/components/index.ts +46 -0
  234. package/src/core/actors.ts +205 -0
  235. package/src/core/component/generator.ts +432 -0
  236. package/src/core/engine.ts +243 -0
  237. package/src/core/introspection.ts +329 -0
  238. package/src/core/logic/generator.ts +420 -0
  239. package/src/core/pluresdb/generator.ts +229 -0
  240. package/src/core/protocol.ts +132 -0
  241. package/src/core/rules.ts +167 -0
  242. package/src/core/schema/loader.ts +247 -0
  243. package/src/core/schema/normalize.ts +322 -0
  244. package/src/core/schema/types.ts +557 -0
  245. package/src/dsl/index.ts +218 -0
  246. package/src/dsl.ts +214 -0
  247. package/src/examples/advanced-todo/App.svelte +506 -0
  248. package/src/examples/advanced-todo/README.md +371 -0
  249. package/src/examples/advanced-todo/index.ts +309 -0
  250. package/src/examples/auth-basic/index.ts +163 -0
  251. package/src/examples/cart/index.ts +259 -0
  252. package/src/examples/hero-ecommerce/index.ts +657 -0
  253. package/src/examples/svelte-counter/index.ts +168 -0
  254. package/src/flows.ts +268 -0
  255. package/src/index.ts +154 -0
  256. package/src/integrations/pluresdb.ts +93 -0
  257. package/src/integrations/svelte.ts +617 -0
  258. package/src/registry.ts +223 -0
  259. package/src/runtime/terminal-adapter.ts +175 -0
  260. package/src/step.ts +151 -0
  261. package/src/types.ts +70 -0
  262. package/templates/basic-app/README.md +147 -0
  263. package/templates/fullstack-app/README.md +279 -0
@@ -0,0 +1,336 @@
1
+ /**
2
+ * GitHub Sponsors and Marketplace Billing
3
+ *
4
+ * Types and utilities for GitHub-based monetization.
5
+ */
6
+
7
+ /**
8
+ * Praxis Cloud subscription tiers
9
+ */
10
+ export enum SubscriptionTier {
11
+ /**
12
+ * Free tier - limited usage
13
+ */
14
+ FREE = "free",
15
+
16
+ /**
17
+ * Solo tier - individual developers via GitHub Sponsors
18
+ */
19
+ SOLO = "solo",
20
+
21
+ /**
22
+ * Team tier - small teams via GitHub Sponsors
23
+ */
24
+ TEAM = "team",
25
+
26
+ /**
27
+ * Enterprise tier - self-service enterprise via GitHub Sponsors/Marketplace
28
+ */
29
+ ENTERPRISE = "enterprise",
30
+ }
31
+
32
+ /**
33
+ * Billing provider type
34
+ */
35
+ export enum BillingProvider {
36
+ /**
37
+ * GitHub Sponsors
38
+ */
39
+ SPONSORS = "sponsors",
40
+
41
+ /**
42
+ * GitHub Marketplace (SaaS)
43
+ */
44
+ MARKETPLACE = "marketplace",
45
+
46
+ /**
47
+ * Free tier (no billing)
48
+ */
49
+ NONE = "none",
50
+ }
51
+
52
+ /**
53
+ * Subscription status
54
+ */
55
+ export enum SubscriptionStatus {
56
+ /**
57
+ * Active subscription
58
+ */
59
+ ACTIVE = "active",
60
+
61
+ /**
62
+ * Subscription cancelled, but still valid until period end
63
+ */
64
+ CANCELLED = "cancelled",
65
+
66
+ /**
67
+ * Subscription expired or payment failed
68
+ */
69
+ EXPIRED = "expired",
70
+
71
+ /**
72
+ * No subscription
73
+ */
74
+ NONE = "none",
75
+ }
76
+
77
+ /**
78
+ * Tier limits for different subscription levels
79
+ */
80
+ export interface TierLimits {
81
+ /**
82
+ * Maximum sync operations per month
83
+ */
84
+ maxSyncsPerMonth: number;
85
+
86
+ /**
87
+ * Maximum storage in bytes
88
+ */
89
+ maxStorageBytes: number;
90
+
91
+ /**
92
+ * Maximum number of team members (null = unlimited)
93
+ */
94
+ maxTeamMembers: number | null;
95
+
96
+ /**
97
+ * Maximum number of apps/projects
98
+ */
99
+ maxApps: number;
100
+
101
+ /**
102
+ * Support level
103
+ */
104
+ supportLevel: "community" | "standard" | "priority";
105
+ }
106
+
107
+ /**
108
+ * Subscription information
109
+ */
110
+ export interface Subscription {
111
+ /**
112
+ * Subscription tier
113
+ */
114
+ tier: SubscriptionTier;
115
+
116
+ /**
117
+ * Subscription status
118
+ */
119
+ status: SubscriptionStatus;
120
+
121
+ /**
122
+ * Billing provider
123
+ */
124
+ provider: BillingProvider;
125
+
126
+ /**
127
+ * GitHub sponsor tier ID (if applicable)
128
+ */
129
+ sponsorTierId?: string;
130
+
131
+ /**
132
+ * GitHub Marketplace plan ID (if applicable)
133
+ */
134
+ marketplacePlanId?: number;
135
+
136
+ /**
137
+ * Subscription start date
138
+ */
139
+ startDate: number;
140
+
141
+ /**
142
+ * Current period end date
143
+ */
144
+ periodEnd?: number;
145
+
146
+ /**
147
+ * Whether subscription auto-renews
148
+ */
149
+ autoRenew: boolean;
150
+
151
+ /**
152
+ * Usage limits for this tier
153
+ */
154
+ limits: TierLimits;
155
+ }
156
+
157
+ /**
158
+ * Billing event type
159
+ */
160
+ export interface BillingEvent {
161
+ /**
162
+ * Event type
163
+ */
164
+ type: "subscription_created" | "subscription_cancelled" | "subscription_renewed" | "tier_changed";
165
+
166
+ /**
167
+ * Timestamp
168
+ */
169
+ timestamp: number;
170
+
171
+ /**
172
+ * User/organization ID
173
+ */
174
+ userId: number;
175
+
176
+ /**
177
+ * Old subscription (for changes)
178
+ */
179
+ oldSubscription?: Subscription;
180
+
181
+ /**
182
+ * New subscription
183
+ */
184
+ newSubscription: Subscription;
185
+ }
186
+
187
+ /**
188
+ * Default tier limits
189
+ */
190
+ export const TIER_LIMITS: Record<SubscriptionTier, TierLimits> = {
191
+ [SubscriptionTier.FREE]: {
192
+ maxSyncsPerMonth: 1000,
193
+ maxStorageBytes: 10 * 1024 * 1024, // 10 MB
194
+ maxTeamMembers: 1,
195
+ maxApps: 1,
196
+ supportLevel: "community",
197
+ },
198
+ [SubscriptionTier.SOLO]: {
199
+ maxSyncsPerMonth: 50000,
200
+ maxStorageBytes: 1024 * 1024 * 1024, // 1 GB
201
+ maxTeamMembers: 1,
202
+ maxApps: 10,
203
+ supportLevel: "standard",
204
+ },
205
+ [SubscriptionTier.TEAM]: {
206
+ maxSyncsPerMonth: 500000,
207
+ maxStorageBytes: 10 * 1024 * 1024 * 1024, // 10 GB
208
+ maxTeamMembers: 10,
209
+ maxApps: 50,
210
+ supportLevel: "standard",
211
+ },
212
+ [SubscriptionTier.ENTERPRISE]: {
213
+ maxSyncsPerMonth: 5000000,
214
+ maxStorageBytes: 100 * 1024 * 1024 * 1024, // 100 GB
215
+ maxTeamMembers: null, // unlimited
216
+ maxApps: 1000,
217
+ supportLevel: "priority",
218
+ },
219
+ };
220
+
221
+ /**
222
+ * Check if a user has access to a specific tier
223
+ */
224
+ export function hasAccessToTier(
225
+ subscription: Subscription,
226
+ requiredTier: SubscriptionTier
227
+ ): boolean {
228
+ if (subscription.status !== SubscriptionStatus.ACTIVE) {
229
+ return false;
230
+ }
231
+
232
+ const tierOrder = [
233
+ SubscriptionTier.FREE,
234
+ SubscriptionTier.SOLO,
235
+ SubscriptionTier.TEAM,
236
+ SubscriptionTier.ENTERPRISE,
237
+ ];
238
+
239
+ const currentTierIndex = tierOrder.indexOf(subscription.tier);
240
+ const requiredTierIndex = tierOrder.indexOf(requiredTier);
241
+
242
+ return currentTierIndex >= requiredTierIndex;
243
+ }
244
+
245
+ /**
246
+ * Check if usage is within tier limits
247
+ */
248
+ export function checkUsageLimits(
249
+ subscription: Subscription,
250
+ usage: {
251
+ syncCount: number;
252
+ storageBytes: number;
253
+ teamMembers: number;
254
+ appCount: number;
255
+ }
256
+ ): {
257
+ withinLimits: boolean;
258
+ violations: string[];
259
+ } {
260
+ const violations: string[] = [];
261
+
262
+ if (usage.syncCount > subscription.limits.maxSyncsPerMonth) {
263
+ violations.push(
264
+ `Sync limit exceeded: ${usage.syncCount}/${subscription.limits.maxSyncsPerMonth}`
265
+ );
266
+ }
267
+
268
+ if (usage.storageBytes > subscription.limits.maxStorageBytes) {
269
+ violations.push(
270
+ `Storage limit exceeded: ${(usage.storageBytes / 1024 / 1024).toFixed(2)}MB/${(subscription.limits.maxStorageBytes / 1024 / 1024).toFixed(2)}MB`
271
+ );
272
+ }
273
+
274
+ if (
275
+ subscription.limits.maxTeamMembers !== null &&
276
+ usage.teamMembers > subscription.limits.maxTeamMembers
277
+ ) {
278
+ violations.push(
279
+ `Team member limit exceeded: ${usage.teamMembers}/${subscription.limits.maxTeamMembers}`
280
+ );
281
+ }
282
+
283
+ if (usage.appCount > subscription.limits.maxApps) {
284
+ violations.push(
285
+ `App limit exceeded: ${usage.appCount}/${subscription.limits.maxApps}`
286
+ );
287
+ }
288
+
289
+ return {
290
+ withinLimits: violations.length === 0,
291
+ violations,
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Create a free tier subscription
297
+ */
298
+ export function createFreeSubscription(): Subscription {
299
+ return {
300
+ tier: SubscriptionTier.FREE,
301
+ status: SubscriptionStatus.ACTIVE,
302
+ provider: BillingProvider.NONE,
303
+ startDate: Date.now(),
304
+ autoRenew: true,
305
+ limits: TIER_LIMITS[SubscriptionTier.FREE],
306
+ };
307
+ }
308
+
309
+ /**
310
+ * Create a subscription from GitHub Sponsors tier
311
+ */
312
+ export function createSponsorSubscription(
313
+ tierName: string,
314
+ monthlyPriceInCents: number
315
+ ): Subscription {
316
+ let tier = SubscriptionTier.FREE;
317
+
318
+ // Map price to tier (example pricing)
319
+ if (monthlyPriceInCents >= 5000) { // $50/month
320
+ tier = SubscriptionTier.ENTERPRISE;
321
+ } else if (monthlyPriceInCents >= 2000) { // $20/month
322
+ tier = SubscriptionTier.TEAM;
323
+ } else if (monthlyPriceInCents >= 500) { // $5/month
324
+ tier = SubscriptionTier.SOLO;
325
+ }
326
+
327
+ return {
328
+ tier,
329
+ status: SubscriptionStatus.ACTIVE,
330
+ provider: BillingProvider.SPONSORS,
331
+ sponsorTierId: tierName,
332
+ startDate: Date.now(),
333
+ autoRenew: true,
334
+ limits: TIER_LIMITS[tier],
335
+ };
336
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Cloud Relay Client
3
+ *
4
+ * Client for connecting to Praxis Cloud Relay service (Azure-based).
5
+ */
6
+
7
+ import type {
8
+ CloudRelayConfig,
9
+ CloudRelayClient,
10
+ RelayStatus,
11
+ CRDTSyncMessage,
12
+ UsageMetrics,
13
+ HealthCheckResponse,
14
+ } from "./types.js";
15
+
16
+ /**
17
+ * Create a cloud relay client
18
+ */
19
+ export function createCloudRelay(config: CloudRelayConfig): CloudRelayClient {
20
+ let status: RelayStatus = {
21
+ connected: false,
22
+ endpoint: config.endpoint,
23
+ appId: config.appId,
24
+ };
25
+
26
+ let syncTimer: NodeJS.Timeout | null = null;
27
+ const vectorClock: Record<string, number> = {};
28
+
29
+ return {
30
+ async connect(): Promise<void> {
31
+ // Validate endpoint
32
+ if (!config.endpoint) {
33
+ throw new Error("Cloud relay endpoint is required");
34
+ }
35
+
36
+ // Test connection
37
+ try {
38
+ const response = await fetch(`${config.endpoint}/health`, {
39
+ method: "GET",
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ ...(config.authToken && {
43
+ Authorization: `Bearer ${config.authToken}`,
44
+ }),
45
+ },
46
+ });
47
+
48
+ if (!response.ok) {
49
+ throw new Error(`Health check failed: ${response.statusText}`);
50
+ }
51
+
52
+ status.connected = true;
53
+ status.lastSync = Date.now();
54
+
55
+ // Start auto-sync if enabled
56
+ if (config.autoSync) {
57
+ const interval = config.syncInterval || 5000;
58
+ syncTimer = setInterval(() => {
59
+ // Auto-sync logic would go here
60
+ // For now, just update lastSync
61
+ status.lastSync = Date.now();
62
+ }, interval);
63
+ }
64
+ } catch (error) {
65
+ status.connected = false;
66
+ throw new Error(
67
+ `Failed to connect to cloud relay: ${error instanceof Error ? error.message : String(error)}`
68
+ );
69
+ }
70
+ },
71
+
72
+ async disconnect(): Promise<void> {
73
+ if (syncTimer) {
74
+ clearInterval(syncTimer);
75
+ syncTimer = null;
76
+ }
77
+ status.connected = false;
78
+ },
79
+
80
+ async sync(message: CRDTSyncMessage): Promise<void> {
81
+ if (!status.connected) {
82
+ throw new Error("Not connected to cloud relay");
83
+ }
84
+
85
+ // Update vector clock
86
+ vectorClock[config.appId] = (vectorClock[config.appId] || 0) + 1;
87
+ message.clock = { ...vectorClock };
88
+
89
+ try {
90
+ const response = await fetch(`${config.endpoint}/sync`, {
91
+ method: "POST",
92
+ headers: {
93
+ "Content-Type": "application/json",
94
+ ...(config.authToken && {
95
+ Authorization: `Bearer ${config.authToken}`,
96
+ }),
97
+ },
98
+ body: JSON.stringify(message),
99
+ });
100
+
101
+ if (!response.ok) {
102
+ throw new Error(`Sync failed: ${response.statusText}`);
103
+ }
104
+
105
+ status.lastSync = Date.now();
106
+
107
+ // Merge received vector clock
108
+ const result = await response.json() as any;
109
+ if (result.clock) {
110
+ Object.entries(result.clock).forEach(([key, value]) => {
111
+ vectorClock[key] = Math.max(
112
+ vectorClock[key] || 0,
113
+ value as number
114
+ );
115
+ });
116
+ }
117
+ } catch (error) {
118
+ throw new Error(
119
+ `Failed to sync: ${error instanceof Error ? error.message : String(error)}`
120
+ );
121
+ }
122
+ },
123
+
124
+ async getUsage(): Promise<UsageMetrics> {
125
+ if (!status.connected) {
126
+ throw new Error("Not connected to cloud relay");
127
+ }
128
+
129
+ try {
130
+ const response = await fetch(
131
+ `${config.endpoint}/usage?appId=${config.appId}`,
132
+ {
133
+ method: "GET",
134
+ headers: {
135
+ "Content-Type": "application/json",
136
+ ...(config.authToken && {
137
+ Authorization: `Bearer ${config.authToken}`,
138
+ }),
139
+ },
140
+ }
141
+ );
142
+
143
+ if (!response.ok) {
144
+ throw new Error(`Failed to get usage: ${response.statusText}`);
145
+ }
146
+
147
+ return await response.json() as UsageMetrics;
148
+ } catch (error) {
149
+ throw new Error(
150
+ `Failed to get usage metrics: ${error instanceof Error ? error.message : String(error)}`
151
+ );
152
+ }
153
+ },
154
+
155
+ async getHealth(): Promise<HealthCheckResponse> {
156
+ try {
157
+ const response = await fetch(`${config.endpoint}/health`, {
158
+ method: "GET",
159
+ headers: {
160
+ "Content-Type": "application/json",
161
+ },
162
+ });
163
+
164
+ if (!response.ok) {
165
+ throw new Error(`Health check failed: ${response.statusText}`);
166
+ }
167
+
168
+ return await response.json() as HealthCheckResponse;
169
+ } catch (error) {
170
+ throw new Error(
171
+ `Failed to get health status: ${error instanceof Error ? error.message : String(error)}`
172
+ );
173
+ }
174
+ },
175
+
176
+ getStatus(): RelayStatus {
177
+ return { ...status };
178
+ },
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Connect to Praxis Cloud Relay
184
+ *
185
+ * @param endpoint - Azure Function App endpoint URL
186
+ * @param options - Additional configuration options
187
+ * @returns Cloud relay client instance
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * import { connectRelay } from "@plures/praxis/cloud";
192
+ *
193
+ * const relay = await connectRelay("https://my-app.azurewebsites.net", {
194
+ * appId: "my-app",
195
+ * authToken: "github-token",
196
+ * autoSync: true
197
+ * });
198
+ *
199
+ * // Sync data
200
+ * await relay.sync({
201
+ * type: "delta",
202
+ * appId: "my-app",
203
+ * clock: {},
204
+ * facts: [...],
205
+ * timestamp: Date.now()
206
+ * });
207
+ * ```
208
+ */
209
+ export async function connectRelay(
210
+ endpoint: string,
211
+ options: Omit<CloudRelayConfig, "endpoint"> = { appId: "default" }
212
+ ): Promise<CloudRelayClient> {
213
+ const config: CloudRelayConfig = {
214
+ endpoint,
215
+ ...options,
216
+ };
217
+
218
+ const client = createCloudRelay(config);
219
+ await client.connect();
220
+ return client;
221
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Praxis Cloud
3
+ *
4
+ * Cloud relay and synchronization for Praxis applications.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { connectRelay } from "@plures/praxis/cloud";
9
+ *
10
+ * const relay = await connectRelay("https://my-app.azurewebsites.net", {
11
+ * appId: "my-app",
12
+ * authToken: "github-token",
13
+ * autoSync: true
14
+ * });
15
+ *
16
+ * // Sync data
17
+ * await relay.sync({
18
+ * type: "delta",
19
+ * appId: "my-app",
20
+ * clock: {},
21
+ * facts: [...],
22
+ * timestamp: Date.now()
23
+ * });
24
+ *
25
+ * // Get usage metrics
26
+ * const usage = await relay.getUsage();
27
+ * console.log(`Syncs: ${usage.syncCount}, Events: ${usage.eventCount}`);
28
+ * ```
29
+ */
30
+
31
+ // Client
32
+ export { createCloudRelay, connectRelay } from "./client.js";
33
+
34
+ // Types
35
+ export type {
36
+ CloudRelayConfig,
37
+ CloudRelayClient,
38
+ RelayStatus,
39
+ CRDTSyncMessage,
40
+ UsageMetrics,
41
+ HealthCheckResponse,
42
+ GitHubUser,
43
+ AuthResult,
44
+ } from "./types.js";
45
+
46
+ // Authentication
47
+ export {
48
+ GitHubOAuth,
49
+ createGitHubOAuth,
50
+ authenticateWithDeviceFlow,
51
+ } from "./auth.js";
52
+ export type { GitHubOAuthConfig } from "./auth.js";
53
+
54
+ // Billing and Subscriptions
55
+ export {
56
+ SubscriptionTier,
57
+ BillingProvider,
58
+ SubscriptionStatus,
59
+ TIER_LIMITS,
60
+ hasAccessToTier,
61
+ checkUsageLimits,
62
+ createFreeSubscription,
63
+ createSponsorSubscription,
64
+ } from "./billing.js";
65
+ export type {
66
+ TierLimits,
67
+ Subscription,
68
+ BillingEvent,
69
+ } from "./billing.js";
70
+
71
+ // GitHub Sponsors
72
+ export {
73
+ GitHubSponsorsClient,
74
+ createSponsorsClient,
75
+ } from "./sponsors.js";
76
+ export type {
77
+ SponsorTier,
78
+ Sponsorship,
79
+ } from "./sponsors.js";
80
+
81
+ // GitHub Marketplace
82
+ export {
83
+ GitHubMarketplaceClient,
84
+ createMarketplaceClient,
85
+ MARKETPLACE_PLANS,
86
+ } from "./marketplace.js";
87
+ export type {
88
+ MarketplacePlan,
89
+ MarketplaceAccount,
90
+ MarketplaceWebhookEvent,
91
+ } from "./marketplace.js";
92
+
93
+ // Auto-Provisioning
94
+ export {
95
+ generateStorageNamespace,
96
+ generateTenantId,
97
+ createTenant,
98
+ validateStorageNamespace,
99
+ getAppStorageContainer,
100
+ provisionTenant,
101
+ getOrCreateTenant,
102
+ } from "./provisioning.js";
103
+ export type {
104
+ Tenant,
105
+ ProvisioningResult,
106
+ } from "./provisioning.js";
107
+
108
+ // Relay endpoints (for Azure Functions deployment)
109
+ export {
110
+ healthEndpoint,
111
+ syncEndpoint,
112
+ usageEndpoint,
113
+ statsEndpoint,
114
+ eventsEndpoint,
115
+ schemaEndpoint,
116
+ } from "./relay/endpoints.js";
117
+ export type {
118
+ AzureContext,
119
+ AzureHttpRequest,
120
+ AzureHttpResponse,
121
+ } from "./relay/endpoints.js";