Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 6 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ designed to make coding as simple and fun as playing a video game.
4. Uncompromised local development experience.
5. Support for pluggable remote back-ends.
6. Phoenix core will work from a static web server.
7. Source code in release builds will always be auditable and readable from dev-tools.

AGPL/Libre license guards your right to audit and change code that handles your data.
Phoenix usually loads up in under one second and loading it faster at the expense of making it hard
to read and develop is a noop. We prioritize simplicity and eaze of development.
7. Compile step less build process. Code changes in phoenix do not need to be recompiled for most cases for development.
8. Prioritize simplicity and ease of development.

## Contributing/ Feedback
* [Get in touch with our community](https://github.com/phcode-dev/phoenix/discussions).
Expand Down Expand Up @@ -111,10 +108,10 @@ It has much better debug UX and fixing it directly in the browser will almost ce
2. Run the unit tests using format: `npm run test<*>Dist`. Eg. `npm run testChromiumDist`.
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`.

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

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

## Previewing changes in dev and staging
One a pull request is merged, it will be automatically deployed to dev.phcode.dev . To view the changes:
1. goto https://dev.phcode.dev/devEnable.html and click `enable dev.phcode.dev` . only needs to be done once.
2. goto https://dev.phcode.dev to preview your changes. If it is a recent change, you might need to wait for
up to 15 minutes before the changes are deployed to the dev stage. Reload page a few times to get the latest
dev build and reset cached content.

The process is the same for `staging.phcode.dev`. Builds that are verified in development will be pushed
periodically to staging. To view staging:
1. goto https://staging.phcode.dev/devEnable.html and click `enable staging.phcode.dev` . only needs to be done once.
2. goto https://staging.phcode.dev to preview your changes. If it is a recent change, you might need to wait for
up to 15 minutes before the changes are deployed to the dev stage. Reload page a few times to get the latest
dev build and reset cached content.

## Deployment to phcode.dev
* All changes pushed to the main branch are automatically published to https://dev.phcode.dev
* To publish the changes to https://staging.phcode.dev , push changes to the `staging` branch in this repo with a pull request.
* Once the changes are validated and tested, trigger a prod deployment by pushing to the `prod` branch.

## Acknowledgements
* Phoenix is based on the Brackets code editor by Adobe. Find out more on [Adobe Brackets here](https://github.com/adobe/brackets/).
* Our main code editor library https://codemirror.net/
* 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
* In browser server based on [nohost](https://github.com/humphd/nohost) by https://github.com/humphd/


## License
Discussion: https://github.com/phcode-dev/phoenix/issues/184

GNU AGPL-3.0 License

Copyright (c) 2021 - present Core.ai

Based on Backets, Copyright (c) 2012 Adobe Systems Incorporated and the brackets.io community

This program is free software: you can redistribute it and/or modify
Expand Down
65 changes: 65 additions & 0 deletions src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,15 @@
*/
message: function (msgStr) {
const msg = JSON.parse(msgStr);
_setPCommReady();
if(msg && typeof msg === "object" && msg.method === "PhoenixComm.execLPFn") {
_onLPFnTrigger(msg.fnName, msg.params);
return;
}
if(msg && typeof msg === "object" && msg.method === "phoenixFnResponse") {
_onPhoenixExecResponse(msg.fnName, msg.fnExecID, msg.resolveWith, msg.rejectWith);
return;
}
// delegates handling/routing to MessageBroker.
MessageBroker.trigger(msg);
},
Expand Down Expand Up @@ -381,15 +386,75 @@
}
}

let currentFnExecID = 1;
let lpCommReady = false;
const pendingExecPromises = new Map();
let queuedExecRequests = []; // array of { fnName, paramObj, fnExecID }

/**
* Sends immediately if ready, else queues for later replay.
* @param {{execFnName:string, paramObj:any, fnExecID:number}} payload
*/
function _sendOrQueueExec(payload) {
if (lpCommReady) {
MessageBroker.send(payload);
} else {
queuedExecRequests.push(payload);
}
}

/** Flush queued execPhoenixFn calls once LP comm becomes ready. */
function _flushQueuedExecRequests() {
if (!lpCommReady || queuedExecRequests.length === 0) {
return;
}
// Preserve order
for (let i = 0; i < queuedExecRequests.length; i++) {
MessageBroker.send(queuedExecRequests[i]);
}
queuedExecRequests = [];
}

const PhoenixComm = {
registerLpFn: function (fnName, fn) {
if(registeredPhoenixCommFns[fnName]){
throw new Error(`Function "${fnName}" already registered with PhoenixComm`);
}
registeredPhoenixCommFns[fnName] = fn;
},
execPhoenixFn: function (fnName, paramObj) {
return new Promise((resolve, reject) => {
const fnExecID = currentFnExecID++;
pendingExecPromises.set(fnExecID, { resolve, reject });
_sendOrQueueExec({
execFnName: fnName,
paramObj,
fnExecID
});
});
}
};

function _setPCommReady() {
lpCommReady = true;
_flushQueuedExecRequests();
}

PhoenixComm.registerLpFn("PH_LP_COMM_READY", _setPCommReady);

function _onPhoenixExecResponse(fnName, fnExecID, resolveWith, rejectWith) {
const pendingPromise = pendingExecPromises.get(fnExecID);
if(!pendingPromise) {
console.error(`execPhoenixFn: No response promise found! for ${fnName}: ${fnExecID}`);
}
pendingExecPromises.delete(fnExecID);
if(rejectWith) {
pendingPromise.reject(rejectWith);
} else {
pendingPromise.resolve(resolveWith);
}
}

global._Brackets_LiveDev_PhoenixComm = PhoenixComm;

window.addEventListener('load', function () {
Expand Down
60 changes: 57 additions & 3 deletions src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ define(function (require, exports, module) {
} else if (messageID) {
processedMessageIDs.set(messageID, true);
}
if(msg && typeof msg === "object" && msg.execFnName) {
_executePhoenixFn(msg.execFnName, msg.paramObj, msg.fnExecID, clientId);
return;
}
if(_livePreviewMessageHandler) {
let preventDefault = _livePreviewMessageHandler(msg);
if(preventDefault){
Expand Down Expand Up @@ -405,6 +409,7 @@ define(function (require, exports, module) {
clientId: clientId,
url: url
});
triggerLPFn("PH_LP_COMM_READY", "", clientId);
}

/**
Expand Down Expand Up @@ -585,6 +590,58 @@ define(function (require, exports, module) {
registeredFunctions[fnName] = fn;
}

function _toErrorString(e) {
if (e instanceof Error) {
if (e.stack) {
return `${e.message}\n${e.stack}`;
}
return e.message || String(e);
}
return String(e);
}

function _executePhoenixFn(fnName, params, fnExecID, clientId) {
try{
if(registeredFunctions[fnName]){
const response = registeredFunctions[fnName](params, clientId);
if(response instanceof Promise) {
response.then((resolveValue)=>{
_transport.send([clientId], JSON.stringify({
method: "phoenixFnResponse",
fnName, fnExecID,
resolveWith: resolveValue
}));
}).catch(err => {
_transport.send([clientId], JSON.stringify({
method: "phoenixFnResponse",
fnName, fnExecID,
rejectWith: _toErrorString(err)
}));
});
return;
}
_transport.send([clientId], JSON.stringify({
method: "phoenixFnResponse",
fnName, fnExecID,
resolveWith: response
}));
} else {
console.error(`Function "${fnName}" not registered with registerPhoenixFn`);
_transport.send([clientId], JSON.stringify({
method: "phoenixFnResponse",
fnName, fnExecID,
rejectWith: `Function "${fnName}" not registered with registerPhoenixFn`
}));
}
} catch (err) {
_transport.send([clientId], JSON.stringify({
method: "phoenixFnResponse",
fnName, fnExecID,
rejectWith: _toErrorString(err)
}));
}
}

/**
* Triggers a named API function in the Live Preview for one or more clients
* in a **fire-and-forget** manner.
Expand Down Expand Up @@ -636,9 +693,6 @@ define(function (require, exports, module) {
});
}


window.ee= triggerLPFn; // todo remove this once all usages are migrated to execLPFn

/**
* Closes the connection to the given client. Proxies to the transport.
* @param {number} clientId
Expand Down
2 changes: 1 addition & 1 deletion tracking-repos.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"phoenixPro": {
"commitID": "1bda799c4c6221d33c85e218226c9d66707e873d"
"commitID": "3826a7180c9463ecf02f4051e78696d503c7c6f1"
}
}
Loading