arcadible 0.1.47 → 0.1.49
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 +1 -0
- package/bin/arc-default.js +1 -0
- package/bin/arc-init.js +1 -1
- package/bin/arc-serve.js +213 -0
- package/package.json +5 -1
- package/src/action/serve/tool.js +166 -0
- package/src/cli/index.js +2 -0
- package/src/cli/serve.js +15 -0
- package/src/config/code.js +1 -0
- package/src/config/template.js +7 -7
- package/src/config/uri.js +5 -0
- package/src/config/url.js +2 -0
- package/src/phrase.js +110 -1
- package/src/serve/files.js +61 -0
- package/src/serve/game.js +185 -0
- package/src/serve/page.js +176 -0
- package/src/serve/reload.js +63 -0
- package/src/serve/remote.js +119 -0
- package/src/serve/server.js +115 -0
package/README.md
CHANGED
|
@@ -46,6 +46,7 @@ Your repository is the store page: `arcadible.toml` holds the game's settings, `
|
|
|
46
46
|
| `arc inspect` | Prints the manifest Arcadible will see, or one value from it |
|
|
47
47
|
| `arc build` | Runs your project's build command |
|
|
48
48
|
| `arc pack` | Packages your project into the zip that `arc deploy` uploads |
|
|
49
|
+
| `arc serve` | Serves your game locally, framed in a play page, as Arcadible does |
|
|
49
50
|
| `arc deploy` | Validates, builds, packages, and publishes, directly or through GitHub Actions |
|
|
50
51
|
| `arc play` | Plays a deployed game from any channel or version |
|
|
51
52
|
| `arc open` | Opens a game's store page |
|
package/bin/arc-default.js
CHANGED
|
@@ -39,6 +39,7 @@ const main = function (argv) {
|
|
|
39
39
|
.command("validate", "Validate project")
|
|
40
40
|
.command("build", "Build project")
|
|
41
41
|
.command("pack", "Package project")
|
|
42
|
+
.command("serve", "Serve a game locally")
|
|
42
43
|
.command("deploy", "Deploy project")
|
|
43
44
|
.command("open", "Open a game's store page")
|
|
44
45
|
.command("play", "Play a game")
|
package/bin/arc-init.js
CHANGED
|
@@ -149,7 +149,7 @@ const main = function (argv) {
|
|
|
149
149
|
default: conf.template,
|
|
150
150
|
describe: [
|
|
151
151
|
"Project template",
|
|
152
|
-
"Public GitHub repository (
|
|
152
|
+
"Public GitHub repository, or a folder in one (user/repo[/folder]).",
|
|
153
153
|
].join("\n"),
|
|
154
154
|
type: "string",
|
|
155
155
|
})
|
package/bin/arc-serve.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// [import]
|
|
5
|
+
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
// [import]
|
|
9
|
+
|
|
10
|
+
const hideBin = require("yargs/helpers").hideBin;
|
|
11
|
+
const yargs = require("yargs/yargs");
|
|
12
|
+
|
|
13
|
+
// [import]
|
|
14
|
+
|
|
15
|
+
const args = require("../src/util/argv");
|
|
16
|
+
const cfgcode = require("../src/config/code");
|
|
17
|
+
const cfgpackage = require("../src/config/package");
|
|
18
|
+
const cfgproduct = require("../src/config/product");
|
|
19
|
+
const cfgurl = require("../src/config/url");
|
|
20
|
+
const error = require("../src/util/error");
|
|
21
|
+
const log = require("../src/util/log");
|
|
22
|
+
const output = require("../src/util/output");
|
|
23
|
+
const phrase = require("../src/phrase");
|
|
24
|
+
const rc = require("../src/util/rc");
|
|
25
|
+
const serve = require("../src/cli/serve");
|
|
26
|
+
const shell = require("../src/util/shell");
|
|
27
|
+
const spinner = require("../src/util/spinner");
|
|
28
|
+
|
|
29
|
+
// [use]
|
|
30
|
+
|
|
31
|
+
const abort = output.abort;
|
|
32
|
+
const ArcError = error.ArcError;
|
|
33
|
+
const findRc = rc.findRc;
|
|
34
|
+
const launch = shell.launch;
|
|
35
|
+
const launched = output.launched;
|
|
36
|
+
const relative = path.relative;
|
|
37
|
+
const resolve = path.resolve;
|
|
38
|
+
const SpinnerWheel = spinner.SpinnerWheel;
|
|
39
|
+
|
|
40
|
+
// [declare]
|
|
41
|
+
|
|
42
|
+
const COMMAND = "serve";
|
|
43
|
+
const PORT = 4100;
|
|
44
|
+
const TOOL = cfgproduct.tool;
|
|
45
|
+
const VERSION = cfgpackage.version;
|
|
46
|
+
|
|
47
|
+
// [define]
|
|
48
|
+
|
|
49
|
+
const conf = {
|
|
50
|
+
build: true,
|
|
51
|
+
open: false,
|
|
52
|
+
port: PORT,
|
|
53
|
+
quiet: false,
|
|
54
|
+
...findRc(TOOL, COMMAND, process.argv),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// [function]
|
|
58
|
+
|
|
59
|
+
// fails unless the port is one a server can listen on.
|
|
60
|
+
const checkPort = function (port) {
|
|
61
|
+
if (Number.isInteger(port) && port > 0 && port < 65536) {
|
|
62
|
+
return Promise.resolve();
|
|
63
|
+
}
|
|
64
|
+
return Promise.reject(
|
|
65
|
+
new ArcError(cfgcode.ARGUMENT_INVALID, phrase.portInvalid()),
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// [function]
|
|
70
|
+
|
|
71
|
+
// the page --url names, as a URL, or a failure when it isn't an http one.
|
|
72
|
+
const checkUrl = function (url) {
|
|
73
|
+
if (url === null || typeof url === "undefined") {
|
|
74
|
+
return Promise.resolve(null);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const parsed = new URL(String(url));
|
|
78
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
79
|
+
return Promise.resolve(parsed.href);
|
|
80
|
+
}
|
|
81
|
+
} catch (ex) {
|
|
82
|
+
// not a URL at all: refused below, as one that isn't http.
|
|
83
|
+
}
|
|
84
|
+
return Promise.reject(
|
|
85
|
+
new ArcError(cfgcode.ARGUMENT_INVALID, phrase.urlInvalid()),
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// [function]
|
|
90
|
+
|
|
91
|
+
// warns once for each of Arcadible's files that's a kept copy, or can't be
|
|
92
|
+
// had, rather than on every request.
|
|
93
|
+
const remoteWarnings = function ({ quiet }) {
|
|
94
|
+
const warned = new Set();
|
|
95
|
+
return function (name, file, ex) {
|
|
96
|
+
const message = ex
|
|
97
|
+
? phrase.remoteUnreachable(name)
|
|
98
|
+
: file?.kept && phrase.remoteKept(name, file.fetchedAt);
|
|
99
|
+
if (!message || quiet || warned.has(message)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
warned.add(message);
|
|
103
|
+
log.pw(message);
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// [function]
|
|
108
|
+
|
|
109
|
+
const start = function (argv) {
|
|
110
|
+
const quiet = argv.quiet;
|
|
111
|
+
// `.arcrc` values are strings, and a flag that isn't a number is NaN.
|
|
112
|
+
const port = Number(argv.port);
|
|
113
|
+
const root = resolve(process.cwd(), argv.cwd);
|
|
114
|
+
const sw = new SpinnerWheel("Building... %s");
|
|
115
|
+
return checkPort(port)
|
|
116
|
+
.then(() => checkUrl(argv.url))
|
|
117
|
+
.then((url) =>
|
|
118
|
+
serve({
|
|
119
|
+
build: argv.build,
|
|
120
|
+
handler: {
|
|
121
|
+
onBuild: () => sw.start(quiet),
|
|
122
|
+
onReload() {
|
|
123
|
+
if (!quiet) {
|
|
124
|
+
log.p(phrase.reloaded());
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
onRemote: remoteWarnings({ quiet }),
|
|
128
|
+
},
|
|
129
|
+
port,
|
|
130
|
+
root,
|
|
131
|
+
url,
|
|
132
|
+
}),
|
|
133
|
+
)
|
|
134
|
+
.then((served) => {
|
|
135
|
+
sw.stop(true);
|
|
136
|
+
const stop = () => {
|
|
137
|
+
served.close();
|
|
138
|
+
process.exit(0);
|
|
139
|
+
};
|
|
140
|
+
process.once("SIGINT", stop);
|
|
141
|
+
process.once("SIGTERM", stop);
|
|
142
|
+
if (!quiet) {
|
|
143
|
+
log.result(phrase.served(served), [
|
|
144
|
+
["Play:", `${served.page}/`],
|
|
145
|
+
[phrase.serveAlone(), served.game],
|
|
146
|
+
served.url
|
|
147
|
+
? phrase.serveNextUrl()
|
|
148
|
+
: phrase.serveNext(relative(root, served.deployDir) || "."),
|
|
149
|
+
]);
|
|
150
|
+
if (served.probed && !served.probed.reachable) {
|
|
151
|
+
log.nlpw(phrase.urlUnreachable(served.url));
|
|
152
|
+
} else if (served.probed && !served.probed.frameable) {
|
|
153
|
+
log.nlpw(phrase.urlNotFrameable(served.url, cfgurl.SERVE_URL));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return argv.open ? launch(`${served.page}/`) : false;
|
|
157
|
+
})
|
|
158
|
+
.then((where) => {
|
|
159
|
+
if (where !== false && !quiet) {
|
|
160
|
+
launched(where, `http://localhost:${port}/`);
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
.catch((ex) => abort(phrase.aborted(COMMAND), ex, { sw }));
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// [function]
|
|
167
|
+
|
|
168
|
+
const main = function (argv) {
|
|
169
|
+
return start(
|
|
170
|
+
yargs(args.normalize(hideBin(argv)))
|
|
171
|
+
.scriptName(`${TOOL} ${COMMAND}`)
|
|
172
|
+
.usage("$0 [options]")
|
|
173
|
+
.option("port", {
|
|
174
|
+
alias: "p",
|
|
175
|
+
default: conf.port,
|
|
176
|
+
describe: "Port to serve on",
|
|
177
|
+
type: "number",
|
|
178
|
+
})
|
|
179
|
+
.option("build", {
|
|
180
|
+
default: conf.build,
|
|
181
|
+
describe: "Run the build command first",
|
|
182
|
+
type: "boolean",
|
|
183
|
+
})
|
|
184
|
+
.option("url", {
|
|
185
|
+
describe: "Frame another server's page instead of deploy_dir",
|
|
186
|
+
type: "string",
|
|
187
|
+
})
|
|
188
|
+
.option("open", {
|
|
189
|
+
alias: "o",
|
|
190
|
+
default: conf.open,
|
|
191
|
+
describe: "Open the play page in a web browser",
|
|
192
|
+
type: "boolean",
|
|
193
|
+
})
|
|
194
|
+
.option("quiet", { ...args.QUIET, default: conf.quiet })
|
|
195
|
+
.option("cwd", args.CWD)
|
|
196
|
+
.help("help")
|
|
197
|
+
.alias("help", "h")
|
|
198
|
+
.version(VERSION)
|
|
199
|
+
.alias("version", "v")
|
|
200
|
+
.strict()
|
|
201
|
+
.parse(),
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// [start]
|
|
206
|
+
|
|
207
|
+
if (require.main === module) {
|
|
208
|
+
main(process.argv);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// [export]
|
|
212
|
+
|
|
213
|
+
module.exports = { main };
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"arc-open": "bin/arc-open.js",
|
|
12
12
|
"arc-pack": "bin/arc-pack.js",
|
|
13
13
|
"arc-play": "bin/arc-play.js",
|
|
14
|
+
"arc-serve": "bin/arc-serve.js",
|
|
14
15
|
"arc-validate": "bin/arc-validate.js",
|
|
15
16
|
"arc-whoami": "bin/arc-whoami.js"
|
|
16
17
|
},
|
|
@@ -28,10 +29,12 @@
|
|
|
28
29
|
"ini": "^6.0.0",
|
|
29
30
|
"jsdom": "^27.3.0",
|
|
30
31
|
"marked": "^17.0.1",
|
|
32
|
+
"mime-types": "^3.0.2",
|
|
31
33
|
"mp4box": "^2.3.0",
|
|
32
34
|
"open": "^8.4.2",
|
|
33
35
|
"pretty-toml": "^1.0.0",
|
|
34
36
|
"qrcode-terminal": "^0.12.0",
|
|
37
|
+
"range-parser": "^1.3.0",
|
|
35
38
|
"semver": "^7.7.3",
|
|
36
39
|
"yargs": "^17.7.2",
|
|
37
40
|
"zod": "^4.1.13"
|
|
@@ -52,6 +55,7 @@
|
|
|
52
55
|
"./pack": "./src/cli/pack.js",
|
|
53
56
|
"./play": "./src/cli/play.js",
|
|
54
57
|
"./predeploy": "./src/cli/predeploy.js",
|
|
58
|
+
"./serve": "./src/cli/serve.js",
|
|
55
59
|
"./validate": "./src/cli/validate.js",
|
|
56
60
|
"./whoami": "./src/cli/whoami.js"
|
|
57
61
|
},
|
|
@@ -75,5 +79,5 @@
|
|
|
75
79
|
"scripts": {
|
|
76
80
|
"test": "node --test \"test/**/*.test.js\""
|
|
77
81
|
},
|
|
78
|
-
"version": "0.1.
|
|
82
|
+
"version": "0.1.49"
|
|
79
83
|
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// arc serve: builds the project unless told not to, then serves deploy_dir
|
|
3
|
+
// framed in a local play page (src/serve), reloading the game when deploy_dir
|
|
4
|
+
// changes.
|
|
5
|
+
|
|
6
|
+
// [import]
|
|
7
|
+
|
|
8
|
+
const path = require("node:path");
|
|
9
|
+
|
|
10
|
+
// [import]
|
|
11
|
+
|
|
12
|
+
const fs = require("fs-extra");
|
|
13
|
+
|
|
14
|
+
// [import]
|
|
15
|
+
|
|
16
|
+
const buildTool = require("../build/tool");
|
|
17
|
+
const cfgcode = require("../../config/code");
|
|
18
|
+
const cfgproduct = require("../../config/product");
|
|
19
|
+
const cfguri = require("../../config/uri");
|
|
20
|
+
const commands = require("../../util/command");
|
|
21
|
+
const error = require("../../util/error");
|
|
22
|
+
const files = require("../../serve/files");
|
|
23
|
+
const given = require("../../parser/given");
|
|
24
|
+
const os = require("../../util/os");
|
|
25
|
+
const phrase = require("../../phrase");
|
|
26
|
+
const reload = require("../../serve/reload");
|
|
27
|
+
const remote = require("../../serve/remote");
|
|
28
|
+
const server = require("../../serve/server");
|
|
29
|
+
|
|
30
|
+
// [use]
|
|
31
|
+
|
|
32
|
+
const ArcError = error.ArcError;
|
|
33
|
+
const buildCommand = commands.buildCommand;
|
|
34
|
+
const configDir = os.configDir;
|
|
35
|
+
const dirFiles = files.dirFiles;
|
|
36
|
+
const join = path.join;
|
|
37
|
+
const pathExists = fs.pathExists;
|
|
38
|
+
const relative = path.relative;
|
|
39
|
+
const remoteFiles = remote.remoteFiles;
|
|
40
|
+
const resolvePath = path.resolve;
|
|
41
|
+
const serve = server.serve;
|
|
42
|
+
const watchDir = reload.watchDir;
|
|
43
|
+
|
|
44
|
+
// [declare]
|
|
45
|
+
|
|
46
|
+
// what build.channel says while served locally (docs: arc-serve.md).
|
|
47
|
+
const CHANNEL = "local";
|
|
48
|
+
// the saves of a game with no id yet, kept apart from any other's.
|
|
49
|
+
const UNNAMED = "local";
|
|
50
|
+
|
|
51
|
+
// [function]
|
|
52
|
+
|
|
53
|
+
// the headers a page needs for the play page to frame it: the play page is
|
|
54
|
+
// cross-origin isolated, so a cross-origin frame must be too, and allow it.
|
|
55
|
+
const FRAMEABLE = {
|
|
56
|
+
"cross-origin-embedder-policy": ["credentialless", "require-corp"],
|
|
57
|
+
"cross-origin-resource-policy": ["cross-origin"],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// [function]
|
|
61
|
+
|
|
62
|
+
// what `url` says when asked: `{ reachable, frameable }`, where `frameable`
|
|
63
|
+
// says whether it sends the headers the play page needs to frame it.
|
|
64
|
+
const probe = async function (url) {
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(url, { method: "GET" });
|
|
67
|
+
await res.body?.cancel();
|
|
68
|
+
const frameable = Object.entries(FRAMEABLE).every(([name, values]) =>
|
|
69
|
+
values.includes(res.headers.get(name)?.trim().toLowerCase()),
|
|
70
|
+
);
|
|
71
|
+
return { frameable, reachable: true };
|
|
72
|
+
} catch (ex) {
|
|
73
|
+
return { frameable: false, reachable: false };
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// [function]
|
|
78
|
+
|
|
79
|
+
// listens, turning a port in use into its code.
|
|
80
|
+
const listen = async function (options) {
|
|
81
|
+
try {
|
|
82
|
+
return await serve(options);
|
|
83
|
+
} catch (ex) {
|
|
84
|
+
if (ex.code === "EADDRINUSE") {
|
|
85
|
+
throw new ArcError(cfgcode.PORT_IN_USE, phrase.portInUse(options.port), {
|
|
86
|
+
cause: ex,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
throw ex;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// [function]
|
|
94
|
+
|
|
95
|
+
// serves the project at `conf.root`. `conf.build` false skips the build, and
|
|
96
|
+
// `conf.url` frames that server's page instead of serving deploy_dir.
|
|
97
|
+
// `conf.handler` hears `onBuild()` as a build starts, `onRemote(name, file,
|
|
98
|
+
// error)` for each of Arcadible's files, and `onReload()` for each reload.
|
|
99
|
+
// resolves with `{ page, game, built, deployDir, url, probed, version,
|
|
100
|
+
// close() }`, where `probed` is what `conf.url` answered.
|
|
101
|
+
const main = async function (conf) {
|
|
102
|
+
const { port, root, url } = conf;
|
|
103
|
+
const handler = {
|
|
104
|
+
onBuild() {},
|
|
105
|
+
onReload() {},
|
|
106
|
+
onRemote() {},
|
|
107
|
+
...conf.handler,
|
|
108
|
+
};
|
|
109
|
+
const project = await given(conf);
|
|
110
|
+
const game = project.game;
|
|
111
|
+
const options = {
|
|
112
|
+
info: {
|
|
113
|
+
channel: CHANNEL,
|
|
114
|
+
id: game.id ?? UNNAMED,
|
|
115
|
+
title: game.title ?? game.handle ?? cfgproduct.title,
|
|
116
|
+
version: game.version ?? null,
|
|
117
|
+
},
|
|
118
|
+
onRemote: handler.onRemote,
|
|
119
|
+
port,
|
|
120
|
+
remote: remoteFiles({
|
|
121
|
+
asset: cfguri.ARCADIBLE_ASSET,
|
|
122
|
+
dir: join(configDir(), cfgproduct.tool, "sdk"),
|
|
123
|
+
sdk: cfguri.ARCADIBLE_SDK,
|
|
124
|
+
}),
|
|
125
|
+
};
|
|
126
|
+
const version = game.version ?? null;
|
|
127
|
+
if (url) {
|
|
128
|
+
const served = await listen({ ...options, src: url });
|
|
129
|
+
const probed = await probe(url);
|
|
130
|
+
return { ...served, built: false, deployDir: null, probed, url, version };
|
|
131
|
+
}
|
|
132
|
+
const built =
|
|
133
|
+
conf.build !== false && Boolean(buildCommand(project.workflow.build));
|
|
134
|
+
if (built) {
|
|
135
|
+
handler.onBuild();
|
|
136
|
+
await buildTool({ project, root });
|
|
137
|
+
}
|
|
138
|
+
const deployDir = resolvePath(root, project.workflow.deploy_dir);
|
|
139
|
+
if (!(await pathExists(deployDir))) {
|
|
140
|
+
throw new ArcError(
|
|
141
|
+
cfgcode.DEPLOY_DIR_MISSING,
|
|
142
|
+
phrase.deployDirMissing(relative(root, deployDir) || "."),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const served = await listen({ ...options, files: dirFiles(deployDir) });
|
|
146
|
+
const stop = watchDir(deployDir, () => {
|
|
147
|
+
served.reload();
|
|
148
|
+
handler.onReload();
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
...served,
|
|
152
|
+
built,
|
|
153
|
+
close() {
|
|
154
|
+
stop();
|
|
155
|
+
served.close();
|
|
156
|
+
},
|
|
157
|
+
deployDir,
|
|
158
|
+
probed: null,
|
|
159
|
+
url: null,
|
|
160
|
+
version,
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// [export]
|
|
165
|
+
|
|
166
|
+
module.exports = main;
|
package/src/cli/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const open = require("./open");
|
|
|
12
12
|
const pack = require("./pack");
|
|
13
13
|
const play = require("./play");
|
|
14
14
|
const predeploy = require("./predeploy");
|
|
15
|
+
const serve = require("./serve");
|
|
15
16
|
const validate = require("./validate");
|
|
16
17
|
const whoami = require("./whoami");
|
|
17
18
|
|
|
@@ -28,6 +29,7 @@ module.exports = {
|
|
|
28
29
|
pack,
|
|
29
30
|
play,
|
|
30
31
|
predeploy,
|
|
32
|
+
serve,
|
|
31
33
|
validate,
|
|
32
34
|
whoami,
|
|
33
35
|
};
|
package/src/cli/serve.js
ADDED
package/src/config/code.js
CHANGED
|
@@ -37,6 +37,7 @@ const CLI = {
|
|
|
37
37
|
PACKAGE_UNCOMPRESSED_TOO_LARGE: "package_uncompressed_too_large",
|
|
38
38
|
PARSE_FAILED: "parse_failed",
|
|
39
39
|
PATH_NOT_FOUND: "path_not_found",
|
|
40
|
+
PORT_IN_USE: "port_in_use",
|
|
40
41
|
PROJECT_NOT_REPO_ROOT: "project_not_repo_root",
|
|
41
42
|
SESSION_EXPIRED: "session_expired",
|
|
42
43
|
TEMPLATE_MISSING: "template_missing",
|
package/src/config/template.js
CHANGED
|
@@ -8,7 +8,7 @@ const phrase = require("../phrase");
|
|
|
8
8
|
// [declare]
|
|
9
9
|
|
|
10
10
|
const HANDLE = cfgproduct.handle;
|
|
11
|
-
const
|
|
11
|
+
const STARTERS = `${HANDLE}/starters`;
|
|
12
12
|
|
|
13
13
|
// [export]
|
|
14
14
|
|
|
@@ -17,29 +17,29 @@ module.exports = {
|
|
|
17
17
|
{
|
|
18
18
|
description: phrase.starterBlank(),
|
|
19
19
|
name: "Blank",
|
|
20
|
-
value:
|
|
20
|
+
value: `${STARTERS}/blank`,
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
description: phrase.starterThreeJs(),
|
|
24
24
|
name: "Three.js",
|
|
25
|
-
value: `${
|
|
25
|
+
value: `${STARTERS}/threejs`,
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
description: phrase.starterPhaser(),
|
|
29
29
|
name: "Phaser",
|
|
30
|
-
value: `${
|
|
30
|
+
value: `${STARTERS}/phaser`,
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
description: phrase.starterPixiJs(),
|
|
34
34
|
name: "PixiJS",
|
|
35
|
-
value: `${
|
|
35
|
+
value: `${STARTERS}/pixijs`,
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
description: phrase.starterBabylonJs(),
|
|
39
39
|
name: "Babylon.js",
|
|
40
|
-
value: `${
|
|
40
|
+
value: `${STARTERS}/babylonjs`,
|
|
41
41
|
},
|
|
42
42
|
],
|
|
43
|
-
default:
|
|
43
|
+
default: `${STARTERS}/blank`,
|
|
44
44
|
message: "Template",
|
|
45
45
|
};
|
package/src/config/uri.js
CHANGED
|
@@ -12,6 +12,11 @@ const PROTOCOL = cfgproduct.protocol;
|
|
|
12
12
|
// [export]
|
|
13
13
|
|
|
14
14
|
module.exports = {
|
|
15
|
+
// where arc serve fetches Arcadible's own files (src/serve/remote.js): the
|
|
16
|
+
// asset site, which has the play page's host, and a build origin, any of
|
|
17
|
+
// which serves the SDK's hosted script at /arcadible/sdk/.
|
|
18
|
+
ARCADIBLE_ASSET: "https://asset.offst.net",
|
|
19
|
+
ARCADIBLE_SDK: `https://sdk.${cfgproduct.handle}.io`,
|
|
15
20
|
HOST_APP_PLAY: `${PROTOCOL}://play`,
|
|
16
21
|
HOST_APP_STORE: `${PROTOCOL}://store`,
|
|
17
22
|
HOST_HTTP_ACCOUNT: `https://account.${DOMAIN}`,
|
package/src/config/url.js
CHANGED
|
@@ -15,4 +15,6 @@ module.exports = {
|
|
|
15
15
|
ERRORS: `${cfguri.HOST_HTTP_DEVELOPER}/references/errors`,
|
|
16
16
|
LINK: `${cfguri.HOST_HTTP_ACCOUNT}/link`,
|
|
17
17
|
MANIFEST: `${cfguri.HOST_HTTP_DEVELOPER}/references/${HANDLE}-manifest`,
|
|
18
|
+
// how to let arc serve's play page frame another server.
|
|
19
|
+
SERVE_URL: `${cfguri.HOST_HTTP_DEVELOPER}/references/${HANDLE}-cli/arc-serve#url`,
|
|
18
20
|
};
|
package/src/phrase.js
CHANGED
|
@@ -272,6 +272,103 @@ const packed = function () {
|
|
|
272
272
|
|
|
273
273
|
// [function]
|
|
274
274
|
|
|
275
|
+
const portInvalid = function () {
|
|
276
|
+
return "The port must be a whole number from 1 to 65535.";
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// [function]
|
|
280
|
+
|
|
281
|
+
const portInUse = function (port) {
|
|
282
|
+
return blocks(
|
|
283
|
+
`Port ${port} is in use.`,
|
|
284
|
+
"Stop what's using it, or pass another with --port.",
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// [function]
|
|
289
|
+
|
|
290
|
+
// what one of Arcadible's files is, for arc serve's warnings: `host`, the play
|
|
291
|
+
// page's, or a path under /arcadible/, such as sdk/v1.js.
|
|
292
|
+
const remoteName = function (name) {
|
|
293
|
+
return name === "host"
|
|
294
|
+
? "the play page's host"
|
|
295
|
+
: `/${cfgproduct.handle}/${name}`;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// [function]
|
|
299
|
+
|
|
300
|
+
// arc serve's copy of one of Arcadible's files, kept from `fetchedAt`.
|
|
301
|
+
const remoteKept = function (name, fetchedAt) {
|
|
302
|
+
return `${TITLE} can't be reached: serving ${remoteName(name)} from ${fetchedAt.slice(0, 10)}.`;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// [function]
|
|
306
|
+
|
|
307
|
+
const remoteUnreachable = function (name) {
|
|
308
|
+
return `${TITLE} can't be reached, and there's no copy of ${remoteName(name)} yet: the game runs without it until ${TITLE} can be.`;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// [function]
|
|
312
|
+
|
|
313
|
+
const reloaded = function () {
|
|
314
|
+
return `Reloaded at ${new Date().toTimeString().slice(0, 8)}.`;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// [function]
|
|
318
|
+
|
|
319
|
+
const serveAlone = function () {
|
|
320
|
+
return "The game alone, without the play page:";
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// [function]
|
|
324
|
+
|
|
325
|
+
const serveNext = function (dir) {
|
|
326
|
+
return `Reloads when ${dir}/ changes. Press Ctrl+C to stop.`;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// [function]
|
|
330
|
+
|
|
331
|
+
const served = function ({ built, url, version }) {
|
|
332
|
+
const what = version ? `v${version}` : "the game";
|
|
333
|
+
if (url) {
|
|
334
|
+
return `Serving ${what} locally, from ${url}`;
|
|
335
|
+
}
|
|
336
|
+
return built
|
|
337
|
+
? `Built and serving ${what} locally.`
|
|
338
|
+
: `Serving ${what} locally.`;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// [function]
|
|
342
|
+
|
|
343
|
+
const serveNextUrl = function () {
|
|
344
|
+
return "Your server reloads the game. Press Ctrl+C to stop.";
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// [function]
|
|
348
|
+
|
|
349
|
+
const urlInvalid = function () {
|
|
350
|
+
return "The URL must be an http or https one, such as http://localhost:5173.";
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// [function]
|
|
354
|
+
|
|
355
|
+
// --url's server sent no headers that let the play page frame it. `link`
|
|
356
|
+
// says how to add them.
|
|
357
|
+
const urlNotFrameable = function (url, link) {
|
|
358
|
+
return blocks(
|
|
359
|
+
`⚠ ${url} can't be framed by the play page: it needs to send Cross-Origin-Embedder-Policy: require-corp and Cross-Origin-Resource-Policy: cross-origin.`,
|
|
360
|
+
["Add them to your server, as the starters' vite.config.js does:", link],
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// [function]
|
|
365
|
+
|
|
366
|
+
const urlUnreachable = function (url) {
|
|
367
|
+
return `⚠ Nothing answers at ${url} yet. Start your server, then reload the play page.`;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// [function]
|
|
371
|
+
|
|
275
372
|
const pressEnterToOpen = function () {
|
|
276
373
|
return "Press Enter to open the URL.";
|
|
277
374
|
};
|
|
@@ -726,7 +823,7 @@ const tagPushed = function (tag) {
|
|
|
726
823
|
const templateUnavailable = function (template) {
|
|
727
824
|
return blocks(
|
|
728
825
|
`Unable to download template ${template}.`,
|
|
729
|
-
"Check that it's a public GitHub repository.",
|
|
826
|
+
"Check that it's a public GitHub repository, or a folder in one.",
|
|
730
827
|
);
|
|
731
828
|
};
|
|
732
829
|
|
|
@@ -971,6 +1068,8 @@ module.exports = {
|
|
|
971
1068
|
packageTooManyFiles,
|
|
972
1069
|
packageUncompressedTooLarge,
|
|
973
1070
|
packed,
|
|
1071
|
+
portInUse,
|
|
1072
|
+
portInvalid,
|
|
974
1073
|
pressEnterToOpen,
|
|
975
1074
|
projectNotRepoRoot,
|
|
976
1075
|
releaseFailed,
|
|
@@ -978,7 +1077,14 @@ module.exports = {
|
|
|
978
1077
|
releasePublished,
|
|
979
1078
|
releaseTimedOut,
|
|
980
1079
|
releaseWaiting,
|
|
1080
|
+
reloaded,
|
|
1081
|
+
remoteKept,
|
|
1082
|
+
remoteUnreachable,
|
|
981
1083
|
repoNotConnected,
|
|
1084
|
+
serveAlone,
|
|
1085
|
+
serveNext,
|
|
1086
|
+
serveNextUrl,
|
|
1087
|
+
served,
|
|
982
1088
|
sessionExpired,
|
|
983
1089
|
sizeB,
|
|
984
1090
|
sizeGb,
|
|
@@ -1001,6 +1107,9 @@ module.exports = {
|
|
|
1001
1107
|
unknownTable,
|
|
1002
1108
|
uploadFailed,
|
|
1003
1109
|
uploadRateLimited,
|
|
1110
|
+
urlInvalid,
|
|
1111
|
+
urlNotFrameable,
|
|
1112
|
+
urlUnreachable,
|
|
1004
1113
|
versionExists,
|
|
1005
1114
|
versionPrerelease,
|
|
1006
1115
|
versionRelease,
|