Skip to content

Commit cb9e4b7

Browse files
committed
chore: reliable api for communicating from lp to phoenix
1 parent 4997a16 commit cb9e4b7

File tree

3 files changed

+128
-35
lines changed

3 files changed

+128
-35
lines changed

README.md

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,8 @@ designed to make coding as simple and fun as playing a video game.
4040
4. Uncompromised local development experience.
4141
5. Support for pluggable remote back-ends.
4242
6. Phoenix core will work from a static web server.
43-
7. Source code in release builds will always be auditable and readable from dev-tools.
44-
45-
AGPL/Libre license guards your right to audit and change code that handles your data.
46-
Phoenix usually loads up in under one second and loading it faster at the expense of making it hard
47-
to read and develop is a noop. We prioritize simplicity and eaze of development.
43+
7. Compile step less build process. Code changes in phoenix do not need to be recompiled for most cases for development.
44+
8. Prioritize simplicity and ease of development.
4845

4946
## Contributing/ Feedback
5047
* [Get in touch with our community](https://github.com/phcode-dev/phoenix/discussions).
@@ -111,10 +108,10 @@ It has much better debug UX and fixing it directly in the browser will almost ce
111108
2. Run the unit tests using format: `npm run test<*>Dist`. Eg. `npm run testChromiumDist`.
112109
3. Run the integration tests using the format: `npx cross-env TEST_ENV=<integration suite name> npm run test<*>Dist`. Eg. `npx cross-env TEST_ENV=mainview npm run testChromiumDist`.
113110

114-
#### To run tests against these stacks go to the following url:
115-
* dev: https://dev.phcode.dev/test/SpecRunner.html
116-
* staging: https://staging.phcode.dev/test/SpecRunner.html
117-
* prod: https://phcode.dev/test/SpecRunner.html
111+
#### To run tests against these dev/staging/prod in the browser
112+
1. Build the release using `npm run release:<stage>`. Eg: `npm run release:dev`
113+
2. Run `npm run serve` to start the server.
114+
3. Go to `http://localhost:8000/dist-test/test/SpecRunner.html` in the browser to run tests that was built for the stage above.
118115

119116
## Browsing the virtual file system
120117
To view/edit the files in the browser virtual file system in Phoenix:
@@ -123,39 +120,16 @@ To view/edit the files in the browser virtual file system in Phoenix:
123120
## Clean and reset builds
124121
* clean builds only: `npm run clean`
125122

126-
## Previewing changes in dev and staging
127-
One a pull request is merged, it will be automatically deployed to dev.phcode.dev . To view the changes:
128-
1. goto https://dev.phcode.dev/devEnable.html and click `enable dev.phcode.dev` . only needs to be done once.
129-
2. goto https://dev.phcode.dev to preview your changes. If it is a recent change, you might need to wait for
130-
up to 15 minutes before the changes are deployed to the dev stage. Reload page a few times to get the latest
131-
dev build and reset cached content.
132-
133-
The process is the same for `staging.phcode.dev`. Builds that are verified in development will be pushed
134-
periodically to staging. To view staging:
135-
1. goto https://staging.phcode.dev/devEnable.html and click `enable staging.phcode.dev` . only needs to be done once.
136-
2. goto https://staging.phcode.dev to preview your changes. If it is a recent change, you might need to wait for
137-
up to 15 minutes before the changes are deployed to the dev stage. Reload page a few times to get the latest
138-
dev build and reset cached content.
139-
140-
## Deployment to phcode.dev
141-
* All changes pushed to the main branch are automatically published to https://dev.phcode.dev
142-
* To publish the changes to https://staging.phcode.dev , push changes to the `staging` branch in this repo with a pull request.
143-
* Once the changes are validated and tested, trigger a prod deployment by pushing to the `prod` branch.
144-
145123
## Acknowledgements
146124
* Phoenix is based on the Brackets code editor by Adobe. Find out more on [Adobe Brackets here](https://github.com/adobe/brackets/).
147125
* Our main code editor library https://codemirror.net/
148126
* Inspired by previous work from the [Mozilla Thimble](https://github.com/mozilla/thimble.mozilla.org) project to port brackets to the web. https://github.com/mozilla/brackets
149127
* In browser server based on [nohost](https://github.com/humphd/nohost) by https://github.com/humphd/
150128

151-
152129
## License
153-
Discussion: https://github.com/phcode-dev/phoenix/issues/184
154-
155130
GNU AGPL-3.0 License
156131

157132
Copyright (c) 2021 - present Core.ai
158-
159133
Based on Backets, Copyright (c) 2012 Adobe Systems Incorporated and the brackets.io community
160134

161135
This program is free software: you can redistribute it and/or modify

src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,15 @@
342342
*/
343343
message: function (msgStr) {
344344
const msg = JSON.parse(msgStr);
345+
_setPCommReady();
345346
if(msg && typeof msg === "object" && msg.method === "PhoenixComm.execLPFn") {
346347
_onLPFnTrigger(msg.fnName, msg.params);
347348
return;
348349
}
350+
if(msg && typeof msg === "object" && msg.method === "phoenixFnResponse") {
351+
_onPhoenixExecResponse(msg.fnName, msg.fnExecID, msg.resolveWith, msg.rejectWith);
352+
return;
353+
}
349354
// delegates handling/routing to MessageBroker.
350355
MessageBroker.trigger(msg);
351356
},
@@ -381,15 +386,75 @@
381386
}
382387
}
383388

389+
let currentFnExecID = 1;
390+
let lpCommReady = false;
391+
const pendingExecPromises = new Map();
392+
let queuedExecRequests = []; // array of { fnName, paramObj, fnExecID }
393+
394+
/**
395+
* Sends immediately if ready, else queues for later replay.
396+
* @param {{execFnName:string, paramObj:any, fnExecID:number}} payload
397+
*/
398+
function _sendOrQueueExec(payload) {
399+
if (lpCommReady) {
400+
MessageBroker.send(payload);
401+
} else {
402+
queuedExecRequests.push(payload);
403+
}
404+
}
405+
406+
/** Flush queued execPhoenixFn calls once LP comm becomes ready. */
407+
function _flushQueuedExecRequests() {
408+
if (!lpCommReady || queuedExecRequests.length === 0) {
409+
return;
410+
}
411+
// Preserve order
412+
for (let i = 0; i < queuedExecRequests.length; i++) {
413+
MessageBroker.send(queuedExecRequests[i]);
414+
}
415+
queuedExecRequests = [];
416+
}
417+
384418
const PhoenixComm = {
385419
registerLpFn: function (fnName, fn) {
386420
if(registeredPhoenixCommFns[fnName]){
387421
throw new Error(`Function "${fnName}" already registered with PhoenixComm`);
388422
}
389423
registeredPhoenixCommFns[fnName] = fn;
424+
},
425+
execPhoenixFn: function (fnName, paramObj) {
426+
return new Promise((resolve, reject) => {
427+
const fnExecID = currentFnExecID++;
428+
pendingExecPromises.set(fnExecID, { resolve, reject });
429+
_sendOrQueueExec({
430+
execFnName: fnName,
431+
paramObj,
432+
fnExecID
433+
});
434+
});
390435
}
391436
};
392437

438+
function _setPCommReady() {
439+
lpCommReady = true;
440+
_flushQueuedExecRequests();
441+
}
442+
443+
PhoenixComm.registerLpFn("PH_LP_COMM_READY", _setPCommReady);
444+
445+
function _onPhoenixExecResponse(fnName, fnExecID, resolveWith, rejectWith) {
446+
const pendingPromise = pendingExecPromises.get(fnExecID);
447+
if(!pendingPromise) {
448+
console.error(`execPhoenixFn: No response promise found! for ${fnName}: ${fnExecID}`);
449+
}
450+
pendingExecPromises.delete(fnExecID);
451+
if(rejectWith) {
452+
pendingPromise.reject(rejectWith);
453+
} else {
454+
pendingPromise.resolve(resolveWith);
455+
}
456+
}
457+
393458
global._Brackets_LiveDev_PhoenixComm = PhoenixComm;
394459

395460
window.addEventListener('load', function () {

src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ define(function (require, exports, module) {
317317
} else if (messageID) {
318318
processedMessageIDs.set(messageID, true);
319319
}
320+
if(msg && typeof msg === "object" && msg.execFnName) {
321+
_executePhoenixFn(msg.execFnName, msg.paramObj, msg.fnExecID, clientId);
322+
return;
323+
}
320324
if(_livePreviewMessageHandler) {
321325
let preventDefault = _livePreviewMessageHandler(msg);
322326
if(preventDefault){
@@ -405,6 +409,7 @@ define(function (require, exports, module) {
405409
clientId: clientId,
406410
url: url
407411
});
412+
triggerLPFn("PH_LP_COMM_READY", "", clientId);
408413
}
409414

410415
/**
@@ -585,6 +590,58 @@ define(function (require, exports, module) {
585590
registeredFunctions[fnName] = fn;
586591
}
587592

593+
function _toErrorString(e) {
594+
if (e instanceof Error) {
595+
if (e.stack) {
596+
return `${e.message}\n${e.stack}`;
597+
}
598+
return e.message || String(e);
599+
}
600+
return String(e);
601+
}
602+
603+
function _executePhoenixFn(fnName, params, fnExecID, clientId) {
604+
try{
605+
if(registeredFunctions[fnName]){
606+
const response = registeredFunctions[fnName](params, clientId);
607+
if(response instanceof Promise) {
608+
response.then((resolveValue)=>{
609+
_transport.send([clientId], JSON.stringify({
610+
method: "phoenixFnResponse",
611+
fnName, fnExecID,
612+
resolveWith: resolveValue
613+
}));
614+
}).catch(err => {
615+
_transport.send([clientId], JSON.stringify({
616+
method: "phoenixFnResponse",
617+
fnName, fnExecID,
618+
rejectWith: _toErrorString(err)
619+
}));
620+
});
621+
return;
622+
}
623+
_transport.send([clientId], JSON.stringify({
624+
method: "phoenixFnResponse",
625+
fnName, fnExecID,
626+
resolveWith: response
627+
}));
628+
} else {
629+
console.error(`Function "${fnName}" not registered with registerPhoenixFn`);
630+
_transport.send([clientId], JSON.stringify({
631+
method: "phoenixFnResponse",
632+
fnName, fnExecID,
633+
rejectWith: `Function "${fnName}" not registered with registerPhoenixFn`
634+
}));
635+
}
636+
} catch (err) {
637+
_transport.send([clientId], JSON.stringify({
638+
method: "phoenixFnResponse",
639+
fnName, fnExecID,
640+
rejectWith: _toErrorString(err)
641+
}));
642+
}
643+
}
644+
588645
/**
589646
* Triggers a named API function in the Live Preview for one or more clients
590647
* in a **fire-and-forget** manner.
@@ -636,9 +693,6 @@ define(function (require, exports, module) {
636693
});
637694
}
638695

639-
640-
window.ee= triggerLPFn; // todo remove this once all usages are migrated to execLPFn
641-
642696
/**
643697
* Closes the connection to the given client. Proxies to the transport.
644698
* @param {number} clientId

0 commit comments

Comments
 (0)