@makefully/adaptfully 2.1.0 → 3.0.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.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { adaptfullyFromCli } from '../lib/node/pipeline.js';
3
+
4
+ adaptfullyFromCli().catch((err) => {
5
+ console.error(err.message || err);
6
+ process.exit(1);
7
+ });
@@ -1,7 +1,22 @@
1
- #!/usr/bin/env node
2
- import { deployFromCli } from '../lib/node/deploy.js';
3
-
4
- deployFromCli().catch((err) => {
5
- console.error(err);
6
- process.exit(1);
7
- });
1
+ #!/usr/bin/env node
2
+ import { loadProjectConfig, resolveServerUrl } from '../lib/node/config.js';
3
+ import { runAdaptfullyStage } from '../lib/node/pipeline.js';
4
+ import { resolveCliPlatformAndBuilder } from '../lib/node/registrations.js';
5
+
6
+ const arg = process.argv[2] ?? 'steam';
7
+ const cliServer = process.argv[3];
8
+ const mode = process.argv[4] ?? 'extract';
9
+
10
+ const { pkg, wrapfullyConfig } = await loadProjectConfig();
11
+ const { platformKey, builder } = resolveCliPlatformAndBuilder(arg, pkg);
12
+
13
+ runAdaptfullyStage('deploy', platformKey, {
14
+ pkg,
15
+ deployFolder: pkg.config?.deployFolder || 'deploy',
16
+ server: resolveServerUrl(wrapfullyConfig, cliServer),
17
+ mode,
18
+ builder,
19
+ }).catch((err) => {
20
+ console.error(err.message || err);
21
+ process.exit(1);
22
+ });
@@ -1,34 +1,43 @@
1
- import archiver from 'archiver';
2
- import fs from 'node:fs';
3
-
4
- /**
5
- * @param {string} deployFolder
6
- * @param {string} contents - serialized package.json for the zip
7
- */
8
- export function createArchive(deployFolder, contents) {
9
- const zip = archiver('zip', { zlib: { level: 0 } });
10
-
11
- zip.on('warning', (err) => {
12
- if (err.code === 'ENOENT') {
13
- console.log(err);
14
- return;
15
- }
16
- throw err;
17
- });
18
- zip.on('error', (err) => {
19
- throw err;
20
- });
21
- zip.on('close', () => {
22
- console.log(`Zipped ${zip.pointer()} total bytes`);
23
- });
24
-
25
- zip.directory(`${deployFolder}/`, 'deploy');
26
- zip.file(`${deployFolder}/index.html`, { name: 'deploy/index.html' });
27
- if (fs.existsSync('assets/meta/')) {
28
- zip.directory('assets/meta/', 'meta');
29
- }
30
- zip.append(contents, { name: 'package.json' });
31
- zip.finalize();
32
-
33
- return zip;
34
- }
1
+ import archiver from 'archiver';
2
+ import fs from 'node:fs';
3
+
4
+ /**
5
+ * @param {string} deployFolder
6
+ * @param {string} contents - serialized package.json for the zip
7
+ * @param {{ indexHtml?: string }} [options]
8
+ */
9
+ export function createArchive(deployFolder, contents, options = {}) {
10
+ const zip = archiver('zip', { zlib: { level: 0 } });
11
+
12
+ zip.on('warning', (err) => {
13
+ if (err.code === 'ENOENT') {
14
+ console.log(err);
15
+ return;
16
+ }
17
+ throw err;
18
+ });
19
+ zip.on('error', (err) => {
20
+ throw err;
21
+ });
22
+ zip.on('close', () => {
23
+ console.log(`Zipped ${zip.pointer()} total bytes`);
24
+ });
25
+
26
+ if (options.indexHtml != null) {
27
+ zip.glob('**/*', {
28
+ cwd: deployFolder,
29
+ ignore: ['index.html'],
30
+ }, { prefix: 'deploy' });
31
+ zip.append(options.indexHtml, { name: 'deploy/index.html' });
32
+ } else {
33
+ zip.directory(`${deployFolder}/`, 'deploy');
34
+ zip.file(`${deployFolder}/index.html`, { name: 'deploy/index.html' });
35
+ }
36
+ if (fs.existsSync('assets/meta/')) {
37
+ zip.directory('assets/meta/', 'meta');
38
+ }
39
+ zip.append(contents, { name: 'package.json' });
40
+ zip.finalize();
41
+
42
+ return zip;
43
+ }
@@ -1,42 +1,42 @@
1
- import fs from 'node:fs/promises';
2
-
3
- const DEFAULT_SERVER = 'http://localhost:9630/';
4
-
5
- /**
6
- * @param {string} [projectRoot='.']
7
- */
8
- export async function loadProjectConfig(projectRoot = '.') {
9
- const pkgPath = `${projectRoot}/package.json`;
10
- const wrapfullyPath = `${projectRoot}/wrapfully.json`;
11
-
12
- const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
13
- let wrapfullyConfig = {};
14
-
15
- try {
16
- wrapfullyConfig = JSON.parse(await fs.readFile(wrapfullyPath, 'utf8'));
17
- } catch (err) {
18
- if (/** @type {NodeJS.ErrnoException} */ (err).code !== 'ENOENT') {
19
- throw err;
20
- }
21
- }
22
-
23
- pkg.config = {
24
- ...pkg.config,
25
- ...wrapfullyConfig,
26
- };
27
-
28
- return { pkg, wrapfullyConfig };
29
- }
30
-
31
- /**
32
- * @param {{ server?: string }} wrapfullyConfig
33
- * @param {string} [cliServer]
34
- */
35
- export function resolveServerUrl(wrapfullyConfig, cliServer) {
36
- return (
37
- cliServer
38
- || process.env.WRAPFULLY_SERVER
39
- || wrapfullyConfig.server
40
- || DEFAULT_SERVER
41
- ).replace(/\/?$/, '/');
42
- }
1
+ import fs from 'node:fs/promises';
2
+
3
+ const DEFAULT_SERVER = 'http://localhost:9630/';
4
+
5
+ /**
6
+ * @param {string} [projectRoot='.']
7
+ */
8
+ export async function loadProjectConfig(projectRoot = '.') {
9
+ const pkgPath = `${projectRoot}/package.json`;
10
+ const wrapfullyPath = `${projectRoot}/wrapfully.json`;
11
+
12
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
13
+ let wrapfullyConfig = {};
14
+
15
+ try {
16
+ wrapfullyConfig = JSON.parse(await fs.readFile(wrapfullyPath, 'utf8'));
17
+ } catch (err) {
18
+ if (/** @type {NodeJS.ErrnoException} */ (err).code !== 'ENOENT') {
19
+ throw err;
20
+ }
21
+ }
22
+
23
+ pkg.config = {
24
+ ...pkg.config,
25
+ ...wrapfullyConfig,
26
+ };
27
+
28
+ return { pkg, wrapfullyConfig };
29
+ }
30
+
31
+ /**
32
+ * @param {{ server?: string }} wrapfullyConfig
33
+ * @param {string} [cliServer]
34
+ */
35
+ export function resolveServerUrl(wrapfullyConfig, cliServer) {
36
+ return (
37
+ cliServer
38
+ || process.env.WRAPFULLY_SERVER
39
+ || wrapfullyConfig.server
40
+ || DEFAULT_SERVER
41
+ ).replace(/\/?$/, '/');
42
+ }
@@ -1,69 +1,52 @@
1
- import axios from 'axios';
2
- import fs from 'node:fs';
3
- import { pipeline } from 'node:stream/promises';
4
- import unzipper from 'unzip-stream';
5
- import { createArchive } from './archive.js';
6
- import { loadProjectConfig, resolveServerUrl } from './config.js';
7
- import { printBuildReport } from './report.js';
8
-
9
- /**
10
- * @param {string} gameId
11
- * @param {string} contents
12
- * @param {string} server
13
- * @param {string} builder
14
- * @param {string} deployFolder
15
- * @param {{ name: string, version: string }} pkg
16
- * @param {'extract' | string} mode
17
- */
18
- export async function send(gameId, contents, server, builder, deployFolder, pkg, mode = 'extract') {
19
- const destination = mode === 'extract'
20
- ? unzipper.Extract({ path: './output/', concurrency: 1 })
21
- : fs.createWriteStream(`./output/${pkg.name}-${pkg.version}-${builder}.zip`);
22
-
23
- const archiveStream = createArchive(deployFolder, contents);
24
- const { data } = await axios.post(`${server}${builder}/${gameId}`, archiveStream, {
25
- maxRedirects: 0,
26
- responseType: 'stream',
27
- });
28
-
29
- archiveStream.on('close', () => {
30
- console.log('completed send');
31
- });
32
-
33
- try {
34
- await pipeline(data, destination);
35
- } catch (err) {
36
- if (/** @type {NodeJS.ErrnoException} */ (err).code === 'ECONNREFUSED') {
37
- console.error(`Cannot connect to Wrapfully server "${server}"`);
38
- process.exit(1);
39
- }
40
- throw err;
41
- }
42
-
43
- if (mode === 'extract') {
44
- printBuildReport(builder, pkg);
45
- }
46
- }
47
-
48
- /**
49
- * @param {string[]} [argv=process.argv]
50
- */
51
- export async function deployFromCli(argv = process.argv) {
52
- const builder = argv[2] ?? 'all';
53
- const cliServer = argv[3];
54
- const mode = argv[4] ?? 'extract';
55
-
56
- const { pkg, wrapfullyConfig } = await loadProjectConfig();
57
- const server = resolveServerUrl(wrapfullyConfig, cliServer);
58
- const deployFolder = pkg.config?.deployFolder || 'deploy';
59
-
60
- await send(
61
- `${pkg.name}-${pkg.version}`,
62
- JSON.stringify(pkg),
63
- server,
64
- builder,
65
- deployFolder,
66
- pkg,
67
- mode,
68
- );
69
- }
1
+ import axios from 'axios';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { pipeline } from 'node:stream/promises';
5
+ import unzipper from 'unzip-stream';
6
+ import { createArchive } from './archive.js';
7
+ import { listHtmlFilesRecursive } from './fs-utils.js';
8
+ import { printBuildReport } from './report.js';
9
+
10
+ /**
11
+ * @param {string} gameId
12
+ * @param {string} contents
13
+ * @param {string} server
14
+ * @param {string} builder
15
+ * @param {string} deployFolder - prebuilt deploy directory
16
+ * @param {{ name: string, version: string }} pkg
17
+ * @param {'extract' | string} mode
18
+ * @param {{ log?: (message: string) => void }} [options]
19
+ */
20
+ export async function send(gameId, contents, server, builder, deployFolder, pkg, mode = 'extract', options = {}) {
21
+ const log = options.log ?? console.log;
22
+ const destination = mode === 'extract'
23
+ ? unzipper.Extract({ path: './output/', concurrency: 1 })
24
+ : fs.createWriteStream(`./output/${pkg.name}-${pkg.version}-${builder}.zip`);
25
+
26
+ const htmlFiles = listHtmlFilesRecursive(deployFolder);
27
+ log(`adaptfully: sending ${htmlFiles.length} HTML file(s) from ${path.resolve(deployFolder)}`);
28
+
29
+ const archiveStream = createArchive(deployFolder, contents);
30
+ const { data } = await axios.post(`${server}${builder}/${gameId}`, archiveStream, {
31
+ maxRedirects: 0,
32
+ responseType: 'stream',
33
+ });
34
+
35
+ archiveStream.on('close', () => {
36
+ log('adaptfully: upload complete');
37
+ });
38
+
39
+ try {
40
+ await pipeline(data, destination);
41
+ } catch (err) {
42
+ if (/** @type {NodeJS.ErrnoException} */ (err).code === 'ECONNREFUSED') {
43
+ console.error(`Cannot connect to Wrapfully server "${server}"`);
44
+ process.exit(1);
45
+ }
46
+ throw err;
47
+ }
48
+
49
+ if (mode === 'extract') {
50
+ printBuildReport(builder, pkg);
51
+ }
52
+ }
@@ -0,0 +1,49 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * @param {string} src
6
+ * @param {string} dest
7
+ */
8
+ export function copyRecursiveSync(src, dest) {
9
+ fs.mkdirSync(dest, { recursive: true });
10
+
11
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
12
+ const srcPath = path.join(src, entry.name);
13
+ const destPath = path.join(dest, entry.name);
14
+
15
+ if (entry.isDirectory()) {
16
+ copyRecursiveSync(srcPath, destPath);
17
+ } else {
18
+ fs.copyFileSync(srcPath, destPath);
19
+ }
20
+ }
21
+ }
22
+
23
+ /**
24
+ * @param {string} dir
25
+ * @returns {string[]}
26
+ */
27
+ export function listHtmlFilesRecursive(dir) {
28
+ /** @type {string[]} */
29
+ const files = [];
30
+
31
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
32
+ const fullPath = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ files.push(...listHtmlFilesRecursive(fullPath));
35
+ } else if (entry.name.endsWith('.html')) {
36
+ files.push(fullPath);
37
+ }
38
+ }
39
+
40
+ return files;
41
+ }
42
+
43
+ /**
44
+ * @param {string} dir
45
+ */
46
+ export function emptyDirSync(dir) {
47
+ fs.rmSync(dir, { recursive: true, force: true });
48
+ fs.mkdirSync(dir, { recursive: true });
49
+ }
package/lib/node/index.js CHANGED
@@ -1,18 +1,20 @@
1
- export { createArchive } from './archive.js';
2
- export { loadProjectConfig, resolveServerUrl } from './config.js';
3
- export { deployFromCli, send } from './deploy.js';
4
- export {
5
- authRegistrationForChannel,
6
- authRegistrationScript,
7
- devAuthRegistration,
8
- distributionSettingsForBuild,
9
- extScriptsForBuildChannel,
10
- filterIncludesForBuildChannel,
11
- getAuthScriptsForChannel,
12
- getBuildChannel,
13
- getPackageRoot,
14
- getRuntimeDir,
15
- resolveRuntimeScript,
16
- VALID_CHANNELS,
17
- } from './distribution.js';
18
- export { printBuildReport } from './report.js';
1
+ export { createArchive } from './archive.js';
2
+ export { loadProjectConfig, resolveServerUrl } from './config.js';
3
+ export { send } from './deploy.js';
4
+ export { adaptfullyFromCli, runAdaptfullyStage } from './pipeline.js';
5
+ export { prebuildPlatform, prebuildOutputDir } from './prebuild.js';
6
+ export { getPackageRoot, getRuntimeDir, resolveRuntimeScript } from './paths.js';
7
+ export {
8
+ STANDARD_PLUGINS,
9
+ DEFAULT_BUILDER_PLATFORMS,
10
+ adaptfullyInjectionForPlatform,
11
+ buildAdaptfullyInjection,
12
+ collectRegistrationParts,
13
+ injectAdaptfullyRegistrations,
14
+ resolveBuilderForPlatform,
15
+ resolveCliPlatformAndBuilder,
16
+ resolvePlatformKey,
17
+ resolvePlatformRegistrationsByKey,
18
+ resolveRegistrationAssets,
19
+ } from './registrations.js';
20
+ export { printBuildReport } from './report.js';
@@ -0,0 +1,18 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
6
+ const RUNTIME_DIR = path.join(PACKAGE_ROOT, 'lib', 'runtime');
7
+
8
+ export function getPackageRoot() {
9
+ return PACKAGE_ROOT;
10
+ }
11
+
12
+ export function getRuntimeDir() {
13
+ return RUNTIME_DIR;
14
+ }
15
+
16
+ export function resolveRuntimeScript(relativePath) {
17
+ return path.join(RUNTIME_DIR, relativePath);
18
+ }
@@ -0,0 +1,77 @@
1
+ import { loadProjectConfig, resolveServerUrl } from './config.js';
2
+ import { send } from './deploy.js';
3
+ import { prebuildPlatform } from './prebuild.js';
4
+ import { resolveBuilderForPlatform } from './registrations.js';
5
+
6
+ /** @typedef {'prebuild' | 'build' | 'deploy'} AdaptfullyStage */
7
+
8
+ const VALID_STAGES = new Set(['prebuild', 'build', 'deploy']);
9
+
10
+ /**
11
+ * @param {AdaptfullyStage} stage
12
+ * @param {string} platformKey
13
+ * @param {{ pkg: object, deployFolder: string, server?: string, mode?: string, builder?: string, log?: (message: string) => void, outputRoot?: string }} options
14
+ */
15
+ export async function runAdaptfullyStage(stage, platformKey, options) {
16
+ if (!VALID_STAGES.has(stage)) {
17
+ throw new Error(`Unknown adaptfully stage "${stage}". Expected: prebuild, build, or deploy.`);
18
+ }
19
+
20
+ const log = options.log ?? console.log;
21
+ const { pkg, deployFolder } = options;
22
+
23
+ const prebuiltDir = prebuildPlatform(deployFolder, platformKey, pkg, {
24
+ log,
25
+ outputRoot: options.outputRoot,
26
+ });
27
+
28
+ if (stage === 'prebuild') {
29
+ return { prebuiltDir, platformKey };
30
+ }
31
+
32
+ const builder = options.builder ?? resolveBuilderForPlatform(platformKey, pkg);
33
+ log(`adaptfully: ${stage} ${platformKey} via Wrapfully builder "${builder}"`);
34
+
35
+ await send(
36
+ `${pkg.name}-${pkg.version}`,
37
+ JSON.stringify(pkg),
38
+ options.server,
39
+ builder,
40
+ prebuiltDir,
41
+ pkg,
42
+ options.mode ?? 'extract',
43
+ { log },
44
+ );
45
+
46
+ return { prebuiltDir, platformKey, builder };
47
+ }
48
+
49
+ /**
50
+ * @param {string[]} [argv=process.argv]
51
+ */
52
+ export async function adaptfullyFromCli(argv = process.argv) {
53
+ const stage = argv[2];
54
+ const platformKey = argv[3];
55
+ const cliServer = argv[4];
56
+ const mode = argv[5] ?? 'extract';
57
+
58
+ if (!stage || !platformKey) {
59
+ throw new Error(
60
+ 'Usage: adaptfully <prebuild|build|deploy> <platform> [server] [mode]\n'
61
+ + ' prebuild Copy deploy/ and apply platform registrations → output/<platform>-prebuild/\n'
62
+ + ' build prebuild + zip and send to Wrapfully\n'
63
+ + ' deploy build + platform release when credentials are present (via Wrapfully)',
64
+ );
65
+ }
66
+
67
+ const { pkg, wrapfullyConfig } = await loadProjectConfig();
68
+ const server = resolveServerUrl(wrapfullyConfig, cliServer);
69
+ const deployFolder = pkg.config?.deployFolder || 'deploy';
70
+
71
+ return runAdaptfullyStage(/** @type {AdaptfullyStage} */ (stage), platformKey, {
72
+ pkg,
73
+ deployFolder,
74
+ server,
75
+ mode,
76
+ });
77
+ }
@@ -0,0 +1,63 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {
4
+ adaptfullyInjectionForPlatform,
5
+ injectAdaptfullyRegistrations,
6
+ } from './registrations.js';
7
+ import { copyRecursiveSync, emptyDirSync, listHtmlFilesRecursive } from './fs-utils.js';
8
+
9
+ /** @typedef {'prebuild' | 'build' | 'deploy'} AdaptfullyStage */
10
+
11
+ /**
12
+ * @param {string} platformKey
13
+ * @param {{ config?: { platforms?: Record<string, unknown>, outputFolder?: string } }} pkg
14
+ * @param {string} [outputRoot='output']
15
+ */
16
+ export function prebuildOutputDir(platformKey, pkg, outputRoot = 'output') {
17
+ const outputFolder = pkg.config?.outputFolder || outputRoot;
18
+ return path.resolve(outputFolder, `${platformKey}-prebuild`);
19
+ }
20
+
21
+ /**
22
+ * @param {string} htmlPath
23
+ * @param {string} injection
24
+ */
25
+ export function injectAdaptfullyIntoHtmlFile(htmlPath, injection) {
26
+ const html = fs.readFileSync(htmlPath, 'utf8');
27
+ const updated = injectAdaptfullyRegistrations(html, injection);
28
+ if (updated !== html) {
29
+ fs.writeFileSync(htmlPath, updated);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * @param {string} deployFolder
35
+ * @param {string} platformKey
36
+ * @param {{ config?: { platforms?: Record<string, { registrations?: Record<string, string> }> } }} pkg
37
+ * @param {{ log?: (message: string) => void, outputRoot?: string }} [options]
38
+ * @returns {string} Absolute path to the prebuild output directory
39
+ */
40
+ export function prebuildPlatform(deployFolder, platformKey, pkg, options = {}) {
41
+ const log = options.log ?? console.log;
42
+ const source = path.resolve(deployFolder);
43
+
44
+ if (!fs.existsSync(source)) {
45
+ throw new Error(`Deploy folder not found: ${source}`);
46
+ }
47
+
48
+ const dest = prebuildOutputDir(platformKey, pkg, options.outputRoot);
49
+ log(`adaptfully: prebuild ${platformKey} → ${dest}`);
50
+
51
+ emptyDirSync(dest);
52
+ copyRecursiveSync(source, dest);
53
+
54
+ const injection = adaptfullyInjectionForPlatform(platformKey, pkg, { log });
55
+ if (injection) {
56
+ for (const htmlPath of listHtmlFilesRecursive(dest)) {
57
+ injectAdaptfullyIntoHtmlFile(htmlPath, injection);
58
+ }
59
+ }
60
+
61
+ log(`adaptfully: prebuild complete (${platformKey})`);
62
+ return dest;
63
+ }