@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.
- package/FRAMEWORK.md +420 -0
- package/LICENSE +21 -0
- package/README.md +1310 -0
- package/dist/adapters/cli.d.ts +43 -0
- package/dist/adapters/cli.d.ts.map +1 -0
- package/dist/adapters/cli.js +126 -0
- package/dist/adapters/cli.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +26 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +233 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/cloud.d.ts +27 -0
- package/dist/cli/commands/cloud.d.ts.map +1 -0
- package/dist/cli/commands/cloud.js +232 -0
- package/dist/cli/commands/cloud.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +25 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +168 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +179 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cloud/auth.d.ts +51 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +194 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/billing.d.ts +184 -0
- package/dist/cloud/billing.d.ts.map +1 -0
- package/dist/cloud/billing.js +179 -0
- package/dist/cloud/billing.js.map +1 -0
- package/dist/cloud/client.d.ts +39 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +176 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/cloud/index.d.ts +44 -0
- package/dist/cloud/index.d.ts.map +1 -0
- package/dist/cloud/index.js +44 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/cloud/marketplace.d.ts +166 -0
- package/dist/cloud/marketplace.d.ts.map +1 -0
- package/dist/cloud/marketplace.js +159 -0
- package/dist/cloud/marketplace.js.map +1 -0
- package/dist/cloud/provisioning.d.ts +110 -0
- package/dist/cloud/provisioning.d.ts.map +1 -0
- package/dist/cloud/provisioning.js +148 -0
- package/dist/cloud/provisioning.js.map +1 -0
- package/dist/cloud/relay/endpoints.d.ts +62 -0
- package/dist/cloud/relay/endpoints.d.ts.map +1 -0
- package/dist/cloud/relay/endpoints.js +217 -0
- package/dist/cloud/relay/endpoints.js.map +1 -0
- package/dist/cloud/relay/health/index.d.ts +5 -0
- package/dist/cloud/relay/health/index.d.ts.map +1 -0
- package/dist/cloud/relay/health/index.js +9 -0
- package/dist/cloud/relay/health/index.js.map +1 -0
- package/dist/cloud/relay/stats/index.d.ts +5 -0
- package/dist/cloud/relay/stats/index.d.ts.map +1 -0
- package/dist/cloud/relay/stats/index.js +9 -0
- package/dist/cloud/relay/stats/index.js.map +1 -0
- package/dist/cloud/relay/sync/index.d.ts +5 -0
- package/dist/cloud/relay/sync/index.d.ts.map +1 -0
- package/dist/cloud/relay/sync/index.js +9 -0
- package/dist/cloud/relay/sync/index.js.map +1 -0
- package/dist/cloud/relay/usage/index.d.ts +5 -0
- package/dist/cloud/relay/usage/index.d.ts.map +1 -0
- package/dist/cloud/relay/usage/index.js +9 -0
- package/dist/cloud/relay/usage/index.js.map +1 -0
- package/dist/cloud/sponsors.d.ts +81 -0
- package/dist/cloud/sponsors.d.ts.map +1 -0
- package/dist/cloud/sponsors.js +130 -0
- package/dist/cloud/sponsors.js.map +1 -0
- package/dist/cloud/types.d.ts +169 -0
- package/dist/cloud/types.d.ts.map +1 -0
- package/dist/cloud/types.js +7 -0
- package/dist/cloud/types.js.map +1 -0
- package/dist/components/index.d.ts +43 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +17 -0
- package/dist/components/index.js.map +1 -0
- package/dist/core/actors.d.ts +95 -0
- package/dist/core/actors.d.ts.map +1 -0
- package/dist/core/actors.js +158 -0
- package/dist/core/actors.js.map +1 -0
- package/dist/core/component/generator.d.ts +122 -0
- package/dist/core/component/generator.d.ts.map +1 -0
- package/dist/core/component/generator.js +307 -0
- package/dist/core/component/generator.js.map +1 -0
- package/dist/core/engine.d.ts +92 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +199 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/introspection.d.ts +141 -0
- package/dist/core/introspection.d.ts.map +1 -0
- package/dist/core/introspection.js +208 -0
- package/dist/core/introspection.js.map +1 -0
- package/dist/core/logic/generator.d.ts +76 -0
- package/dist/core/logic/generator.d.ts.map +1 -0
- package/dist/core/logic/generator.js +339 -0
- package/dist/core/logic/generator.js.map +1 -0
- package/dist/core/pluresdb/generator.d.ts +58 -0
- package/dist/core/pluresdb/generator.d.ts.map +1 -0
- package/dist/core/pluresdb/generator.js +162 -0
- package/dist/core/pluresdb/generator.js.map +1 -0
- package/dist/core/protocol.d.ts +121 -0
- package/dist/core/protocol.d.ts.map +1 -0
- package/dist/core/protocol.js +46 -0
- package/dist/core/protocol.js.map +1 -0
- package/dist/core/rules.d.ts +120 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +81 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/schema/loader.d.ts +47 -0
- package/dist/core/schema/loader.d.ts.map +1 -0
- package/dist/core/schema/loader.js +189 -0
- package/dist/core/schema/loader.js.map +1 -0
- package/dist/core/schema/normalize.d.ts +72 -0
- package/dist/core/schema/normalize.d.ts.map +1 -0
- package/dist/core/schema/normalize.js +190 -0
- package/dist/core/schema/normalize.js.map +1 -0
- package/dist/core/schema/types.d.ts +370 -0
- package/dist/core/schema/types.d.ts.map +1 -0
- package/dist/core/schema/types.js +161 -0
- package/dist/core/schema/types.js.map +1 -0
- package/dist/dsl/index.d.ts +152 -0
- package/dist/dsl/index.d.ts.map +1 -0
- package/dist/dsl/index.js +132 -0
- package/dist/dsl/index.js.map +1 -0
- package/dist/dsl.d.ts +124 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +130 -0
- package/dist/dsl.js.map +1 -0
- package/dist/examples/advanced-todo/index.d.ts +55 -0
- package/dist/examples/advanced-todo/index.d.ts.map +1 -0
- package/dist/examples/advanced-todo/index.js +222 -0
- package/dist/examples/advanced-todo/index.js.map +1 -0
- package/dist/examples/auth-basic/index.d.ts +17 -0
- package/dist/examples/auth-basic/index.d.ts.map +1 -0
- package/dist/examples/auth-basic/index.js +122 -0
- package/dist/examples/auth-basic/index.js.map +1 -0
- package/dist/examples/cart/index.d.ts +19 -0
- package/dist/examples/cart/index.d.ts.map +1 -0
- package/dist/examples/cart/index.js +202 -0
- package/dist/examples/cart/index.js.map +1 -0
- package/dist/examples/hero-ecommerce/index.d.ts +39 -0
- package/dist/examples/hero-ecommerce/index.d.ts.map +1 -0
- package/dist/examples/hero-ecommerce/index.js +506 -0
- package/dist/examples/hero-ecommerce/index.js.map +1 -0
- package/dist/examples/svelte-counter/index.d.ts +31 -0
- package/dist/examples/svelte-counter/index.d.ts.map +1 -0
- package/dist/examples/svelte-counter/index.js +123 -0
- package/dist/examples/svelte-counter/index.js.map +1 -0
- package/dist/flows.d.ts +125 -0
- package/dist/flows.d.ts.map +1 -0
- package/dist/flows.js +160 -0
- package/dist/flows.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/pluresdb.d.ts +56 -0
- package/dist/integrations/pluresdb.d.ts.map +1 -0
- package/dist/integrations/pluresdb.js +46 -0
- package/dist/integrations/pluresdb.js.map +1 -0
- package/dist/integrations/svelte.d.ts +306 -0
- package/dist/integrations/svelte.d.ts.map +1 -0
- package/dist/integrations/svelte.js +447 -0
- package/dist/integrations/svelte.js.map +1 -0
- package/dist/registry.d.ts +94 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +181 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/terminal-adapter.d.ts +105 -0
- package/dist/runtime/terminal-adapter.d.ts.map +1 -0
- package/dist/runtime/terminal-adapter.js +113 -0
- package/dist/runtime/terminal-adapter.js.map +1 -0
- package/dist/step.d.ts +34 -0
- package/dist/step.d.ts.map +1 -0
- package/dist/step.js +111 -0
- package/dist/step.js.map +1 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/MONETIZATION.md +394 -0
- package/docs/TERMINAL_NODE.md +588 -0
- package/docs/guides/canvas.md +389 -0
- package/docs/guides/getting-started.md +347 -0
- package/docs/guides/history-state-pattern.md +618 -0
- package/docs/guides/orchestration.md +617 -0
- package/docs/guides/parallel-state-pattern.md +767 -0
- package/docs/guides/svelte-integration.md +691 -0
- package/package.json +96 -0
- package/src/__tests__/actors.test.ts +270 -0
- package/src/__tests__/billing.test.ts +175 -0
- package/src/__tests__/cloud.test.ts +247 -0
- package/src/__tests__/dsl.test.ts +154 -0
- package/src/__tests__/edge-cases.test.ts +475 -0
- package/src/__tests__/engine.test.ts +137 -0
- package/src/__tests__/generators.test.ts +270 -0
- package/src/__tests__/introspection.test.ts +321 -0
- package/src/__tests__/protocol.test.ts +40 -0
- package/src/__tests__/provisioning.test.ts +162 -0
- package/src/__tests__/schema.test.ts +241 -0
- package/src/__tests__/svelte-integration.test.ts +431 -0
- package/src/__tests__/terminal-node.test.ts +352 -0
- package/src/adapters/cli.ts +175 -0
- package/src/cli/commands/auth.ts +271 -0
- package/src/cli/commands/cloud.ts +281 -0
- package/src/cli/commands/generate.ts +225 -0
- package/src/cli/index.ts +190 -0
- package/src/cloud/README.md +383 -0
- package/src/cloud/auth.ts +245 -0
- package/src/cloud/billing.ts +336 -0
- package/src/cloud/client.ts +221 -0
- package/src/cloud/index.ts +121 -0
- package/src/cloud/marketplace.ts +303 -0
- package/src/cloud/provisioning.ts +254 -0
- package/src/cloud/relay/endpoints.ts +307 -0
- package/src/cloud/relay/health/function.json +17 -0
- package/src/cloud/relay/health/index.ts +10 -0
- package/src/cloud/relay/host.json +15 -0
- package/src/cloud/relay/local.settings.json +8 -0
- package/src/cloud/relay/stats/function.json +17 -0
- package/src/cloud/relay/stats/index.ts +10 -0
- package/src/cloud/relay/sync/function.json +17 -0
- package/src/cloud/relay/sync/index.ts +10 -0
- package/src/cloud/relay/usage/function.json +17 -0
- package/src/cloud/relay/usage/index.ts +10 -0
- package/src/cloud/sponsors.ts +213 -0
- package/src/cloud/types.ts +198 -0
- package/src/components/README.md +125 -0
- package/src/components/TerminalNode.svelte +457 -0
- package/src/components/index.ts +46 -0
- package/src/core/actors.ts +205 -0
- package/src/core/component/generator.ts +432 -0
- package/src/core/engine.ts +243 -0
- package/src/core/introspection.ts +329 -0
- package/src/core/logic/generator.ts +420 -0
- package/src/core/pluresdb/generator.ts +229 -0
- package/src/core/protocol.ts +132 -0
- package/src/core/rules.ts +167 -0
- package/src/core/schema/loader.ts +247 -0
- package/src/core/schema/normalize.ts +322 -0
- package/src/core/schema/types.ts +557 -0
- package/src/dsl/index.ts +218 -0
- package/src/dsl.ts +214 -0
- package/src/examples/advanced-todo/App.svelte +506 -0
- package/src/examples/advanced-todo/README.md +371 -0
- package/src/examples/advanced-todo/index.ts +309 -0
- package/src/examples/auth-basic/index.ts +163 -0
- package/src/examples/cart/index.ts +259 -0
- package/src/examples/hero-ecommerce/index.ts +657 -0
- package/src/examples/svelte-counter/index.ts +168 -0
- package/src/flows.ts +268 -0
- package/src/index.ts +154 -0
- package/src/integrations/pluresdb.ts +93 -0
- package/src/integrations/svelte.ts +617 -0
- package/src/registry.ts +223 -0
- package/src/runtime/terminal-adapter.ts +175 -0
- package/src/step.ts +151 -0
- package/src/types.ts +70 -0
- package/templates/basic-app/README.md +147 -0
- package/templates/fullstack-app/README.md +279 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure Functions Relay Endpoints
|
|
3
|
+
*
|
|
4
|
+
* HTTP-triggered Azure Functions for Praxis Cloud Relay.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
CRDTSyncMessage,
|
|
9
|
+
UsageMetrics,
|
|
10
|
+
HealthCheckResponse,
|
|
11
|
+
} from "../types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Azure Function context (simplified interface)
|
|
15
|
+
*/
|
|
16
|
+
export interface AzureContext {
|
|
17
|
+
log: (message: string) => void;
|
|
18
|
+
done: (err?: Error, result?: unknown) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Azure HTTP request
|
|
23
|
+
*/
|
|
24
|
+
export interface AzureHttpRequest {
|
|
25
|
+
method: string;
|
|
26
|
+
url: string;
|
|
27
|
+
headers: Record<string, string>;
|
|
28
|
+
query: Record<string, string>;
|
|
29
|
+
body?: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Azure HTTP response
|
|
34
|
+
*/
|
|
35
|
+
export interface AzureHttpResponse {
|
|
36
|
+
status: number;
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
body?: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* In-memory storage for demo (replace with Azure Storage in production)
|
|
43
|
+
*/
|
|
44
|
+
const storage = {
|
|
45
|
+
syncs: new Map<string, CRDTSyncMessage[]>(),
|
|
46
|
+
usage: new Map<string, UsageMetrics>(),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Health check endpoint
|
|
51
|
+
* GET /health
|
|
52
|
+
*/
|
|
53
|
+
export async function healthEndpoint(
|
|
54
|
+
context: AzureContext,
|
|
55
|
+
_req: AzureHttpRequest
|
|
56
|
+
): Promise<AzureHttpResponse> {
|
|
57
|
+
context.log("Health check requested");
|
|
58
|
+
|
|
59
|
+
const response: HealthCheckResponse = {
|
|
60
|
+
status: "healthy",
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
version: "0.1.0",
|
|
63
|
+
services: {
|
|
64
|
+
relay: true,
|
|
65
|
+
eventGrid: true,
|
|
66
|
+
storage: true,
|
|
67
|
+
auth: true,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
status: 200,
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: response,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* CRDT sync endpoint
|
|
80
|
+
* POST /sync
|
|
81
|
+
*/
|
|
82
|
+
export async function syncEndpoint(
|
|
83
|
+
context: AzureContext,
|
|
84
|
+
req: AzureHttpRequest
|
|
85
|
+
): Promise<AzureHttpResponse> {
|
|
86
|
+
context.log("Sync request received");
|
|
87
|
+
|
|
88
|
+
// Validate request
|
|
89
|
+
if (req.method !== "POST") {
|
|
90
|
+
return {
|
|
91
|
+
status: 405,
|
|
92
|
+
body: { error: "Method not allowed" },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const message = req.body as CRDTSyncMessage;
|
|
97
|
+
|
|
98
|
+
if (!message || !message.appId) {
|
|
99
|
+
return {
|
|
100
|
+
status: 400,
|
|
101
|
+
body: { error: "Invalid sync message" },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Store sync message
|
|
106
|
+
const appSyncs = storage.syncs.get(message.appId) || [];
|
|
107
|
+
appSyncs.push(message);
|
|
108
|
+
storage.syncs.set(message.appId, appSyncs);
|
|
109
|
+
|
|
110
|
+
// Update usage metrics
|
|
111
|
+
const usage = storage.usage.get(message.appId) || {
|
|
112
|
+
appId: message.appId,
|
|
113
|
+
syncCount: 0,
|
|
114
|
+
eventCount: 0,
|
|
115
|
+
factCount: 0,
|
|
116
|
+
storageBytes: 0,
|
|
117
|
+
periodStart: Date.now(),
|
|
118
|
+
periodEnd: Date.now(),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
usage.syncCount++;
|
|
122
|
+
usage.eventCount += message.events?.length || 0;
|
|
123
|
+
usage.factCount += message.facts?.length || 0;
|
|
124
|
+
usage.storageBytes += JSON.stringify(message).length;
|
|
125
|
+
usage.periodEnd = Date.now();
|
|
126
|
+
|
|
127
|
+
storage.usage.set(message.appId, usage);
|
|
128
|
+
|
|
129
|
+
context.log(`Synced for app ${message.appId}: ${usage.syncCount} total syncs`);
|
|
130
|
+
|
|
131
|
+
// Return updated vector clock
|
|
132
|
+
return {
|
|
133
|
+
status: 200,
|
|
134
|
+
headers: { "Content-Type": "application/json" },
|
|
135
|
+
body: {
|
|
136
|
+
success: true,
|
|
137
|
+
clock: message.clock,
|
|
138
|
+
timestamp: Date.now(),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Usage metrics endpoint
|
|
145
|
+
* GET /usage?appId=<appId>
|
|
146
|
+
*/
|
|
147
|
+
export async function usageEndpoint(
|
|
148
|
+
context: AzureContext,
|
|
149
|
+
req: AzureHttpRequest
|
|
150
|
+
): Promise<AzureHttpResponse> {
|
|
151
|
+
context.log("Usage metrics requested");
|
|
152
|
+
|
|
153
|
+
const appId = req.query.appId;
|
|
154
|
+
|
|
155
|
+
if (!appId) {
|
|
156
|
+
return {
|
|
157
|
+
status: 400,
|
|
158
|
+
body: { error: "appId query parameter is required" },
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const usage = storage.usage.get(appId);
|
|
163
|
+
|
|
164
|
+
if (!usage) {
|
|
165
|
+
return {
|
|
166
|
+
status: 404,
|
|
167
|
+
body: { error: "No usage data found for this app" },
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
status: 200,
|
|
173
|
+
headers: { "Content-Type": "application/json" },
|
|
174
|
+
body: usage,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Stats endpoint for aggregated metrics
|
|
180
|
+
* GET /stats?appId=<appId>
|
|
181
|
+
*/
|
|
182
|
+
export async function statsEndpoint(
|
|
183
|
+
context: AzureContext,
|
|
184
|
+
req: AzureHttpRequest
|
|
185
|
+
): Promise<AzureHttpResponse> {
|
|
186
|
+
context.log("Stats requested");
|
|
187
|
+
|
|
188
|
+
const appId = req.query.appId;
|
|
189
|
+
|
|
190
|
+
if (!appId) {
|
|
191
|
+
return {
|
|
192
|
+
status: 400,
|
|
193
|
+
body: { error: "appId query parameter is required" },
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const usage = storage.usage.get(appId);
|
|
198
|
+
const syncs = storage.syncs.get(appId) || [];
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
status: 200,
|
|
202
|
+
headers: { "Content-Type": "application/json" },
|
|
203
|
+
body: {
|
|
204
|
+
appId,
|
|
205
|
+
totalSyncs: syncs.length,
|
|
206
|
+
usage: usage || null,
|
|
207
|
+
lastSync: syncs.length > 0 ? syncs[syncs.length - 1].timestamp : null,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Event forwarding endpoint
|
|
214
|
+
* POST /events
|
|
215
|
+
*/
|
|
216
|
+
export async function eventsEndpoint(
|
|
217
|
+
context: AzureContext,
|
|
218
|
+
req: AzureHttpRequest
|
|
219
|
+
): Promise<AzureHttpResponse> {
|
|
220
|
+
context.log("Event forwarding requested");
|
|
221
|
+
|
|
222
|
+
if (req.method !== "POST") {
|
|
223
|
+
return {
|
|
224
|
+
status: 405,
|
|
225
|
+
body: { error: "Method not allowed" },
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const { appId, events } = req.body as { appId: string; events: unknown[] };
|
|
230
|
+
|
|
231
|
+
if (!appId || !events) {
|
|
232
|
+
return {
|
|
233
|
+
status: 400,
|
|
234
|
+
body: { error: "Invalid event forwarding request" },
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
context.log(`Forwarding ${events.length} events for app ${appId}`);
|
|
239
|
+
|
|
240
|
+
// In production, publish to Azure Event Grid / Service Bus
|
|
241
|
+
// For now, just acknowledge receipt
|
|
242
|
+
return {
|
|
243
|
+
status: 200,
|
|
244
|
+
headers: { "Content-Type": "application/json" },
|
|
245
|
+
body: {
|
|
246
|
+
success: true,
|
|
247
|
+
forwarded: events.length,
|
|
248
|
+
timestamp: Date.now(),
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Schema registry endpoint
|
|
255
|
+
* GET /schema?appId=<appId>
|
|
256
|
+
* POST /schema (to register a schema)
|
|
257
|
+
*/
|
|
258
|
+
export async function schemaEndpoint(
|
|
259
|
+
context: AzureContext,
|
|
260
|
+
req: AzureHttpRequest
|
|
261
|
+
): Promise<AzureHttpResponse> {
|
|
262
|
+
context.log("Schema registry requested");
|
|
263
|
+
|
|
264
|
+
if (req.method === "POST") {
|
|
265
|
+
const { appId, schema } = req.body as { appId: string; schema: unknown };
|
|
266
|
+
|
|
267
|
+
if (!appId || !schema) {
|
|
268
|
+
return {
|
|
269
|
+
status: 400,
|
|
270
|
+
body: { error: "Invalid schema registration request" },
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
context.log(`Schema registered for app ${appId}`);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
status: 200,
|
|
278
|
+
headers: { "Content-Type": "application/json" },
|
|
279
|
+
body: {
|
|
280
|
+
success: true,
|
|
281
|
+
schemaId: `${appId}-${Date.now()}`,
|
|
282
|
+
timestamp: Date.now(),
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// GET request
|
|
288
|
+
const appId = req.query.appId;
|
|
289
|
+
|
|
290
|
+
if (!appId) {
|
|
291
|
+
return {
|
|
292
|
+
status: 400,
|
|
293
|
+
body: { error: "appId query parameter is required" },
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Return placeholder schema
|
|
298
|
+
return {
|
|
299
|
+
status: 200,
|
|
300
|
+
headers: { "Content-Type": "application/json" },
|
|
301
|
+
body: {
|
|
302
|
+
appId,
|
|
303
|
+
schema: null,
|
|
304
|
+
message: "Schema not found",
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0",
|
|
3
|
+
"logging": {
|
|
4
|
+
"applicationInsights": {
|
|
5
|
+
"samplingSettings": {
|
|
6
|
+
"isEnabled": true,
|
|
7
|
+
"maxTelemetryItemsPerSecond": 20
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"extensionBundle": {
|
|
12
|
+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
|
13
|
+
"version": "[4.*, 5.0.0)"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Sponsors API Client
|
|
3
|
+
*
|
|
4
|
+
* Client for checking GitHub Sponsors subscription status.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Subscription } from "./billing.js";
|
|
8
|
+
import { createSponsorSubscription, createFreeSubscription } from "./billing.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* GitHub Sponsors tier information
|
|
12
|
+
*/
|
|
13
|
+
export interface SponsorTier {
|
|
14
|
+
/**
|
|
15
|
+
* Tier ID
|
|
16
|
+
*/
|
|
17
|
+
id: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tier name
|
|
21
|
+
*/
|
|
22
|
+
name: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Monthly price in cents
|
|
26
|
+
*/
|
|
27
|
+
monthlyPriceInCents: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Tier description
|
|
31
|
+
*/
|
|
32
|
+
description?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether this is a one-time sponsorship
|
|
36
|
+
*/
|
|
37
|
+
isOneTime: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sponsorship information
|
|
42
|
+
*/
|
|
43
|
+
export interface Sponsorship {
|
|
44
|
+
/**
|
|
45
|
+
* Sponsor login
|
|
46
|
+
*/
|
|
47
|
+
sponsorLogin: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Sponsor ID
|
|
51
|
+
*/
|
|
52
|
+
sponsorId: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Tier information
|
|
56
|
+
*/
|
|
57
|
+
tier: SponsorTier;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creation date
|
|
61
|
+
*/
|
|
62
|
+
createdAt: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether sponsorship is active
|
|
66
|
+
*/
|
|
67
|
+
isActive: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* GitHub Sponsors API client
|
|
72
|
+
*/
|
|
73
|
+
export class GitHubSponsorsClient {
|
|
74
|
+
private token: string;
|
|
75
|
+
private accountLogin: string;
|
|
76
|
+
|
|
77
|
+
constructor(token: string, accountLogin: string) {
|
|
78
|
+
this.token = token;
|
|
79
|
+
this.accountLogin = accountLogin;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get current user's sponsorship of the Praxis account
|
|
84
|
+
*/
|
|
85
|
+
async getSponsorship(userLogin: string): Promise<Sponsorship | null> {
|
|
86
|
+
try {
|
|
87
|
+
const query = `
|
|
88
|
+
query($accountLogin: String!, $userLogin: String!) {
|
|
89
|
+
user(login: $userLogin) {
|
|
90
|
+
sponsorshipForViewerAsSponsor(activeOnly: true) {
|
|
91
|
+
tier {
|
|
92
|
+
id
|
|
93
|
+
name
|
|
94
|
+
monthlyPriceInCents
|
|
95
|
+
description
|
|
96
|
+
isOneTime
|
|
97
|
+
}
|
|
98
|
+
createdAt
|
|
99
|
+
isActive
|
|
100
|
+
}
|
|
101
|
+
sponsorshipsAsSponsor(first: 100, activeOnly: true) {
|
|
102
|
+
nodes {
|
|
103
|
+
sponsorable {
|
|
104
|
+
... on User {
|
|
105
|
+
login
|
|
106
|
+
}
|
|
107
|
+
... on Organization {
|
|
108
|
+
login
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
tier {
|
|
112
|
+
id
|
|
113
|
+
name
|
|
114
|
+
monthlyPriceInCents
|
|
115
|
+
description
|
|
116
|
+
isOneTime
|
|
117
|
+
}
|
|
118
|
+
createdAt
|
|
119
|
+
isActive
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
|
|
126
|
+
const response = await fetch("https://api.github.com/graphql", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: {
|
|
129
|
+
Authorization: `Bearer ${this.token}`,
|
|
130
|
+
"Content-Type": "application/json",
|
|
131
|
+
},
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
query,
|
|
134
|
+
variables: {
|
|
135
|
+
accountLogin: this.accountLogin,
|
|
136
|
+
userLogin,
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`GitHub API error: ${response.statusText}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const data = await response.json() as any;
|
|
146
|
+
|
|
147
|
+
if (data.errors) {
|
|
148
|
+
throw new Error(`GraphQL error: ${JSON.stringify(data.errors)}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check if user sponsors the Praxis account
|
|
152
|
+
const sponsorships = data.data?.user?.sponsorshipsAsSponsor?.nodes || [];
|
|
153
|
+
const praxisSponsorship = sponsorships.find(
|
|
154
|
+
(s: any) => s.sponsorable?.login === this.accountLogin
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (!praxisSponsorship) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
sponsorLogin: userLogin,
|
|
163
|
+
sponsorId: data.data.user.id,
|
|
164
|
+
tier: {
|
|
165
|
+
id: praxisSponsorship.tier.id,
|
|
166
|
+
name: praxisSponsorship.tier.name,
|
|
167
|
+
monthlyPriceInCents: praxisSponsorship.tier.monthlyPriceInCents,
|
|
168
|
+
description: praxisSponsorship.tier.description,
|
|
169
|
+
isOneTime: praxisSponsorship.tier.isOneTime,
|
|
170
|
+
},
|
|
171
|
+
createdAt: praxisSponsorship.createdAt,
|
|
172
|
+
isActive: praxisSponsorship.isActive,
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error("Failed to get sponsorship:", error);
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get subscription from sponsorship
|
|
182
|
+
*/
|
|
183
|
+
async getSubscription(userLogin: string): Promise<Subscription> {
|
|
184
|
+
const sponsorship = await this.getSponsorship(userLogin);
|
|
185
|
+
|
|
186
|
+
if (!sponsorship || !sponsorship.isActive) {
|
|
187
|
+
return createFreeSubscription();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return createSponsorSubscription(
|
|
191
|
+
sponsorship.tier.name,
|
|
192
|
+
sponsorship.tier.monthlyPriceInCents
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Check if user is a sponsor
|
|
198
|
+
*/
|
|
199
|
+
async isSponsor(userLogin: string): Promise<boolean> {
|
|
200
|
+
const sponsorship = await this.getSponsorship(userLogin);
|
|
201
|
+
return sponsorship !== null && sponsorship.isActive;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Create a GitHub Sponsors client
|
|
207
|
+
*/
|
|
208
|
+
export function createSponsorsClient(
|
|
209
|
+
token: string,
|
|
210
|
+
accountLogin: string = "plures"
|
|
211
|
+
): GitHubSponsorsClient {
|
|
212
|
+
return new GitHubSponsorsClient(token, accountLogin);
|
|
213
|
+
}
|