@mmapp/react-compiler 0.1.0-alpha.1
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/README.md +107 -0
- package/compile-blueprint-chat.mjs +99 -0
- package/compile-blueprint-glass-console.mjs +98 -0
- package/compile-chat-defs.mjs +92 -0
- package/dist/babel/index.d.mts +3 -0
- package/dist/babel/index.d.ts +3 -0
- package/dist/babel/index.js +4851 -0
- package/dist/babel/index.mjs +7 -0
- package/dist/chunk-26U577GB.mjs +3465 -0
- package/dist/chunk-2FBDFAX6.mjs +2362 -0
- package/dist/chunk-2L4QSMXG.mjs +175 -0
- package/dist/chunk-2REDFOER.mjs +931 -0
- package/dist/chunk-46YKSHQR.mjs +175 -0
- package/dist/chunk-4XHK6FWL.mjs +2058 -0
- package/dist/chunk-5M7DKKBC.mjs +215 -0
- package/dist/chunk-5VNJ7C6N.mjs +154 -0
- package/dist/chunk-6CQOAAMV.mjs +1803 -0
- package/dist/chunk-6SEVAAVT.mjs +3516 -0
- package/dist/chunk-6YLR5ZDA.mjs +2829 -0
- package/dist/chunk-AOGY2GK6.mjs +3292 -0
- package/dist/chunk-AXXUXRNA.mjs +1434 -0
- package/dist/chunk-CHLVKMQW.mjs +175 -0
- package/dist/chunk-CKGOZAB7.mjs +939 -0
- package/dist/chunk-D34RAZUX.mjs +2223 -0
- package/dist/chunk-EQGA6A6D.mjs +121 -0
- package/dist/chunk-EY2CSXYA.mjs +822 -0
- package/dist/chunk-FIQ65CDR.mjs +925 -0
- package/dist/chunk-FOZXJFAR.mjs +186 -0
- package/dist/chunk-FX6URXWN.mjs +186 -0
- package/dist/chunk-G7SMOWOL.mjs +828 -0
- package/dist/chunk-GGB4G5YY.mjs +175 -0
- package/dist/chunk-HLRGCCIL.mjs +4839 -0
- package/dist/chunk-HOIUP6IF.mjs +690 -0
- package/dist/chunk-I3AU7GRD.mjs +120 -0
- package/dist/chunk-ILFGMUVD.mjs +1933 -0
- package/dist/chunk-IPTX5MJU.mjs +3223 -0
- package/dist/chunk-ITGUSH2Z.mjs +2783 -0
- package/dist/chunk-IXHBCAMF.mjs +3306 -0
- package/dist/chunk-J7TWJ3TM.mjs +2784 -0
- package/dist/chunk-JDPLDGVF.mjs +4810 -0
- package/dist/chunk-K53XP2DL.mjs +148 -0
- package/dist/chunk-K5HX2SVL.mjs +1902 -0
- package/dist/chunk-KFGYOOVS.mjs +214 -0
- package/dist/chunk-KFVVOS5N.mjs +925 -0
- package/dist/chunk-L2OZ4CDV.mjs +113 -0
- package/dist/chunk-MIZV3TAN.mjs +3293 -0
- package/dist/chunk-NKKLQE5V.mjs +148 -0
- package/dist/chunk-NOW23XFZ.mjs +186 -0
- package/dist/chunk-NRXQKQ74.mjs +148 -0
- package/dist/chunk-OWI6XWCD.mjs +3375 -0
- package/dist/chunk-PRUMNNDI.mjs +3192 -0
- package/dist/chunk-QTBD5B3F.mjs +148 -0
- package/dist/chunk-SKSDPPNT.mjs +3788 -0
- package/dist/chunk-SP2YUS33.mjs +186 -0
- package/dist/chunk-SU4E6E7B.mjs +3153 -0
- package/dist/chunk-SYUUKW5A.mjs +3379 -0
- package/dist/chunk-UL2XZEMA.mjs +3128 -0
- package/dist/chunk-XMWUHQVV.mjs +939 -0
- package/dist/chunk-XZNEDRGN.mjs +3876 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/chunk-YFS6JMYO.mjs +3342 -0
- package/dist/chunk-Z6AIQ4KL.mjs +113 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +11585 -0
- package/dist/cli/index.mjs +701 -0
- package/dist/codemod/cli.d.mts +1 -0
- package/dist/codemod/cli.d.ts +1 -0
- package/dist/codemod/cli.js +1104 -0
- package/dist/codemod/cli.mjs +157 -0
- package/dist/codemod/index.d.mts +148 -0
- package/dist/codemod/index.d.ts +148 -0
- package/dist/codemod/index.js +981 -0
- package/dist/codemod/index.mjs +25 -0
- package/dist/dev-server-Bs_sz2DG.d.mts +111 -0
- package/dist/dev-server-Bs_sz2DG.d.ts +111 -0
- package/dist/dev-server-CjoufJ-u.d.mts +109 -0
- package/dist/dev-server-CjoufJ-u.d.ts +109 -0
- package/dist/dev-server.d.mts +3 -0
- package/dist/dev-server.d.ts +3 -0
- package/dist/dev-server.js +7603 -0
- package/dist/dev-server.mjs +11 -0
- package/dist/envelope-DD7v0v6E.d.mts +265 -0
- package/dist/envelope-DD7v0v6E.d.ts +265 -0
- package/dist/envelope-vCVjrHlo.d.mts +265 -0
- package/dist/envelope-vCVjrHlo.d.ts +265 -0
- package/dist/envelope.d.mts +2 -0
- package/dist/envelope.d.ts +2 -0
- package/dist/envelope.js +5184 -0
- package/dist/envelope.mjs +9 -0
- package/dist/index-B5gSgvnd.d.mts +44 -0
- package/dist/index-B5gSgvnd.d.ts +44 -0
- package/dist/index-Bs0MnR54.d.mts +103 -0
- package/dist/index-Bs0MnR54.d.ts +103 -0
- package/dist/index-DR0nNc_f.d.mts +101 -0
- package/dist/index-DR0nNc_f.d.ts +101 -0
- package/dist/index-revho_gS.d.mts +104 -0
- package/dist/index-revho_gS.d.ts +104 -0
- package/dist/index.d.mts +1099 -0
- package/dist/index.d.ts +1099 -0
- package/dist/index.js +10162 -0
- package/dist/index.mjs +372 -0
- package/dist/init-IXEE2RCF.mjs +340 -0
- package/dist/project-compiler-EGJUTAJU.mjs +10 -0
- package/dist/project-compiler-VFR6CSDX.mjs +10 -0
- package/dist/project-decompiler-5GY2KSG4.mjs +7 -0
- package/dist/pull-A2QUHW4K.mjs +109 -0
- package/dist/pull-JBEQWVPE.mjs +109 -0
- package/dist/testing/index.d.mts +211 -0
- package/dist/testing/index.d.ts +211 -0
- package/dist/testing/index.js +5106 -0
- package/dist/testing/index.mjs +247 -0
- package/dist/vite/index.d.mts +59 -0
- package/dist/vite/index.d.ts +59 -0
- package/dist/vite/index.js +5023 -0
- package/dist/vite/index.mjs +8 -0
- package/examples/README.md +72 -0
- package/examples/authentication/main.workflow.tsx +139 -0
- package/examples/authentication/mm.config.ts +22 -0
- package/examples/authentication/models/auth.ts +45 -0
- package/examples/authentication/pages/LoginPage.tsx +79 -0
- package/examples/authentication/pages/SignupPage.tsx +87 -0
- package/examples/counter.workflow.tsx +65 -0
- package/examples/dashboard.workflow.tsx +419 -0
- package/examples/invoice-approval/actions/invoice.server.ts +72 -0
- package/examples/invoice-approval/main.workflow.tsx +168 -0
- package/examples/invoice-approval/mm.config.ts +18 -0
- package/examples/invoice-approval/models/invoice.ts +46 -0
- package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +175 -0
- package/examples/invoice-approval/pages/InvoiceFormPage.tsx +198 -0
- package/examples/invoice-approval/pages/InvoiceListPage.tsx +141 -0
- package/examples/todo-app.workflow.tsx +131 -0
- package/examples/uber-app/actions/matching.server.ts +177 -0
- package/examples/uber-app/actions/notifications.server.ts +176 -0
- package/examples/uber-app/actions/payments.server.ts +184 -0
- package/examples/uber-app/actions/pricing.server.ts +176 -0
- package/examples/uber-app/app/admin/analytics.tsx +102 -0
- package/examples/uber-app/app/admin/fleet.tsx +102 -0
- package/examples/uber-app/app/admin/surge-pricing.tsx +95 -0
- package/examples/uber-app/app/driver/dashboard.tsx +87 -0
- package/examples/uber-app/app/driver/earnings.tsx +101 -0
- package/examples/uber-app/app/driver/navigation.tsx +94 -0
- package/examples/uber-app/app/driver/ride-acceptance.tsx +103 -0
- package/examples/uber-app/app/rider/home.tsx +109 -0
- package/examples/uber-app/app/rider/payment-methods.tsx +134 -0
- package/examples/uber-app/app/rider/ride-history.tsx +90 -0
- package/examples/uber-app/app/rider/ride-tracking.tsx +108 -0
- package/examples/uber-app/components/DriverCard.tsx +176 -0
- package/examples/uber-app/components/MapView.tsx +216 -0
- package/examples/uber-app/components/RatingStars.tsx +227 -0
- package/examples/uber-app/components/RideCard.tsx +167 -0
- package/examples/uber-app/mm.config.ts +30 -0
- package/examples/uber-app/models/location.model.ts +70 -0
- package/examples/uber-app/models/payment.model.ts +87 -0
- package/examples/uber-app/models/rating.model.ts +54 -0
- package/examples/uber-app/models/ride.model.ts +118 -0
- package/examples/uber-app/models/user.model.ts +66 -0
- package/examples/uber-app/models/vehicle.model.ts +63 -0
- package/examples/uber-app/tests/payment.test.tsx +129 -0
- package/examples/uber-app/tests/ride-flow.test.tsx +123 -0
- package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +205 -0
- package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +227 -0
- package/examples/uber-app/workflows/payment-processing.workflow.tsx +223 -0
- package/examples/uber-app/workflows/ride-request.workflow.tsx +194 -0
- package/package.json +77 -0
- package/package.json.backup +86 -0
- package/scripts/decompile.ts +226 -0
- package/scripts/seed-auth.ts +267 -0
- package/scripts/seed-uber.ts +248 -0
- package/scripts/validate-uber.ts +119 -0
- package/seed-blueprint-chat.mjs +444 -0
- package/seed-blueprint-glass-console.mjs +445 -0
- package/seed-compiled.mjs +318 -0
- package/src/RoundTripValidator.ts +400 -0
- package/src/__tests__/atom-rendering-coverage.test.ts +680 -0
- package/src/__tests__/auth-module-compilation.test.ts +247 -0
- package/src/__tests__/auth-template-compilation.test.ts +589 -0
- package/src/__tests__/change-extractor.test.ts +142 -0
- package/src/__tests__/cli-pull.test.ts +73 -0
- package/src/__tests__/cli-test.test.ts +72 -0
- package/src/__tests__/component-extractor.test.ts +331 -0
- package/src/__tests__/context-extractor.test.ts +145 -0
- package/src/__tests__/decompiler.test.ts +718 -0
- package/src/__tests__/define-blueprint.test.ts +133 -0
- package/src/__tests__/definition-validator.test.ts +519 -0
- package/src/__tests__/during-extractor.test.ts +152 -0
- package/src/__tests__/effect-extractor.test.ts +107 -0
- package/src/__tests__/event-emission.test.ts +127 -0
- package/src/__tests__/examples.test.ts +236 -0
- package/src/__tests__/full-blueprint-coverage.test.ts +1221 -0
- package/src/__tests__/golden-suite.test.ts +403 -0
- package/src/__tests__/grammar-island-extractor.test.ts +289 -0
- package/src/__tests__/instance-key.test.ts +82 -0
- package/src/__tests__/ir-migration.test.ts +255 -0
- package/src/__tests__/lock-file.test.ts +117 -0
- package/src/__tests__/model-extractor.test.ts +195 -0
- package/src/__tests__/model-field-acl.test.ts +237 -0
- package/src/__tests__/model-hooks.test.ts +130 -0
- package/src/__tests__/model-ref-resolution.test.ts +268 -0
- package/src/__tests__/model-roundtrip.test.ts +502 -0
- package/src/__tests__/model-runtime.test.ts +112 -0
- package/src/__tests__/model-transitions.test.ts +183 -0
- package/src/__tests__/nrt-action-trace.test.ts +391 -0
- package/src/__tests__/pipeline-hardening.test.ts +413 -0
- package/src/__tests__/project-compiler.test.ts +546 -0
- package/src/__tests__/project-decompiler.test.ts +343 -0
- package/src/__tests__/query-compilation.test.ts +145 -0
- package/src/__tests__/round-trip/PLAN.md +158 -0
- package/src/__tests__/round-trip/README.md +52 -0
- package/src/__tests__/round-trip/RESULTS.md +86 -0
- package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +55 -0
- package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +11 -0
- package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +54 -0
- package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +79 -0
- package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +12 -0
- package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +50 -0
- package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +25 -0
- package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +11 -0
- package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +32 -0
- package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +79 -0
- package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +10 -0
- package/src/__tests__/round-trip/round-trip.test.ts +2598 -0
- package/src/__tests__/round-trip-ir.test.ts +300 -0
- package/src/__tests__/round-trip.test.ts +1212 -0
- package/src/__tests__/route-merging.test.ts +372 -0
- package/src/__tests__/router-composition.test.ts +489 -0
- package/src/__tests__/router-extractor.test.ts +176 -0
- package/src/__tests__/server-action-extractor.test.ts +128 -0
- package/src/__tests__/smart-type-inference.test.ts +365 -0
- package/src/__tests__/source-envelope.test.ts +284 -0
- package/src/__tests__/source-fidelity.test.ts +516 -0
- package/src/__tests__/state-extractor.test.ts +115 -0
- package/src/__tests__/strict-mode.test.ts +227 -0
- package/src/__tests__/transition-effect-extractor.test.ts +119 -0
- package/src/__tests__/transition-extractor.test.ts +68 -0
- package/src/__tests__/ts-to-expression.test.ts +462 -0
- package/src/__tests__/type-generator.test.ts +201 -0
- package/src/__tests__/uber-validation.test.ts +502 -0
- package/src/action-compiler.ts +361 -0
- package/src/babel/emitters/experience-transform.ts +199 -0
- package/src/babel/emitters/ir-to-tsx-emitter.ts +110 -0
- package/src/babel/emitters/pure-form-emitter.ts +1023 -0
- package/src/babel/emitters/runtime-glue-emitter.ts +39 -0
- package/src/babel/extractors/change-extractor.ts +199 -0
- package/src/babel/extractors/component-extractor.ts +907 -0
- package/src/babel/extractors/computed-extractor.ts +262 -0
- package/src/babel/extractors/context-extractor.ts +277 -0
- package/src/babel/extractors/during-extractor.ts +295 -0
- package/src/babel/extractors/effect-extractor.ts +340 -0
- package/src/babel/extractors/event-extractor.ts +235 -0
- package/src/babel/extractors/grammar-island-extractor.ts +302 -0
- package/src/babel/extractors/model-extractor.ts +1018 -0
- package/src/babel/extractors/router-extractor.ts +303 -0
- package/src/babel/extractors/server-action-extractor.ts +173 -0
- package/src/babel/extractors/server-action-hook-extractor.ts +72 -0
- package/src/babel/extractors/server-state-extractor.ts +88 -0
- package/src/babel/extractors/state-extractor.ts +214 -0
- package/src/babel/extractors/transition-effect-extractor.ts +176 -0
- package/src/babel/extractors/transition-extractor.ts +143 -0
- package/src/babel/index.ts +24 -0
- package/src/babel/transpilers/ts-to-expression.ts +674 -0
- package/src/babel/visitor.ts +807 -0
- package/src/cli/auth.ts +255 -0
- package/src/cli/build.ts +288 -0
- package/src/cli/deploy.ts +206 -0
- package/src/cli/index.ts +328 -0
- package/src/cli/init.ts +388 -0
- package/src/cli/installer.ts +261 -0
- package/src/cli/lock-file.ts +94 -0
- package/src/cli/mmrc.ts +22 -0
- package/src/cli/pull.ts +172 -0
- package/src/cli/registry-client.ts +175 -0
- package/src/cli/test.ts +397 -0
- package/src/cli/type-generator.ts +243 -0
- package/src/codemod/__tests__/forward.test.ts +239 -0
- package/src/codemod/__tests__/reverse.test.ts +145 -0
- package/src/codemod/__tests__/round-trip.test.ts +137 -0
- package/src/codemod/annotation.ts +97 -0
- package/src/codemod/classify.ts +197 -0
- package/src/codemod/cli.ts +207 -0
- package/src/codemod/control-flow.ts +409 -0
- package/src/codemod/forward.ts +244 -0
- package/src/codemod/import-manager.ts +171 -0
- package/src/codemod/index.ts +120 -0
- package/src/codemod/reverse.ts +197 -0
- package/src/codemod/rules.ts +174 -0
- package/src/codemod/state-transform.ts +126 -0
- package/src/decompiler/ast-builder.ts +538 -0
- package/src/decompiler/config-generator.ts +151 -0
- package/src/decompiler/index.ts +315 -0
- package/src/decompiler/project-decompiler.ts +1776 -0
- package/src/decompiler/project.ts +862 -0
- package/src/decompiler/split-strategy.ts +140 -0
- package/src/decompiler/state-emitter.ts +1053 -0
- package/src/decompiler/sx-emitter.ts +318 -0
- package/src/decompiler/workspace-hydrator.ts +189 -0
- package/src/dev-server.ts +238 -0
- package/src/envelope/fs-tree.ts +217 -0
- package/src/envelope/source-envelope.ts +264 -0
- package/src/envelope.ts +315 -0
- package/src/incremental-compiler.ts +401 -0
- package/src/index.ts +99 -0
- package/src/model-compiler.ts +277 -0
- package/src/project-compiler.ts +1629 -0
- package/src/route-extractor.ts +333 -0
- package/src/testing/index.ts +32 -0
- package/src/testing/snapshot.ts +252 -0
- package/src/testing/test-utils.ts +226 -0
- package/src/types.ts +68 -0
- package/src/vite/index.ts +288 -0
- package/test-compile.mjs +142 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +23 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @workflow slug="uber-rideshare"
|
|
3
|
+
* @description Driver matching algorithm — finds optimal driver based on proximity, rating, and vehicle type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TransitionContext, ActionResult } from '@mindmatrix/react';
|
|
7
|
+
|
|
8
|
+
/** Haversine distance in kilometers between two lat/lng points */
|
|
9
|
+
function haversineKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
|
|
10
|
+
const R = 6371;
|
|
11
|
+
const dLat = ((lat2 - lat1) * Math.PI) / 180;
|
|
12
|
+
const dLon = ((lon2 - lon1) * Math.PI) / 180;
|
|
13
|
+
const a =
|
|
14
|
+
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
15
|
+
Math.cos((lat1 * Math.PI) / 180) *
|
|
16
|
+
Math.cos((lat2 * Math.PI) / 180) *
|
|
17
|
+
Math.sin(dLon / 2) *
|
|
18
|
+
Math.sin(dLon / 2);
|
|
19
|
+
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Score a driver for matching — higher is better */
|
|
23
|
+
function scoreDriver(
|
|
24
|
+
driver: Record<string, unknown>,
|
|
25
|
+
pickupLat: number,
|
|
26
|
+
pickupLng: number,
|
|
27
|
+
requestedType: string
|
|
28
|
+
): number {
|
|
29
|
+
const dist = haversineKm(
|
|
30
|
+
pickupLat,
|
|
31
|
+
pickupLng,
|
|
32
|
+
driver.currentLatitude as number,
|
|
33
|
+
driver.currentLongitude as number
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Distance score: closer = higher (max 50 points, 0 at 15km+)
|
|
37
|
+
const distanceScore = Math.max(0, 50 - dist * (50 / 15));
|
|
38
|
+
|
|
39
|
+
// Rating score: 0-25 points
|
|
40
|
+
const ratingScore = ((driver.driverRating as number) / 5) * 25;
|
|
41
|
+
|
|
42
|
+
// Acceptance rate score: 0-15 points
|
|
43
|
+
const acceptScore = ((driver.acceptanceRate as number) / 100) * 15;
|
|
44
|
+
|
|
45
|
+
// Vehicle type match bonus: 10 points
|
|
46
|
+
const typeBonus = driver.vehicleType === requestedType ? 10 : 0;
|
|
47
|
+
|
|
48
|
+
return distanceScore + ratingScore + acceptScore + typeBonus;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Find and match the best available driver for a ride request */
|
|
52
|
+
export async function findBestDriver(ctx: TransitionContext): Promise<ActionResult> {
|
|
53
|
+
const { instance, env } = ctx;
|
|
54
|
+
const { originLatitude, originLongitude, vehicleType } = instance.fields as Record<string, unknown>;
|
|
55
|
+
|
|
56
|
+
// Query nearby online drivers with matching vehicle type
|
|
57
|
+
const drivers = await env.query('uber-user', {
|
|
58
|
+
filter: {
|
|
59
|
+
role: 'driver',
|
|
60
|
+
isOnline: true,
|
|
61
|
+
status: 'active',
|
|
62
|
+
},
|
|
63
|
+
limit: 50,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!drivers.data || drivers.data.length === 0) {
|
|
67
|
+
return { success: false, data: { reason: 'no_drivers_available' } };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Score and rank all candidate drivers
|
|
71
|
+
const scored = drivers.data
|
|
72
|
+
.map((driver: Record<string, unknown>) => ({
|
|
73
|
+
driver,
|
|
74
|
+
score: scoreDriver(
|
|
75
|
+
driver,
|
|
76
|
+
originLatitude as number,
|
|
77
|
+
originLongitude as number,
|
|
78
|
+
vehicleType as string
|
|
79
|
+
),
|
|
80
|
+
distance: haversineKm(
|
|
81
|
+
originLatitude as number,
|
|
82
|
+
originLongitude as number,
|
|
83
|
+
driver.currentLatitude as number,
|
|
84
|
+
driver.currentLongitude as number
|
|
85
|
+
),
|
|
86
|
+
}))
|
|
87
|
+
.filter((d) => d.distance <= 15) // Max 15km radius
|
|
88
|
+
.sort((a, b) => b.score - a.score);
|
|
89
|
+
|
|
90
|
+
if (scored.length === 0) {
|
|
91
|
+
return { success: false, data: { reason: 'no_drivers_in_range' } };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const best = scored[0];
|
|
95
|
+
const etaMinutes = Math.round((best.distance / 30) * 60); // Assume 30km/h avg
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
data: {
|
|
100
|
+
driverId: best.driver.id,
|
|
101
|
+
driverName: best.driver.displayName,
|
|
102
|
+
vehicleId: best.driver.vehicleId,
|
|
103
|
+
driverLatitude: best.driver.currentLatitude,
|
|
104
|
+
driverLongitude: best.driver.currentLongitude,
|
|
105
|
+
driverRating: best.driver.driverRating,
|
|
106
|
+
etaMinutes,
|
|
107
|
+
matchScore: best.score,
|
|
108
|
+
candidatesEvaluated: scored.length,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Batch-match multiple ride requests to available drivers (for dispatch optimization) */
|
|
114
|
+
export async function batchMatchDrivers(ctx: TransitionContext): Promise<ActionResult> {
|
|
115
|
+
const { env } = ctx;
|
|
116
|
+
|
|
117
|
+
const pendingRides = await env.query('uber-ride', {
|
|
118
|
+
filter: { status: 'searching' },
|
|
119
|
+
limit: 100,
|
|
120
|
+
orderBy: 'requestedAt',
|
|
121
|
+
order: 'asc',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const matchedDriverIds = new Set<string>();
|
|
125
|
+
const results: Array<{ rideId: string; driverId: string; eta: number }> = [];
|
|
126
|
+
|
|
127
|
+
for (const ride of pendingRides.data) {
|
|
128
|
+
const drivers = await env.query('uber-user', {
|
|
129
|
+
filter: {
|
|
130
|
+
role: 'driver',
|
|
131
|
+
isOnline: true,
|
|
132
|
+
status: 'active',
|
|
133
|
+
},
|
|
134
|
+
limit: 50,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const available = drivers.data.filter(
|
|
138
|
+
(d: Record<string, unknown>) => !matchedDriverIds.has(d.id as string)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (available.length > 0) {
|
|
142
|
+
const scored = available
|
|
143
|
+
.map((driver: Record<string, unknown>) => ({
|
|
144
|
+
driver,
|
|
145
|
+
score: scoreDriver(
|
|
146
|
+
driver,
|
|
147
|
+
ride.originLatitude as number,
|
|
148
|
+
ride.originLongitude as number,
|
|
149
|
+
ride.vehicleType as string
|
|
150
|
+
),
|
|
151
|
+
distance: haversineKm(
|
|
152
|
+
ride.originLatitude as number,
|
|
153
|
+
ride.originLongitude as number,
|
|
154
|
+
driver.currentLatitude as number,
|
|
155
|
+
driver.currentLongitude as number
|
|
156
|
+
),
|
|
157
|
+
}))
|
|
158
|
+
.filter((d) => d.distance <= 15)
|
|
159
|
+
.sort((a, b) => b.score - a.score);
|
|
160
|
+
|
|
161
|
+
if (scored.length > 0) {
|
|
162
|
+
const best = scored[0];
|
|
163
|
+
matchedDriverIds.add(best.driver.id as string);
|
|
164
|
+
results.push({
|
|
165
|
+
rideId: ride.id as string,
|
|
166
|
+
driverId: best.driver.id as string,
|
|
167
|
+
eta: Math.round((best.distance / 30) * 60),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
data: { matched: results.length, total: pendingRides.data.length, results },
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @workflow slug="uber-rideshare"
|
|
3
|
+
* @description Push notification delivery for ride lifecycle events
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TransitionContext, ActionResult } from '@mindmatrix/react';
|
|
7
|
+
|
|
8
|
+
type NotificationChannel = 'push' | 'sms' | 'email' | 'in_app';
|
|
9
|
+
|
|
10
|
+
interface NotificationPayload {
|
|
11
|
+
userId: string;
|
|
12
|
+
title: string;
|
|
13
|
+
body: string;
|
|
14
|
+
channels: NotificationChannel[];
|
|
15
|
+
data?: Record<string, unknown>;
|
|
16
|
+
priority?: 'high' | 'normal' | 'low';
|
|
17
|
+
sound?: string;
|
|
18
|
+
badge?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Send notification via configured channels */
|
|
22
|
+
async function sendNotification(
|
|
23
|
+
env: TransitionContext['env'],
|
|
24
|
+
payload: NotificationPayload
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
// In production: calls FCM/APNs/Twilio/SendGrid
|
|
27
|
+
await env.emit('notification:sent', {
|
|
28
|
+
userId: payload.userId,
|
|
29
|
+
title: payload.title,
|
|
30
|
+
channels: payload.channels,
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Notify rider that a driver has been matched */
|
|
36
|
+
export async function notifyRideMatched(ctx: TransitionContext): Promise<ActionResult> {
|
|
37
|
+
const { instance, env } = ctx;
|
|
38
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
39
|
+
|
|
40
|
+
await sendNotification(env, {
|
|
41
|
+
userId: fields.riderId as string,
|
|
42
|
+
title: 'Driver Found!',
|
|
43
|
+
body: `${fields.driverName} is on the way in a ${fields.vehicleType}. ETA: ${fields.etaMinutes} min`,
|
|
44
|
+
channels: ['push', 'in_app'],
|
|
45
|
+
priority: 'high',
|
|
46
|
+
sound: 'ride_matched.mp3',
|
|
47
|
+
data: {
|
|
48
|
+
type: 'ride_matched',
|
|
49
|
+
rideId: instance.id,
|
|
50
|
+
driverName: fields.driverName,
|
|
51
|
+
etaMinutes: fields.etaMinutes,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return { success: true, data: { notified: 'rider', event: 'ride_matched' } };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Notify rider that driver has arrived at pickup */
|
|
59
|
+
export async function notifyDriverArrived(ctx: TransitionContext): Promise<ActionResult> {
|
|
60
|
+
const { instance, env } = ctx;
|
|
61
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
62
|
+
|
|
63
|
+
await sendNotification(env, {
|
|
64
|
+
userId: fields.riderId as string,
|
|
65
|
+
title: 'Your driver has arrived',
|
|
66
|
+
body: `${fields.driverName} is waiting at the pickup location. Share code: ${fields.shareCode}`,
|
|
67
|
+
channels: ['push', 'sms', 'in_app'],
|
|
68
|
+
priority: 'high',
|
|
69
|
+
sound: 'driver_arrived.mp3',
|
|
70
|
+
data: { type: 'driver_arrived', rideId: instance.id, shareCode: fields.shareCode },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return { success: true, data: { notified: 'rider', event: 'driver_arrived' } };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Notify driver of an incoming ride request */
|
|
77
|
+
export async function notifyNewRideRequest(ctx: TransitionContext): Promise<ActionResult> {
|
|
78
|
+
const { instance, env } = ctx;
|
|
79
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
80
|
+
|
|
81
|
+
await sendNotification(env, {
|
|
82
|
+
userId: fields.driverId as string,
|
|
83
|
+
title: 'New Ride Request',
|
|
84
|
+
body: `Pickup: ${fields.originAddress}. Fare: $${fields.estimatedFare}`,
|
|
85
|
+
channels: ['push', 'in_app'],
|
|
86
|
+
priority: 'high',
|
|
87
|
+
sound: 'new_request.mp3',
|
|
88
|
+
badge: 1,
|
|
89
|
+
data: {
|
|
90
|
+
type: 'ride_request',
|
|
91
|
+
rideId: instance.id,
|
|
92
|
+
originAddress: fields.originAddress,
|
|
93
|
+
estimatedFare: fields.estimatedFare,
|
|
94
|
+
vehicleType: fields.vehicleType,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return { success: true, data: { notified: 'driver', event: 'ride_request' } };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Notify rider when ride is completed with fare summary */
|
|
102
|
+
export async function notifyRideCompleted(ctx: TransitionContext): Promise<ActionResult> {
|
|
103
|
+
const { instance, env } = ctx;
|
|
104
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
105
|
+
|
|
106
|
+
// Notify rider
|
|
107
|
+
await sendNotification(env, {
|
|
108
|
+
userId: fields.riderId as string,
|
|
109
|
+
title: 'Ride Complete',
|
|
110
|
+
body: `You arrived at ${fields.destinationAddress}. Total: $${fields.actualFare}`,
|
|
111
|
+
channels: ['push', 'in_app'],
|
|
112
|
+
data: { type: 'ride_completed', rideId: instance.id, fare: fields.actualFare },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Notify driver
|
|
116
|
+
await sendNotification(env, {
|
|
117
|
+
userId: fields.driverId as string,
|
|
118
|
+
title: 'Trip Complete',
|
|
119
|
+
body: `Earnings: $${fields.actualFare}. Don't forget to rate your rider.`,
|
|
120
|
+
channels: ['push', 'in_app'],
|
|
121
|
+
data: { type: 'ride_completed', rideId: instance.id },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return { success: true, data: { notified: 'both', event: 'ride_completed' } };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Notify about ride cancellation */
|
|
128
|
+
export async function notifyCancellation(ctx: TransitionContext): Promise<ActionResult> {
|
|
129
|
+
const { instance, env } = ctx;
|
|
130
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
131
|
+
const cancelledBy = fields.cancelledBy as string;
|
|
132
|
+
|
|
133
|
+
const targetUserId = cancelledBy === 'rider'
|
|
134
|
+
? fields.driverId as string
|
|
135
|
+
: fields.riderId as string;
|
|
136
|
+
|
|
137
|
+
if (targetUserId) {
|
|
138
|
+
await sendNotification(env, {
|
|
139
|
+
userId: targetUserId,
|
|
140
|
+
title: 'Ride Cancelled',
|
|
141
|
+
body: `The ride was cancelled by the ${cancelledBy}. ${
|
|
142
|
+
(fields.cancellationFee as number) > 0
|
|
143
|
+
? `Cancellation fee: $${fields.cancellationFee}`
|
|
144
|
+
: 'No cancellation fee.'
|
|
145
|
+
}`,
|
|
146
|
+
channels: ['push', 'in_app'],
|
|
147
|
+
data: {
|
|
148
|
+
type: 'ride_cancelled',
|
|
149
|
+
rideId: instance.id,
|
|
150
|
+
cancelledBy,
|
|
151
|
+
cancellationFee: fields.cancellationFee,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { success: true, data: { notified: targetUserId ? 'counterparty' : 'none', event: 'cancellation' } };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Send safety alert to emergency contacts */
|
|
160
|
+
export async function sendSafetyAlert(ctx: TransitionContext): Promise<ActionResult> {
|
|
161
|
+
const { instance, env } = ctx;
|
|
162
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
163
|
+
|
|
164
|
+
await env.emit('safety:alert', {
|
|
165
|
+
rideId: instance.id,
|
|
166
|
+
riderId: fields.riderId,
|
|
167
|
+
driverId: fields.driverId,
|
|
168
|
+
location: {
|
|
169
|
+
latitude: fields.driverLatitude,
|
|
170
|
+
longitude: fields.driverLongitude,
|
|
171
|
+
},
|
|
172
|
+
timestamp: new Date().toISOString(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return { success: true, data: { event: 'safety_alert', emergencyContacted: true } };
|
|
176
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @workflow slug="uber-rideshare"
|
|
3
|
+
* @description Payment processing — authorization, capture, settlement, refund, and payout
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TransitionContext, ActionResult } from '@mindmatrix/react';
|
|
7
|
+
|
|
8
|
+
/** Authorize a payment hold before ride starts */
|
|
9
|
+
export async function authorizePayment(ctx: TransitionContext): Promise<ActionResult> {
|
|
10
|
+
const { instance, env } = ctx;
|
|
11
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
// Pre-authorize for estimated fare + 20% buffer
|
|
14
|
+
const estimatedFare = fields.estimatedFare as number;
|
|
15
|
+
const authAmount = Math.round(estimatedFare * 1.2 * 100) / 100;
|
|
16
|
+
|
|
17
|
+
// Mock processor call — in production this calls Stripe/Adyen
|
|
18
|
+
const processorTransactionId = `txn_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
19
|
+
const authorizationCode = `auth_${Math.random().toString(36).slice(2, 10)}`;
|
|
20
|
+
|
|
21
|
+
await env.emit('payment:authorized', {
|
|
22
|
+
rideId: fields.rideId,
|
|
23
|
+
amount: authAmount,
|
|
24
|
+
processorTransactionId,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
data: {
|
|
30
|
+
status: 'authorized',
|
|
31
|
+
processorTransactionId,
|
|
32
|
+
authorizationCode,
|
|
33
|
+
totalAmount: authAmount,
|
|
34
|
+
authorizedAt: new Date().toISOString(),
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Capture the actual fare after ride completion */
|
|
40
|
+
export async function capturePayment(ctx: TransitionContext): Promise<ActionResult> {
|
|
41
|
+
const { instance, env } = ctx;
|
|
42
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
43
|
+
|
|
44
|
+
const actualFare = fields.actualFare as number;
|
|
45
|
+
const tipAmount = (fields.tipAmount as number) || 0;
|
|
46
|
+
const totalCapture = actualFare + tipAmount;
|
|
47
|
+
|
|
48
|
+
// Calculate tax
|
|
49
|
+
const taxRate = 8.875;
|
|
50
|
+
const taxAmount = Math.round(totalCapture * (taxRate / 100) * 100) / 100;
|
|
51
|
+
|
|
52
|
+
// Platform split: 25% of fare (not tip)
|
|
53
|
+
const platformFeePercent = 25;
|
|
54
|
+
const platformFee = Math.round(actualFare * (platformFeePercent / 100) * 100) / 100;
|
|
55
|
+
const driverPayout = Math.round((actualFare - platformFee + tipAmount) * 100) / 100;
|
|
56
|
+
|
|
57
|
+
// Generate receipt
|
|
58
|
+
const invoiceNumber = `INV-${new Date().getFullYear()}-${String(Date.now()).slice(-8)}`;
|
|
59
|
+
const receiptUrl = `/receipts/${invoiceNumber}`;
|
|
60
|
+
|
|
61
|
+
await env.emit('payment:captured', {
|
|
62
|
+
rideId: fields.rideId,
|
|
63
|
+
amount: totalCapture,
|
|
64
|
+
driverPayout,
|
|
65
|
+
platformFee,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
data: {
|
|
71
|
+
status: 'captured',
|
|
72
|
+
totalAmount: Math.round(totalCapture * 100) / 100,
|
|
73
|
+
taxAmount,
|
|
74
|
+
taxRate,
|
|
75
|
+
platformFee,
|
|
76
|
+
platformFeePercent,
|
|
77
|
+
driverPayout,
|
|
78
|
+
capturedAt: new Date().toISOString(),
|
|
79
|
+
invoiceNumber,
|
|
80
|
+
receiptUrl,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Settle payment — transfer funds to driver account */
|
|
86
|
+
export async function settlePayment(ctx: TransitionContext): Promise<ActionResult> {
|
|
87
|
+
const { instance, env } = ctx;
|
|
88
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
89
|
+
|
|
90
|
+
const driverPayout = fields.driverPayout as number;
|
|
91
|
+
const driverId = fields.driverId as string;
|
|
92
|
+
const payoutId = `po_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
93
|
+
|
|
94
|
+
// Credit driver's earnings balance
|
|
95
|
+
await env.update('uber-user', driverId, {
|
|
96
|
+
earningsBalance: { $increment: driverPayout },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await env.emit('payment:settled', {
|
|
100
|
+
rideId: fields.rideId,
|
|
101
|
+
driverId,
|
|
102
|
+
payoutId,
|
|
103
|
+
amount: driverPayout,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
data: {
|
|
109
|
+
status: 'settled',
|
|
110
|
+
payoutId,
|
|
111
|
+
payoutStatus: 'completed',
|
|
112
|
+
settledAt: new Date().toISOString(),
|
|
113
|
+
payoutCompletedAt: new Date().toISOString(),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Process a refund — full or partial */
|
|
119
|
+
export async function processRefund(ctx: TransitionContext): Promise<ActionResult> {
|
|
120
|
+
const { instance, env } = ctx;
|
|
121
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
122
|
+
|
|
123
|
+
const refundAmount = fields.refundAmount as number;
|
|
124
|
+
const totalAmount = fields.totalAmount as number;
|
|
125
|
+
const isPartial = refundAmount < totalAmount;
|
|
126
|
+
|
|
127
|
+
// Reverse platform fee proportionally
|
|
128
|
+
const refundRatio = refundAmount / totalAmount;
|
|
129
|
+
const platformFeeRefund = Math.round((fields.platformFee as number) * refundRatio * 100) / 100;
|
|
130
|
+
const driverPayoutDeduction = Math.round((fields.driverPayout as number) * refundRatio * 100) / 100;
|
|
131
|
+
|
|
132
|
+
// Debit driver's earnings balance
|
|
133
|
+
const driverId = fields.driverId as string;
|
|
134
|
+
await env.update('uber-user', driverId, {
|
|
135
|
+
earningsBalance: { $increment: -driverPayoutDeduction },
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Credit rider's wallet
|
|
139
|
+
const riderId = fields.riderId as string;
|
|
140
|
+
await env.update('uber-user', riderId, {
|
|
141
|
+
walletBalance: { $increment: refundAmount },
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await env.emit('payment:refunded', {
|
|
145
|
+
rideId: fields.rideId,
|
|
146
|
+
amount: refundAmount,
|
|
147
|
+
isPartial,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
data: {
|
|
153
|
+
status: isPartial ? 'partially_refunded' : 'refunded',
|
|
154
|
+
refundAmount,
|
|
155
|
+
refundedAt: new Date().toISOString(),
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Process driver payout to external bank account */
|
|
161
|
+
export async function processDriverPayout(ctx: TransitionContext): Promise<ActionResult> {
|
|
162
|
+
const { instance } = ctx;
|
|
163
|
+
const fields = instance.fields as Record<string, unknown>;
|
|
164
|
+
|
|
165
|
+
const driverId = fields.driverId as string;
|
|
166
|
+
const amount = fields.earningsBalance as number;
|
|
167
|
+
|
|
168
|
+
if (amount < 25) {
|
|
169
|
+
return { success: false, data: { reason: 'minimum_payout_not_met', minimum: 25, current: amount } };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const payoutId = `payout_${Date.now()}`;
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
success: true,
|
|
176
|
+
data: {
|
|
177
|
+
payoutId,
|
|
178
|
+
amount,
|
|
179
|
+
driverId,
|
|
180
|
+
payoutStatus: 'processing',
|
|
181
|
+
payoutScheduledAt: new Date().toISOString(),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|