@incajs/cli 0.0.0 → 0.0.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/LICENSE +5 -0
- package/README.md +108 -2
- package/bin/inca.mjs +7 -0
- package/dist/adapter/types.d.mts +47 -0
- package/dist/adapter/vite/build.d.mts +8 -0
- package/dist/adapter/vite/captureOutput.d.mts +15 -0
- package/dist/adapter/vite/config.d.mts +26 -0
- package/dist/adapter/vite/index.d.mts +2 -0
- package/dist/adapter/vite/watch.d.mts +8 -0
- package/dist/build.d.mts +18 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.js +481 -0
- package/dist/defaultBundler.d.mts +7 -0
- package/dist/dev-client/hostBin.d.mts +9 -0
- package/dist/dev-client/hostClient.d.mts +50 -0
- package/dist/dev-client/hostError.d.mts +6 -0
- package/dist/dev-client/index.d.mts +4 -0
- package/dist/dev-client/protocol.d.mts +38 -0
- package/dist/dev.d.mts +25 -0
- package/dist/entry.d.mts +8 -0
- package/dist/fault.d.mts +6 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.js +0 -0
- package/dist/metadata.d.mts +30 -0
- package/dist/package.d.mts +45 -0
- package/dist/plist.d.mts +4 -0
- package/package.json +58 -3
package/LICENSE
ADDED
package/README.md
CHANGED
|
@@ -1,2 +1,108 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) 2026 tom96da
|
|
3
|
+
SPDX-License-Identifier: MIT OR Apache-2.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# @incajs/cli
|
|
7
|
+
|
|
8
|
+
The `inca` command. Internally it wires a bundler adapter's build watch to
|
|
9
|
+
a dev-protocol client talking to `inca-host` — both live inside this
|
|
10
|
+
package (`src/adapter/`, `src/dev-client/`) since neither is ever imported
|
|
11
|
+
on its own.
|
|
12
|
+
|
|
13
|
+
`inca dev` watches an app's entry point, starts `inca-host` once the
|
|
14
|
+
first bundle lands, and reloads it on every rebuild. `inca build` runs
|
|
15
|
+
the same pipeline once, with the watcher removed and production settings
|
|
16
|
+
on, and never starts a host. `inca package` builds the same way, then
|
|
17
|
+
pairs the bundle with a prebuilt `inca-host` into a distributable
|
|
18
|
+
application. `adapter/vite` is the bundler wired in by default for all
|
|
19
|
+
three — swapping it for another `Bundler` (the contract `adapter/types.mts`
|
|
20
|
+
defines) is a dependency change in `defaultBundler.mts`, not an edit
|
|
21
|
+
anywhere else.
|
|
22
|
+
|
|
23
|
+
## App entry
|
|
24
|
+
|
|
25
|
+
No entry point is required. Drop a `src/App.vue` and that's a whole app —
|
|
26
|
+
`inca` wraps it in `createIncaApp(App).mount()` (from `incajs/vue`)
|
|
27
|
+
itself. Commit a `src/main.mts` instead for full control over
|
|
28
|
+
bootstrapping; it wins outright when both exist.
|
|
29
|
+
|
|
30
|
+
## `inca package`
|
|
31
|
+
|
|
32
|
+
Emits a platform-native application into `dist/`: a `.app` on macOS, a
|
|
33
|
+
plain directory on Linux — other platforms aren't supported yet. Either
|
|
34
|
+
way, the layout puts the host binary and `bundle.js` beside each other, so
|
|
35
|
+
the app launches with no arguments and no terminal.
|
|
36
|
+
|
|
37
|
+
It needs a release build of the host to bundle — see `resolveHostBin`
|
|
38
|
+
below for how one is found.
|
|
39
|
+
|
|
40
|
+
App metadata comes from the app's own `package.json`, with an optional
|
|
41
|
+
`"inca"` key overriding what's derived from it:
|
|
42
|
+
|
|
43
|
+
```jsonc
|
|
44
|
+
{
|
|
45
|
+
"name": "click_counter",
|
|
46
|
+
"version": "1.0.0",
|
|
47
|
+
"inca": {
|
|
48
|
+
"productName": "Click Counter", // defaults to "name", scope stripped
|
|
49
|
+
"identifier": "com.example.click-counter", // defaults to a generated org.inca.<slug>
|
|
50
|
+
"icon": "assets/icon.icns" // resolved relative to the app's own directory
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
An `identifier` should be world-unique, so `inca package` prints a note
|
|
56
|
+
when it falls back to the generated one rather than using it silently.
|
|
57
|
+
|
|
58
|
+
## `dev-client`
|
|
59
|
+
|
|
60
|
+
The Node end of the dev protocol: resolves and spawns `inca-host --dev
|
|
61
|
+
<bundle>`, and speaks the newline-delimited JSON-RPC 2.0 channel it
|
|
62
|
+
answers on.
|
|
63
|
+
|
|
64
|
+
`HostClient` correlates each request it sends with the response that
|
|
65
|
+
answers it and relays the host's stderr. Two notification methods carry
|
|
66
|
+
meaning of their own: `ready`, whose `params.protocol` it checks against
|
|
67
|
+
the revision it was built for, reporting and terminating the child on any
|
|
68
|
+
mismatch; and `appError`, an app fault the host caught and kept rendering
|
|
69
|
+
past, handed to its own callback rather than a generic one. Any other
|
|
70
|
+
method name is routed, `params` untouched and unparsed, to whichever
|
|
71
|
+
integration the caller registered for it.
|
|
72
|
+
|
|
73
|
+
A line the host writes that doesn't parse as a JSON-RPC message is treated
|
|
74
|
+
as stray output from a dependency, not a protocol violation: it's logged
|
|
75
|
+
and the channel keeps reading.
|
|
76
|
+
|
|
77
|
+
`resolveHostBin` tries, in order:
|
|
78
|
+
|
|
79
|
+
1. `INCA_HOST_BIN`, if set — names the binary to spawn outright. This is
|
|
80
|
+
the escape hatch for a platform with no published binary yet, or a
|
|
81
|
+
custom-built one.
|
|
82
|
+
2. The `optionalDependency` matching this OS/arch
|
|
83
|
+
(`@incajs/host-darwin-arm64`, `-darwin-x64`, `-linux-arm64`,
|
|
84
|
+
`-linux-x64` — Windows isn't supported yet), which almost every install
|
|
85
|
+
resolves through without either side ever needing Cargo or Rust.
|
|
86
|
+
|
|
87
|
+
@throws if neither resolves.
|
|
88
|
+
|
|
89
|
+
## `adapter/vite`
|
|
90
|
+
|
|
91
|
+
Bundles an app's entry point into one self-contained bundle that
|
|
92
|
+
`inca-host` can evaluate: no unresolved imports, no dependency on
|
|
93
|
+
QuickJS having Node.js globals.
|
|
94
|
+
|
|
95
|
+
`watch(options)` builds `options.entry` into a bundle under
|
|
96
|
+
`options.outDir` — the returned `Watcher`'s `bundlePath` names the exact
|
|
97
|
+
file — rebuilding it on every change and reporting each result through
|
|
98
|
+
`options.onBuild`/`options.onError`. `build(options)` runs the same
|
|
99
|
+
pipeline once, minified and with `outDir` cleared first, resolving with
|
|
100
|
+
the bundle's path or rejecting on failure rather than reporting it
|
|
101
|
+
through a callback.
|
|
102
|
+
|
|
103
|
+
The template compiler is retargeted at `@vue/runtime-core` — the only Vue
|
|
104
|
+
runtime package `incajs/vue` itself depends on — instead of the default
|
|
105
|
+
`vue` import. `@vitejs/plugin-vue` itself still imports `vue` directly for
|
|
106
|
+
its own internals, which is why it's a peer dependency of this package:
|
|
107
|
+
that build-time need for `vue` never reaches an app's dependency tree or
|
|
108
|
+
the bundle it produces.
|
package/bin/inca.mjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** A running build watch — the value {@link Bundler.watch} resolves to. */
|
|
2
|
+
export interface Watcher {
|
|
3
|
+
/** Stops watching and releases the underlying build process. */
|
|
4
|
+
close(): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/** Options for {@link Bundler.watch}. */
|
|
7
|
+
export interface BundlerOptions {
|
|
8
|
+
/** The app's own entry point — may import `.vue` files. */
|
|
9
|
+
entry: string;
|
|
10
|
+
/** Where the build's output is written. */
|
|
11
|
+
outDir: string;
|
|
12
|
+
/** `"development"` keeps a framework's own warnings; `"production"` strips them. */
|
|
13
|
+
mode: "development" | "production";
|
|
14
|
+
/** Called after each successful (re)build, with what it wrote. */
|
|
15
|
+
onBuild: (output: BuildOutput) => void;
|
|
16
|
+
/** Called instead of `onBuild` when a (re)build fails. */
|
|
17
|
+
onError: (error: {
|
|
18
|
+
message: string;
|
|
19
|
+
stack: string | null;
|
|
20
|
+
}) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Options for {@link Bundler.build}. */
|
|
23
|
+
export interface BuildOptions {
|
|
24
|
+
/** The app's own entry point — may import `.vue` files. */
|
|
25
|
+
entry: string;
|
|
26
|
+
/** Where the build's output is written. */
|
|
27
|
+
outDir: string;
|
|
28
|
+
}
|
|
29
|
+
/** What a build wrote — the result of {@link Bundler.build}, and every {@link BundlerOptions.onBuild} call. */
|
|
30
|
+
export interface BuildOutput {
|
|
31
|
+
/** Directory holding every file this build emitted. */
|
|
32
|
+
outDir: string;
|
|
33
|
+
/** Absolute path to the entry module `inca-host` evaluates. */
|
|
34
|
+
entryFile: string;
|
|
35
|
+
/** Every file this build emitted, relative to `outDir` — may include chunks and assets besides the entry itself. */
|
|
36
|
+
files: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The contract a bundler adapter satisfies. Supporting another bundler
|
|
40
|
+
* means adding a sibling adapter module and injecting its `Bundler` here —
|
|
41
|
+
* never editing this package's own types.
|
|
42
|
+
*/
|
|
43
|
+
export interface Bundler {
|
|
44
|
+
watch(options: BundlerOptions): Promise<Watcher>;
|
|
45
|
+
/** One-shot production build, used by `inca build` — rejects on failure. */
|
|
46
|
+
build(options: BuildOptions): Promise<BuildOutput>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BuildOptions, BuildOutput } from '../types.mts';
|
|
2
|
+
export type { BuildOptions, BuildOutput } from '../types.mts';
|
|
3
|
+
/**
|
|
4
|
+
* Builds `entry` into a minified, production build under `outDir` once, and
|
|
5
|
+
* rejects on failure rather than reporting it through a callback. Never
|
|
6
|
+
* starts or talks to `inca-host` — that's `dev.mts`'s job.
|
|
7
|
+
*/
|
|
8
|
+
export declare function build({ entry, outDir }: BuildOptions): Promise<BuildOutput>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
/** What one (re)build actually wrote, relative to its own `outDir`. */
|
|
3
|
+
export interface CapturedOutput {
|
|
4
|
+
/** The emitted chunk marked as this build's entry. */
|
|
5
|
+
entryFile: string;
|
|
6
|
+
/** Every emitted file, as the bundler reported it. */
|
|
7
|
+
files: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A Vite plugin reporting `sink` the files a build wrote once they land on
|
|
11
|
+
* disk, via `writeBundle` — the one hook that fires uniformly for a
|
|
12
|
+
* one-shot build and every watch rebuild, so both read the bundler's real
|
|
13
|
+
* output instead of assuming a file name or layout.
|
|
14
|
+
*/
|
|
15
|
+
export declare function captureOutput(sink: (output: CapturedOutput) => void): Plugin;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { InlineConfig } from 'vite';
|
|
2
|
+
import { CapturedOutput } from './captureOutput.mts';
|
|
3
|
+
export declare const BUNDLE_FILE_NAME = "bundle.js";
|
|
4
|
+
export interface ResolveConfigOptions {
|
|
5
|
+
/** The app's own entry point — may import `.vue` files. */
|
|
6
|
+
entry: string;
|
|
7
|
+
/** Where the build's output is written. */
|
|
8
|
+
outDir: string;
|
|
9
|
+
/** `"development"` keeps `@vue/runtime-core`'s own warnings; `"production"` strips them. */
|
|
10
|
+
mode: "development" | "production";
|
|
11
|
+
/** Whether Vite should keep rebuilding on file changes. */
|
|
12
|
+
watch: boolean;
|
|
13
|
+
/** Called with what each successful (re)build wrote. */
|
|
14
|
+
onOutput: (output: CapturedOutput) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds the Vite config shared by `watch` and `build`, compiling `.vue`
|
|
18
|
+
* files via `@vitejs/plugin-vue` targeted at `@vue/runtime-core` rather than
|
|
19
|
+
* the `vue` meta-package it requires to run.
|
|
20
|
+
*
|
|
21
|
+
* An import the entry doesn't inline — a dynamic `import()`, or a `.vue`
|
|
22
|
+
* file's own `<style>` block — lands as its own chunk or asset alongside
|
|
23
|
+
* the entry, named by `chunkFileNames`/`assetFileNames` below, rather than
|
|
24
|
+
* failing the build.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveViteConfig({ entry, outDir, mode, watch, onOutput, }: ResolveConfigOptions): InlineConfig;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BundlerOptions, Watcher } from '../types.mts';
|
|
2
|
+
export type { Watcher } from '../types.mts';
|
|
3
|
+
/**
|
|
4
|
+
* Builds `entry` into a build under `outDir` and rebuilds it on every
|
|
5
|
+
* change, calling `onBuild` with what each rebuild wrote. Never starts,
|
|
6
|
+
* reloads, or talks to `inca-host` — that's `dev.mts`'s job.
|
|
7
|
+
*/
|
|
8
|
+
export declare function watch({ onBuild, onError, ...buildOptions }: BundlerOptions): Promise<Watcher>;
|
package/dist/build.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Bundler, BuildOutput } from './adapter/types.mts';
|
|
2
|
+
export interface BuildAppOptions {
|
|
3
|
+
/** The app's root directory. Defaults to `process.cwd()`. */
|
|
4
|
+
cwd?: string;
|
|
5
|
+
/**
|
|
6
|
+
* The app's entry point. Defaults to resolving it automatically: a
|
|
7
|
+
* committed `src/main.mts`, or `src/App.vue` wrapped in a synthesized one.
|
|
8
|
+
*/
|
|
9
|
+
entry?: string;
|
|
10
|
+
/** Overrides the bundler — see {@link defaultBundler} for what's wired in by default. */
|
|
11
|
+
bundler?: Bundler;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Builds the app once through the same bundler `dev` uses, and returns what
|
|
15
|
+
* it wrote. Rejects on failure — deciding what that means for the process
|
|
16
|
+
* is `cli.mts`'s job.
|
|
17
|
+
*/
|
|
18
|
+
export declare function build(options?: BuildAppOptions): Promise<BuildOutput>;
|
package/dist/cli.d.mts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import e from "node:path";
|
|
2
|
+
import { build as t } from "vite";
|
|
3
|
+
import n from "@vitejs/plugin-vue";
|
|
4
|
+
import { existsSync as r } from "node:fs";
|
|
5
|
+
import { chmod as i, cp as a, mkdir as o, readFile as s, readdir as c, rm as l, writeFile as u } from "node:fs/promises";
|
|
6
|
+
import { createRequire as d } from "node:module";
|
|
7
|
+
import { spawn as f } from "node:child_process";
|
|
8
|
+
import { once as p } from "node:events";
|
|
9
|
+
import m from "node:readline";
|
|
10
|
+
import { setTimeout as h } from "node:timers/promises";
|
|
11
|
+
//#region src/adapter/vite/captureOutput.mts
|
|
12
|
+
function g(e) {
|
|
13
|
+
return {
|
|
14
|
+
name: "inca:capture-output",
|
|
15
|
+
writeBundle(t, n) {
|
|
16
|
+
let r = Object.keys(n), i = Object.values(n).find((e) => e.type === "chunk" && e.isEntry);
|
|
17
|
+
if (!i) throw Error("inca: build produced no entry chunk");
|
|
18
|
+
e({
|
|
19
|
+
entryFile: i.fileName,
|
|
20
|
+
files: r
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/adapter/vite/config.mts
|
|
27
|
+
var _ = "bundle.js";
|
|
28
|
+
function v({ entry: t, outDir: r, mode: i, watch: a, onOutput: o }) {
|
|
29
|
+
return {
|
|
30
|
+
configFile: !1,
|
|
31
|
+
root: e.dirname(t),
|
|
32
|
+
mode: i,
|
|
33
|
+
clearScreen: !1,
|
|
34
|
+
logLevel: "silent",
|
|
35
|
+
define: { "process.env.NODE_ENV": JSON.stringify(i) },
|
|
36
|
+
plugins: [n({ template: { compilerOptions: { runtimeModuleName: "@vue/runtime-core" } } }), g(o)],
|
|
37
|
+
build: {
|
|
38
|
+
lib: {
|
|
39
|
+
entry: t,
|
|
40
|
+
formats: ["es"],
|
|
41
|
+
fileName: () => _
|
|
42
|
+
},
|
|
43
|
+
outDir: r,
|
|
44
|
+
emptyOutDir: i === "production",
|
|
45
|
+
minify: i === "production",
|
|
46
|
+
watch: a ? {} : void 0,
|
|
47
|
+
rolldownOptions: { output: {
|
|
48
|
+
chunkFileNames: "chunks/[name]-[hash].js",
|
|
49
|
+
assetFileNames: "assets/[name]-[hash][extname]"
|
|
50
|
+
} }
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/adapter/vite/build.mts
|
|
56
|
+
async function y({ entry: n, outDir: r }) {
|
|
57
|
+
let i;
|
|
58
|
+
if (await t(v({
|
|
59
|
+
entry: n,
|
|
60
|
+
outDir: r,
|
|
61
|
+
mode: "production",
|
|
62
|
+
watch: !1,
|
|
63
|
+
onOutput: (t) => {
|
|
64
|
+
i = {
|
|
65
|
+
outDir: r,
|
|
66
|
+
entryFile: e.join(r, t.entryFile),
|
|
67
|
+
files: t.files
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
})), !i) throw Error("inca: build produced no output");
|
|
71
|
+
return i;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/adapter/vite/watch.mts
|
|
75
|
+
async function b({ onBuild: n, onError: r, ...i }) {
|
|
76
|
+
let { outDir: a } = i, o = await t(v({
|
|
77
|
+
...i,
|
|
78
|
+
watch: !0,
|
|
79
|
+
onOutput: (t) => {
|
|
80
|
+
n({
|
|
81
|
+
outDir: a,
|
|
82
|
+
entryFile: e.join(a, t.entryFile),
|
|
83
|
+
files: t.files
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
return o.on("event", (e) => {
|
|
88
|
+
e.code === "ERROR" && r({
|
|
89
|
+
message: e.error.message,
|
|
90
|
+
stack: e.error.stack ?? null
|
|
91
|
+
});
|
|
92
|
+
}), { close: () => o.close() };
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/defaultBundler.mts
|
|
96
|
+
var x = {
|
|
97
|
+
watch: b,
|
|
98
|
+
build: y
|
|
99
|
+
};
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/entry.mts
|
|
102
|
+
async function S(t) {
|
|
103
|
+
let n = e.join(t, "src/main.mts");
|
|
104
|
+
if (r(n)) return n;
|
|
105
|
+
let i = e.join(t, "src/App.vue");
|
|
106
|
+
if (r(i)) return C(i, t);
|
|
107
|
+
throw Error(`no app entry found — expected ${n} or ${i}`);
|
|
108
|
+
}
|
|
109
|
+
async function C(t, n) {
|
|
110
|
+
let r = e.join(n, "node_modules/.inca");
|
|
111
|
+
await o(r, { recursive: !0 });
|
|
112
|
+
let i = e.join(r, "entry.mts"), a = JSON.stringify(t.split(e.sep).join("/"));
|
|
113
|
+
return await u(i, `import { createIncaApp } from "incajs/vue";\nimport App from ${a};\ncreateIncaApp(App).mount();\n`), i;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/build.mts
|
|
117
|
+
async function w(t = {}) {
|
|
118
|
+
let n = t.cwd ?? process.cwd(), r = e.join(n, "dist"), i = t.bundler ?? x, a = t.entry ?? await S(n);
|
|
119
|
+
return i.build({
|
|
120
|
+
entry: a,
|
|
121
|
+
outDir: r
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/dev-client/hostBin.mts
|
|
126
|
+
var T = "INCA_HOST_BIN";
|
|
127
|
+
function E() {
|
|
128
|
+
let e = process.platform === "darwin" || process.platform === "linux" ? process.platform : void 0, t = process.arch === "arm64" || process.arch === "x64" ? process.arch : void 0;
|
|
129
|
+
return e && t ? `@incajs/host-${e}-${t}` : void 0;
|
|
130
|
+
}
|
|
131
|
+
function D() {
|
|
132
|
+
let e = E();
|
|
133
|
+
if (e) try {
|
|
134
|
+
return d(import.meta.url).resolve(`${e}/bin/inca-host`);
|
|
135
|
+
} catch {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function O() {
|
|
140
|
+
let e = process.env[T];
|
|
141
|
+
if (e) return e;
|
|
142
|
+
let t = D();
|
|
143
|
+
if (t) return t;
|
|
144
|
+
let n = E(), r = n ? `no ${n} package is installed for it` : `this platform (${process.platform}/${process.arch}) has no published package`;
|
|
145
|
+
throw Error(`no inca-host binary found — ${r}, and ${T} isn't set to one`);
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/dev-client/hostError.mts
|
|
149
|
+
var k = class extends Error {
|
|
150
|
+
code;
|
|
151
|
+
hostStack;
|
|
152
|
+
constructor(e, t, n = null) {
|
|
153
|
+
super(e), this.code = t, this.hostStack = n, this.name = "HostError";
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
function A(e) {
|
|
157
|
+
return typeof e == "object" && !!e && e.jsonrpc === "2.0";
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/dev-client/hostClient.mts
|
|
161
|
+
var j = (e) => {
|
|
162
|
+
process.stderr.write(e.endsWith("\n") ? e : `${e}\n`);
|
|
163
|
+
}, M = class {
|
|
164
|
+
#e;
|
|
165
|
+
#t;
|
|
166
|
+
#n = 1;
|
|
167
|
+
#r = /* @__PURE__ */ new Map();
|
|
168
|
+
constructor(e) {
|
|
169
|
+
this.#e = e;
|
|
170
|
+
}
|
|
171
|
+
async start() {
|
|
172
|
+
let e = this.#e.hostBin ?? O(), t = f(e, ["--dev", this.#e.entryFile], { stdio: [
|
|
173
|
+
"pipe",
|
|
174
|
+
"pipe",
|
|
175
|
+
"pipe"
|
|
176
|
+
] });
|
|
177
|
+
this.#t = t;
|
|
178
|
+
let n = this.#e.onStderr ?? j;
|
|
179
|
+
t.stderr.setEncoding("utf8"), t.stderr.on("data", (e) => n(e)), m.createInterface({ input: t.stdout }).on("line", (e) => {
|
|
180
|
+
this.#i(e, n);
|
|
181
|
+
}), t.on("exit", (e, t) => {
|
|
182
|
+
let n = /* @__PURE__ */ Error(`inca-host exited (code=${String(e)}, signal=${String(t)}) before answering`);
|
|
183
|
+
for (let { reject: e } of this.#r.values()) e(n);
|
|
184
|
+
this.#r.clear();
|
|
185
|
+
}), await p(t, "spawn");
|
|
186
|
+
}
|
|
187
|
+
#i(e, t) {
|
|
188
|
+
let n;
|
|
189
|
+
try {
|
|
190
|
+
n = JSON.parse(e);
|
|
191
|
+
} catch {
|
|
192
|
+
t(`[stray stdout] ${e}`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!A(n)) {
|
|
196
|
+
t(`[stray stdout] ${e}`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if ("method" in n) {
|
|
200
|
+
this.#a(n.method, n.params, t);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let r = this.#r.get(n.id);
|
|
204
|
+
r && (this.#r.delete(n.id), "error" in n ? r.reject(new k(n.error.message, n.error.code, n.error.data?.stack ?? null)) : r.resolve(n.result));
|
|
205
|
+
}
|
|
206
|
+
#a(e, t, n) {
|
|
207
|
+
switch (e) {
|
|
208
|
+
case "ready": {
|
|
209
|
+
let { protocol: e } = t;
|
|
210
|
+
if (e !== 0) {
|
|
211
|
+
n(`inca-host speaks protocol ${String(e)}, this package was built for 0 — stopping it rather than carrying on`), this.#t && this.#o(this.#t);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.#e.onReady?.();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
case "appError":
|
|
218
|
+
this.#e.onAppError?.(t);
|
|
219
|
+
return;
|
|
220
|
+
default: {
|
|
221
|
+
let r = this.#e.integrations?.[e];
|
|
222
|
+
r ? r(t) : n(`[unhandled notification] ${e}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async #o(e) {
|
|
227
|
+
let t = p(e, "exit");
|
|
228
|
+
e.kill(), await t;
|
|
229
|
+
}
|
|
230
|
+
async call(e, t) {
|
|
231
|
+
let n = this.#t;
|
|
232
|
+
if (!n) throw Error("HostClient.start() has not been called");
|
|
233
|
+
let r = this.#n++, i = JSON.stringify({
|
|
234
|
+
jsonrpc: "2.0",
|
|
235
|
+
id: r,
|
|
236
|
+
method: e,
|
|
237
|
+
params: t
|
|
238
|
+
});
|
|
239
|
+
return new Promise((e, t) => {
|
|
240
|
+
this.#r.set(r, {
|
|
241
|
+
resolve: e,
|
|
242
|
+
reject: t
|
|
243
|
+
}), n.stdin.write(`${i}\n`);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async stop(e = 2e3) {
|
|
247
|
+
let t = this.#t;
|
|
248
|
+
if (!t || t.exitCode !== null || t.signalCode !== null) return;
|
|
249
|
+
let n = p(t, "exit");
|
|
250
|
+
this.call("shutdown").catch(() => {}), await Promise.race([n.then(() => "exited"), h(e).then(() => "timeout")]) === "timeout" && (t.kill(), await n);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/fault.mts
|
|
255
|
+
function N(e) {
|
|
256
|
+
return e instanceof k ? {
|
|
257
|
+
message: e.message,
|
|
258
|
+
stack: e.hostStack
|
|
259
|
+
} : e instanceof Error ? {
|
|
260
|
+
message: e.message,
|
|
261
|
+
stack: e.stack ?? null
|
|
262
|
+
} : {
|
|
263
|
+
message: String(e),
|
|
264
|
+
stack: null
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function P(e, t, n) {
|
|
268
|
+
e.write(`[inca] ${t}: ${n.message}\n`), n.stack && e.write(`${n.stack}\n`);
|
|
269
|
+
}
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/dev.mts
|
|
272
|
+
async function F(t, n) {
|
|
273
|
+
let r = new Set(n), i;
|
|
274
|
+
try {
|
|
275
|
+
i = await c(t, {
|
|
276
|
+
recursive: !0,
|
|
277
|
+
withFileTypes: !0
|
|
278
|
+
});
|
|
279
|
+
} catch {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
await Promise.all(i.filter((e) => e.isFile()).map((n) => e.relative(t, e.join(n.parentPath, n.name))).filter((e) => !r.has(e)).map((n) => l(e.join(t, n), { force: !0 })));
|
|
283
|
+
}
|
|
284
|
+
async function I(t) {
|
|
285
|
+
let n = t.cwd ?? process.cwd(), r = e.join(n, "dist"), i = t.bundler ?? x, a = t.stdout ?? process.stdout, o = t.stderr ?? process.stderr, s = t.entry ?? await S(n), c, l = !1, u = !1, d = Promise.resolve();
|
|
286
|
+
async function f() {
|
|
287
|
+
try {
|
|
288
|
+
await c?.call("reload");
|
|
289
|
+
} catch (e) {
|
|
290
|
+
P(o, "reload failed", N(e));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function p(e) {
|
|
294
|
+
if (await F(e.outDir, e.files), !c) {
|
|
295
|
+
let n = new M({
|
|
296
|
+
entryFile: e.entryFile,
|
|
297
|
+
hostBin: t.hostBin,
|
|
298
|
+
onStderr: (e) => o.write(e),
|
|
299
|
+
onReady: () => {
|
|
300
|
+
l = !0, a.write("[inca] ready\n"), u && (u = !1, f());
|
|
301
|
+
},
|
|
302
|
+
onAppError: (e) => P(o, "app error", e)
|
|
303
|
+
});
|
|
304
|
+
try {
|
|
305
|
+
await n.start();
|
|
306
|
+
} catch (e) {
|
|
307
|
+
P(o, "failed to start inca-host", N(e));
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
c = n;
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (!l) {
|
|
314
|
+
u = !0;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
await f();
|
|
318
|
+
}
|
|
319
|
+
let m = await i.watch({
|
|
320
|
+
entry: s,
|
|
321
|
+
outDir: r,
|
|
322
|
+
mode: "development",
|
|
323
|
+
onBuild: (e) => {
|
|
324
|
+
d = d.then(() => p(e));
|
|
325
|
+
},
|
|
326
|
+
onError: (e) => {
|
|
327
|
+
P(o, "build failed", e);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
await new Promise((e) => {
|
|
331
|
+
if (t.signal.aborted) {
|
|
332
|
+
e();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
t.signal.addEventListener("abort", () => e(), { once: !0 });
|
|
336
|
+
}), await d, await c?.stop(), await m.close();
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/metadata.mts
|
|
340
|
+
function L(e) {
|
|
341
|
+
let t = e.indexOf("/");
|
|
342
|
+
return e.startsWith("@") && t !== -1 ? e.slice(t + 1) : e;
|
|
343
|
+
}
|
|
344
|
+
function R(e) {
|
|
345
|
+
return e.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
346
|
+
}
|
|
347
|
+
async function z(t) {
|
|
348
|
+
let n = e.join(t, "package.json"), i;
|
|
349
|
+
try {
|
|
350
|
+
i = await s(n, "utf8");
|
|
351
|
+
} catch {
|
|
352
|
+
throw Error(`no package.json found at ${n}`);
|
|
353
|
+
}
|
|
354
|
+
let a = JSON.parse(i), o = a.inca ?? {}, c = o.productName ?? (a.name ? L(a.name) : void 0);
|
|
355
|
+
if (!c) throw Error(`${n} needs a "name", or "inca": { "productName" }`);
|
|
356
|
+
let l = a.version ?? "0.0.0", u = o.identifier ?? `org.inca.${R(c)}`, d;
|
|
357
|
+
if (o.icon && (d = e.resolve(t, o.icon), !r(d))) throw Error(`"inca.icon" points to ${d}, which doesn't exist`);
|
|
358
|
+
return {
|
|
359
|
+
productName: c,
|
|
360
|
+
identifier: u,
|
|
361
|
+
identifierIsDefault: o.identifier === void 0,
|
|
362
|
+
version: l,
|
|
363
|
+
icon: d
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/plist.mts
|
|
368
|
+
function B(e) {
|
|
369
|
+
return e.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
370
|
+
}
|
|
371
|
+
function V(e) {
|
|
372
|
+
return typeof e == "boolean" ? e ? "<true/>" : "<false/>" : `<string>${B(e)}</string>`;
|
|
373
|
+
}
|
|
374
|
+
function H(e) {
|
|
375
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
376
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
377
|
+
<plist version="1.0">
|
|
378
|
+
<dict>
|
|
379
|
+
${Object.entries(e).map(([e, t]) => `\t<key>${B(e)}</key>\n\t${V(t)}`).join("\n")}\n</dict>
|
|
380
|
+
</plist>
|
|
381
|
+
`;
|
|
382
|
+
}
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/package.mts
|
|
385
|
+
function U() {
|
|
386
|
+
if (process.platform === "darwin") return "macos";
|
|
387
|
+
if (process.platform === "linux") return "linux";
|
|
388
|
+
throw Error(`inca package doesn't support ${process.platform} yet`);
|
|
389
|
+
}
|
|
390
|
+
async function W(t, n) {
|
|
391
|
+
await Promise.all(t.files.map(async (r) => {
|
|
392
|
+
let i = e.join(n, r);
|
|
393
|
+
await o(e.dirname(i), { recursive: !0 }), await a(e.join(t.outDir, r), i);
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
async function G({ metadata: t, output: n, hostBin: r }) {
|
|
397
|
+
let s = e.join(n.outDir, `${t.productName}.app`);
|
|
398
|
+
await l(s, {
|
|
399
|
+
recursive: !0,
|
|
400
|
+
force: !0
|
|
401
|
+
});
|
|
402
|
+
let c = e.join(s, "Contents"), d = e.join(c, "MacOS"), f = e.join(c, "Resources");
|
|
403
|
+
await o(d, { recursive: !0 }), await o(f, { recursive: !0 });
|
|
404
|
+
let p = e.join(d, t.productName);
|
|
405
|
+
await a(r, p), await i(p, 493), await W(n, f);
|
|
406
|
+
let m = {
|
|
407
|
+
CFBundleName: t.productName,
|
|
408
|
+
CFBundleDisplayName: t.productName,
|
|
409
|
+
CFBundleExecutable: t.productName,
|
|
410
|
+
CFBundleIdentifier: t.identifier,
|
|
411
|
+
CFBundleVersion: t.version,
|
|
412
|
+
CFBundleShortVersionString: t.version,
|
|
413
|
+
CFBundlePackageType: "APPL",
|
|
414
|
+
CFBundleInfoDictionaryVersion: "6.0",
|
|
415
|
+
NSHighResolutionCapable: !0
|
|
416
|
+
};
|
|
417
|
+
if (t.icon) {
|
|
418
|
+
let n = e.basename(t.icon);
|
|
419
|
+
await a(t.icon, e.join(f, n)), m.CFBundleIconFile = n;
|
|
420
|
+
}
|
|
421
|
+
return await u(e.join(c, "Info.plist"), H(m)), await u(e.join(c, "PkgInfo"), "APPL????"), s;
|
|
422
|
+
}
|
|
423
|
+
async function K({ metadata: t, output: n, hostBin: r }) {
|
|
424
|
+
let s = R(t.productName), c = e.join(n.outDir, s);
|
|
425
|
+
await l(c, {
|
|
426
|
+
recursive: !0,
|
|
427
|
+
force: !0
|
|
428
|
+
}), await o(c, { recursive: !0 });
|
|
429
|
+
let u = e.join(c, s);
|
|
430
|
+
return await a(r, u), await i(u, 493), await W(n, c), c;
|
|
431
|
+
}
|
|
432
|
+
async function q(e = {}) {
|
|
433
|
+
let t = e.cwd ?? process.cwd(), n = e.stdout ?? process.stdout, i = e.target ?? U(), a = await z(t);
|
|
434
|
+
a.identifierIsDefault && n.write(`[inca] no "inca.identifier" set in package.json — using generated identifier ${a.identifier}\n`);
|
|
435
|
+
let o = await w({
|
|
436
|
+
cwd: t,
|
|
437
|
+
entry: e.entry,
|
|
438
|
+
bundler: e.bundler
|
|
439
|
+
}), s = e.hostBin ?? O();
|
|
440
|
+
if (!r(s)) throw Error(`no host binary at ${s} — check that it was built and is executable`);
|
|
441
|
+
let c = {
|
|
442
|
+
metadata: a,
|
|
443
|
+
output: o,
|
|
444
|
+
hostBin: s
|
|
445
|
+
};
|
|
446
|
+
return { appPath: i === "macos" ? await G(c) : await K(c) };
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/cli.mts
|
|
450
|
+
var J = "Usage: inca <dev|build|package>";
|
|
451
|
+
async function Y(e = process.argv) {
|
|
452
|
+
let t = e[2];
|
|
453
|
+
if (t === "build") {
|
|
454
|
+
try {
|
|
455
|
+
let e = await w();
|
|
456
|
+
process.stdout.write(`[inca] built ${e.entryFile}\n`);
|
|
457
|
+
} catch (e) {
|
|
458
|
+
P(process.stderr, "build failed", N(e)), process.exitCode = 1;
|
|
459
|
+
}
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (t === "package") {
|
|
463
|
+
try {
|
|
464
|
+
let { appPath: e } = await q();
|
|
465
|
+
process.stdout.write(`[inca] packaged ${e}\n`);
|
|
466
|
+
} catch (e) {
|
|
467
|
+
P(process.stderr, "package failed", N(e)), process.exitCode = 1;
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (t !== "dev") {
|
|
472
|
+
process.stderr.write(`${J}\n`), process.exitCode = 1;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
let n = new AbortController(), r = () => {
|
|
476
|
+
process.stderr.write("[inca] shutting down\n"), n.abort();
|
|
477
|
+
};
|
|
478
|
+
process.on("SIGINT", r), process.on("SIGTERM", r), await I({ signal: n.signal });
|
|
479
|
+
}
|
|
480
|
+
//#endregion
|
|
481
|
+
export { Y as run };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Bundler } from './adapter/types.mts';
|
|
2
|
+
/**
|
|
3
|
+
* `adapter/vite` wired in as the one adapter `dev` and `build` share by
|
|
4
|
+
* default — a single place to swap bundlers, and the reason they can't
|
|
5
|
+
* drift onto different ones.
|
|
6
|
+
*/
|
|
7
|
+
export declare const defaultBundler: Bundler;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where `inca-host` itself lives: `INCA_HOST_BIN` if set, otherwise the
|
|
3
|
+
* per-platform npm package for this OS/arch (`@incajs/host-<os>-<arch>`, an
|
|
4
|
+
* `optionalDependency` of this package).
|
|
5
|
+
* @throws if neither resolves — no override is set, and either this
|
|
6
|
+
* platform has no published binary, or the optional dependency carrying
|
|
7
|
+
* it failed to install.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveHostBin(): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AppErrorParams } from './protocol.mts';
|
|
2
|
+
export interface HostClientOptions {
|
|
3
|
+
/** The entry module passed to the host as `--dev <entryFile>`. */
|
|
4
|
+
entryFile: string;
|
|
5
|
+
/** Overrides which `inca-host` binary gets spawned, in place of automatic resolution. */
|
|
6
|
+
hostBin?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Every diagnostic line the transport itself produces: the host's real
|
|
9
|
+
* stderr, a stray stdout line, and a `ready` protocol mismatch. Defaults
|
|
10
|
+
* to this process's own stderr.
|
|
11
|
+
*/
|
|
12
|
+
onStderr?: (line: string) => void;
|
|
13
|
+
/**
|
|
14
|
+
* The window is up and the first bundle has been evaluated, and the host
|
|
15
|
+
* speaks the protocol revision this package was built for.
|
|
16
|
+
*/
|
|
17
|
+
onReady?: () => void;
|
|
18
|
+
/** An app's own event listener threw; the host caught it and kept rendering. */
|
|
19
|
+
onAppError?: (error: AppErrorParams) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Handlers for notification `method`s this package doesn't implement
|
|
22
|
+
* itself (e.g. a future Vite integration), keyed by method name —
|
|
23
|
+
* `params` forwarded untouched.
|
|
24
|
+
*/
|
|
25
|
+
integrations?: Record<string, (params: unknown) => void>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Spawns `inca-host --dev <entryFile>` and speaks newline-delimited
|
|
29
|
+
* JSON-RPC 2.0 on its stdin/stdout. One instance owns exactly one child
|
|
30
|
+
* process for its whole lifetime — a reload is a `reload` call on the same
|
|
31
|
+
* child, never a respawn.
|
|
32
|
+
*/
|
|
33
|
+
export declare class HostClient {
|
|
34
|
+
#private;
|
|
35
|
+
constructor(options: HostClientOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Starts the child and wires up its streams. Resolves once the process
|
|
38
|
+
* has actually spawned; rejects if it never does (e.g. the resolved
|
|
39
|
+
* binary doesn't exist).
|
|
40
|
+
*/
|
|
41
|
+
start(): Promise<void>;
|
|
42
|
+
/** Sends a request and resolves with its result once the host answers. */
|
|
43
|
+
call(method: string, params?: unknown): Promise<unknown>;
|
|
44
|
+
/**
|
|
45
|
+
* Asks the host to `shutdown` and waits for the child to actually exit —
|
|
46
|
+
* that exit is the real acknowledgement, not the response. Kills it once
|
|
47
|
+
* `timeoutMs` passes without that, so a wedged app can't block teardown.
|
|
48
|
+
*/
|
|
49
|
+
stop(timeoutMs?: number): Promise<void>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const JSONRPC = "2.0";
|
|
2
|
+
/**
|
|
3
|
+
* The host method-set revision this package expects, checked against
|
|
4
|
+
* `ready`'s `params.protocol` — a mismatch means an incompatible host
|
|
5
|
+
* build.
|
|
6
|
+
*/
|
|
7
|
+
export declare const HOST_PROTOCOL_VERSION = 0;
|
|
8
|
+
/** `ready`'s notification params. */
|
|
9
|
+
export interface ReadyParams {
|
|
10
|
+
protocol: number;
|
|
11
|
+
}
|
|
12
|
+
/** `appError`'s notification params. */
|
|
13
|
+
export interface AppErrorParams {
|
|
14
|
+
message: string;
|
|
15
|
+
stack: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface RpcResult {
|
|
18
|
+
jsonrpc: typeof JSONRPC;
|
|
19
|
+
id: number;
|
|
20
|
+
result: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface RpcFailure {
|
|
23
|
+
jsonrpc: typeof JSONRPC;
|
|
24
|
+
id: number;
|
|
25
|
+
error: {
|
|
26
|
+
code: number;
|
|
27
|
+
message: string;
|
|
28
|
+
data?: {
|
|
29
|
+
stack: string | null;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface RpcNotification {
|
|
34
|
+
jsonrpc: typeof JSONRPC;
|
|
35
|
+
method: string;
|
|
36
|
+
params: unknown;
|
|
37
|
+
}
|
|
38
|
+
export declare function isRpcMessage(value: unknown): value is RpcResult | RpcFailure | RpcNotification;
|
package/dist/dev.d.mts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Bundler } from './adapter/types.mts';
|
|
2
|
+
export interface DevOptions {
|
|
3
|
+
/** The app's root directory. Defaults to `process.cwd()`. */
|
|
4
|
+
cwd?: string;
|
|
5
|
+
/**
|
|
6
|
+
* The app's entry point. Defaults to resolving it automatically: a
|
|
7
|
+
* committed `src/main.mts`, or `src/App.vue` wrapped in a synthesized one.
|
|
8
|
+
*/
|
|
9
|
+
entry?: string;
|
|
10
|
+
/** Overrides the bundler — see {@link defaultBundler} for what's wired in by default. */
|
|
11
|
+
bundler?: Bundler;
|
|
12
|
+
/** Overrides host binary resolution — passed straight through to `HostClient`. */
|
|
13
|
+
hostBin?: string;
|
|
14
|
+
/** Defaults to `process.stdout`. */
|
|
15
|
+
stdout?: NodeJS.WritableStream;
|
|
16
|
+
/** Defaults to `process.stderr`. */
|
|
17
|
+
stderr?: NodeJS.WritableStream;
|
|
18
|
+
/** Aborting tears the host and the bundler watcher down and resolves `dev()`. */
|
|
19
|
+
signal: AbortSignal;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Builds the app, starts `inca-host` once the first build lands, and
|
|
23
|
+
* reloads it on every rebuild — until `options.signal` aborts.
|
|
24
|
+
*/
|
|
25
|
+
export declare function dev(options: DevOptions): Promise<void>;
|
package/dist/entry.d.mts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves an app's entry point. `src/main.mts`, committed by the app,
|
|
3
|
+
* wins outright and is used verbatim — full control over bootstrapping.
|
|
4
|
+
* Otherwise `src/App.vue` is wrapped in a synthesized entry equivalent to
|
|
5
|
+
* `createIncaApp(App).mount()`, so a plain `.vue` file is a whole app
|
|
6
|
+
* with no configuration at all.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveEntry(cwd: string): Promise<string>;
|
package/dist/fault.d.mts
ADDED
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { BuildOptions, BuildOutput, Bundler, BundlerOptions, Watcher, } from './adapter/types.mts';
|
package/dist/index.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** An app's identity and version, for `inca package` to stamp onto the application it emits. */
|
|
2
|
+
export interface AppMetadata {
|
|
3
|
+
/** Shown to the user — the app's display name. */
|
|
4
|
+
productName: string;
|
|
5
|
+
/** A reverse-DNS-style unique id — e.g. macOS's `CFBundleIdentifier`. */
|
|
6
|
+
identifier: string;
|
|
7
|
+
/** True when `identifier` was derived rather than set by the app. */
|
|
8
|
+
identifierIsDefault: boolean;
|
|
9
|
+
/** The packaged app's version. */
|
|
10
|
+
version: string;
|
|
11
|
+
/** Absolute path to an icon file, if the app declared one. */
|
|
12
|
+
icon?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Turns a display name into a filesystem/URL-safe slug — lowercased, with
|
|
16
|
+
* anything outside `[a-z0-9]` collapsed into a single `-`. Used for the
|
|
17
|
+
* default `identifier` and for the Linux packaged-app directory name.
|
|
18
|
+
*/
|
|
19
|
+
export declare function slugify(name: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Reads an app's package metadata for `inca package`: `productName`,
|
|
22
|
+
* `identifier`, `version`, and an optional `icon`. Everything is derived
|
|
23
|
+
* from the app's own `package.json` — its `name`/`version` fields, and an
|
|
24
|
+
* optional `"inca"` key overriding any of them:
|
|
25
|
+
* `{ "inca": { "productName", "identifier", "icon" } }`.
|
|
26
|
+
*
|
|
27
|
+
* @throws if `package.json` is missing, has neither a `name` nor a
|
|
28
|
+
* `"inca".productName`, or `"inca".icon` doesn't resolve to a real file.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readAppMetadata(cwd: string): Promise<AppMetadata>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Bundler } from './adapter/types.mts';
|
|
2
|
+
/** A platform `inca package` can emit a distributable application for. */
|
|
3
|
+
export type PackageTarget = "macos" | "linux";
|
|
4
|
+
/** Options for {@link packageApp}. */
|
|
5
|
+
export interface PackageAppOptions {
|
|
6
|
+
/** The app's root directory. Defaults to `process.cwd()`. */
|
|
7
|
+
cwd?: string;
|
|
8
|
+
/**
|
|
9
|
+
* The app's entry point. Defaults to resolving it the same way `inca
|
|
10
|
+
* dev`/`build` do: a committed `src/main.mts`, or `src/App.vue` wrapped
|
|
11
|
+
* in a synthesized one.
|
|
12
|
+
*/
|
|
13
|
+
entry?: string;
|
|
14
|
+
/** Overrides the bundler — see {@link build}'s default. */
|
|
15
|
+
bundler?: Bundler;
|
|
16
|
+
/**
|
|
17
|
+
* Overrides which `inca-host` binary gets embedded in the packaged
|
|
18
|
+
* app, in place of automatic resolution (`INCA_HOST_BIN`, then the
|
|
19
|
+
* per-platform `@incajs/host-*` package — see {@link resolveHostBin}).
|
|
20
|
+
*/
|
|
21
|
+
hostBin?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The platform to package for. Defaults to whichever one the current
|
|
24
|
+
* process is running on.
|
|
25
|
+
*/
|
|
26
|
+
target?: PackageTarget;
|
|
27
|
+
/** Where progress notes go. Defaults to `process.stdout`. */
|
|
28
|
+
stdout?: NodeJS.WritableStream;
|
|
29
|
+
}
|
|
30
|
+
/** The result of a successful {@link packageApp} call. */
|
|
31
|
+
export interface PackageResult {
|
|
32
|
+
/** The packaged app's path — a `.app` directory on macOS, a plain directory on Linux. */
|
|
33
|
+
appPath: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Builds the app and pairs it with the prebuilt `inca-host` into a
|
|
37
|
+
* distributable, platform-native application — a `.app` on macOS, a plain
|
|
38
|
+
* directory on Linux — written alongside the build output, under the
|
|
39
|
+
* app's own `dist/`. The app launches with no arguments and no terminal,
|
|
40
|
+
* since `inca-host` finds its own bundle beside its executable.
|
|
41
|
+
*
|
|
42
|
+
* @throws if the app's metadata can't be read, the build fails, or no
|
|
43
|
+
* `inca-host` binary can be resolved for the target platform.
|
|
44
|
+
*/
|
|
45
|
+
export declare function packageApp(options?: PackageAppOptions): Promise<PackageResult>;
|
package/dist/plist.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** A macOS `Info.plist`'s value types — the only ones `inca package` writes. */
|
|
2
|
+
export type PlistValue = string | boolean;
|
|
3
|
+
/** Encodes a flat key/value dict as an `Info.plist` XML document. */
|
|
4
|
+
export declare function encodePlist(dict: Record<string, PlistValue>): string;
|
package/package.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@incajs/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "The inca CLI: watches an app's entry point through a bundler adapter, starts inca-host once the first bundle lands, and reloads it on every rebuild.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bundler",
|
|
7
7
|
"cli",
|
|
8
8
|
"desktop-app",
|
|
9
|
+
"dev-server",
|
|
9
10
|
"gpui",
|
|
10
11
|
"gui",
|
|
11
12
|
"incajs",
|
|
12
13
|
"incarnative.js",
|
|
13
|
-
"
|
|
14
|
+
"json-rpc",
|
|
15
|
+
"quickjs",
|
|
16
|
+
"vite"
|
|
14
17
|
],
|
|
15
18
|
"license": "(MIT OR Apache-2.0)",
|
|
16
19
|
"repository": {
|
|
@@ -18,7 +21,59 @@
|
|
|
18
21
|
"url": "git+https://github.com/tom96da/incajs.git",
|
|
19
22
|
"directory": "packages/cli"
|
|
20
23
|
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"inca": "./bin/inca.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"bin"
|
|
30
|
+
],
|
|
31
|
+
"type": "module",
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.mts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.mts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
21
41
|
"publishConfig": {
|
|
22
42
|
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@vitejs/plugin-vue": "^6.0.8",
|
|
46
|
+
"vite": "^8.2.2"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^26.4.1",
|
|
50
|
+
"@vue/runtime-core": "^3.5.42",
|
|
51
|
+
"oxfmt": "^0.66.0",
|
|
52
|
+
"oxlint": "^1.81.0",
|
|
53
|
+
"rolldown": "~1.2.4",
|
|
54
|
+
"vue": "^3.5.42"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"vue": "^3.5.42"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"vue": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"optionalDependencies": {
|
|
65
|
+
"@incajs/host-darwin-arm64": "0.0.1",
|
|
66
|
+
"@incajs/host-linux-arm64": "0.0.1",
|
|
67
|
+
"@incajs/host-linux-x64": "0.0.1"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22"
|
|
71
|
+
},
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "vite build",
|
|
74
|
+
"test": "vitest run",
|
|
75
|
+
"lint": "oxlint --type-aware",
|
|
76
|
+
"format": "oxfmt --check .",
|
|
77
|
+
"typecheck": "oxlint -A all --type-aware --type-check"
|
|
23
78
|
}
|
|
24
79
|
}
|