@stacksjs/desktop 0.70.159 → 0.70.161
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 +42 -6
- package/dist/index.js +1 -1
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# Stacks Desktop
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Features
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Native macOS, Linux, and Windows windows powered by Craft
|
|
6
|
+
- Pretty development URLs through the Stacks rpx and tlsx integration
|
|
7
|
+
- Hot reload and native developer tools
|
|
8
|
+
- Multi-recipient invite delivery with application-owned token and mail callbacks
|
|
9
|
+
- HTTPS update checks with SHA-256 verification and atomic staging
|
|
6
10
|
|
|
7
|
-
##
|
|
11
|
+
## Usage
|
|
8
12
|
|
|
9
13
|
```bash
|
|
10
14
|
bun install -d @stacksjs/desktop
|
|
@@ -13,12 +17,44 @@ bun install -d @stacksjs/desktop
|
|
|
13
17
|
Now, you can use it in your project:
|
|
14
18
|
|
|
15
19
|
```js
|
|
16
|
-
import
|
|
20
|
+
import { openDevWindow } from '@stacksjs/desktop'
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
await openDevWindow(3000, {
|
|
23
|
+
title: 'My Stacks App',
|
|
24
|
+
})
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
`APP_URL` is used by default, so a normal Stacks project opens its pretty HTTPS URL. Pass `url` only when embedding another development server.
|
|
28
|
+
|
|
29
|
+
Craft is provisioned by Pantry from `config/deps.ts`. Run `pantry install` if the native binary is not available yet.
|
|
30
|
+
|
|
31
|
+
Invite delivery stays transport-neutral so applications can use their configured Stacks mail provider:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { sendDesktopInvites } from '@stacksjs/desktop'
|
|
35
|
+
|
|
36
|
+
await sendDesktopInvites(users, {
|
|
37
|
+
baseUrl: 'https://app.example.com',
|
|
38
|
+
createToken: user => inviteTokens.create(user),
|
|
39
|
+
send: invite => mail.send(invite.recipient.email, invite.url),
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The updater never executes unverified downloads. Check the signed release channel, then stage the checksum-verified artifact for the platform installer:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { checkForDesktopUpdate, stageDesktopUpdate } from '@stacksjs/desktop'
|
|
47
|
+
|
|
48
|
+
const update = await checkForDesktopUpdate({
|
|
49
|
+
currentVersion: '1.4.0',
|
|
50
|
+
manifestUrl: 'https://app.example.com/desktop/updates/stable.json',
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (update)
|
|
54
|
+
await stageDesktopUpdate(update, '/path/to/update.bin')
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Testing
|
|
22
58
|
|
|
23
59
|
```bash
|
|
24
60
|
bun test
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
async function g(
|
|
2
|
+
import{existsSync as p}from"fs";import{homedir as v}from"os";import{join as n}from"path";function m(t,e,r={}){if(!e.trim())throw Error("Invite token is required");let s=new URL("/invite",/^https?:\/\//.test(t)?t:`https://${t}`);if(s.searchParams.set("token",e),r.email)s.searchParams.set("email",r.email);if(r.team)s.searchParams.set("team",r.team);if(r.role)s.searchParams.set("role",r.role);if(r.expiresAt){let i=r.expiresAt instanceof Date?r.expiresAt:new Date(r.expiresAt);if(Number.isNaN(i.valueOf()))throw Error("Invite expiration must be a valid date");s.searchParams.set("expires",i.toISOString())}return s.toString()}async function U(t,e){return Promise.all(t.map(async(r)=>{let s=typeof r==="string"?{email:r}:r;try{h(s.email);let i=await e.createToken(s),a=m(e.baseUrl,i,{email:s.email,team:s.team,role:s.role,expiresAt:e.expiresAt});return await e.send({recipient:s,url:a}),{recipient:s,url:a,delivered:!0}}catch(i){return{recipient:s,url:"",delivered:!1,error:i instanceof Error?i.message:String(i)}}}))}function h(t){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t))throw Error(`Invalid invite email: ${t}`)}import{createHash as k}from"crypto";import{mkdir as g,rename as d,writeFile as l}from"fs/promises";import{dirname as D}from"path";async function L(t){o(t.manifestUrl);let e=await(t.fetcher||fetch)(t.manifestUrl,{headers:{Accept:"application/json"}});if(!e.ok)throw Error(`Desktop update check failed with HTTP ${e.status}`);let r=u(await e.json());return w(r.version,t.currentVersion)>0?r:null}async function z(t,e,r=fetch){u(t),o(t.url);let s=await r(t.url);if(!s.ok)throw Error(`Desktop update download failed with HTTP ${s.status}`);let i=new Uint8Array(await s.arrayBuffer());if(t.size!==void 0&&i.byteLength!==t.size)throw Error(`Desktop update size mismatch: expected ${t.size}, received ${i.byteLength}`);if(k("sha256").update(i).digest("hex")!==t.sha256.toLowerCase())throw Error("Desktop update checksum mismatch");await g(D(e),{recursive:!0});let f=`${e}.download-${crypto.randomUUID()}`;return await l(f,i,{mode:448}),await d(f,e),{manifest:t,path:e,bytes:i.byteLength}}function w(t,e){let r=c(t),s=c(e);for(let i=0;i<3;i++){let a=r[i]-s[i];if(a!==0)return Math.sign(a)}return 0}function u(t){if(!t||typeof t!=="object")throw Error("Desktop update manifest must be an object");let e=t;if(c(e.version||""),typeof e.url!=="string")throw Error("Desktop update manifest requires a URL");if(typeof e.sha256!=="string"||!/^[a-f0-9]{64}$/i.test(e.sha256))throw Error("Desktop update manifest requires a SHA-256 checksum");if(e.size!==void 0&&(!Number.isInteger(e.size)||e.size<0))throw Error("Desktop update manifest size must be a non-negative integer");return e}function c(t){let e=t.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/);if(!e)throw Error(`Invalid semantic version: ${t}`);return[Number(e[1]),Number(e[2]),Number(e[3])]}function o(t){let e=new URL(t),r=["localhost","127.0.0.1","::1"].includes(e.hostname);if(e.protocol!=="https:"&&!(e.protocol==="http:"&&r))throw Error("Desktop updates require HTTPS outside loopback development")}function y(t=process.env.CRAFT_BIN){if(t){if(!p(t))throw Error(`Craft binary not found: ${t}`);return t}let e=n(v(),"Code/Tools/craft");return[n(e,"packages/zig/zig-out/bin/craft"),n(e,"craft"),n(e,"bin/craft")].find((s)=>p(s))||"craft"}function I(t,e={}){if(!Number.isInteger(t)||t<1||t>65535)throw RangeError(`Invalid desktop development port: ${t}`);let r=e.url||process.env.APP_URL;if(!r)return"https://stacks.test";let s=/^https?:\/\//.test(r)?r:`https://${r}`;return new URL(s).toString().replace(/\/$/,"")}function b(t,e={}){let r=[y(e.craftBin),I(t,e),"--title",e.title||"Stacks","--width",String(e.width||1400),"--height",String(e.height||900)];if(e.hotReload!==!1)r.push("--hot-reload");if(e.devTools===!1)r.push("--no-devtools");if(e.darkMode)r.push("--dark");if(e.systemTray)r.push("--system-tray");if(e.hideDockIcon)r.push("--hide-dock-icon");if(e.menubarOnly)r.push("--menubar-only");return r}function N(t,e="stable"){if(!/^[a-z0-9-]+$/i.test(e))throw Error("Update channel may only contain letters, numbers, and hyphens");return new URL(`/desktop/updates/${e}.json`,/^https?:\/\//.test(t)?t:`https://${t}`).toString()}async function P(t){Bun.spawn(t,{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).unref()}async function H(t,e={},r=P){return await r(b(t,e)),!0}export{u as validateUpdateManifest,z as stageDesktopUpdate,U as sendDesktopInvites,I as resolveDevWindowUrl,y as resolveCraftBinary,H as openDevWindow,N as createUpdateManifestUrl,m as createInviteLink,b as craftDevCommand,w as compareVersions,L as checkForDesktopUpdate};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/desktop",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.161",
|
|
6
6
|
"description": "The Stacks Desktop engine.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -54,17 +54,16 @@
|
|
|
54
54
|
"dist"
|
|
55
55
|
],
|
|
56
56
|
"scripts": {
|
|
57
|
-
"dev": "
|
|
58
|
-
"dev:app": "
|
|
57
|
+
"dev": "bun ../../actions/src/dev/desktop.ts",
|
|
58
|
+
"dev:app": "bun ../../actions/src/dev/desktop.ts",
|
|
59
59
|
"build": "bun build.ts",
|
|
60
|
-
"build:app": "
|
|
61
|
-
"tauri:build": "tauri build",
|
|
60
|
+
"build:app": "bun ../../actions/src/build/desktop.ts",
|
|
62
61
|
"typecheck": "bun tsc --noEmit",
|
|
63
62
|
"prepublishOnly": "bun run build"
|
|
64
63
|
},
|
|
65
64
|
"devDependencies": {
|
|
66
|
-
"@stacksjs/build": "0.70.
|
|
65
|
+
"@stacksjs/build": "0.70.161",
|
|
67
66
|
"better-dx": "^0.2.17",
|
|
68
|
-
"@stacksjs/server": "0.70.
|
|
67
|
+
"@stacksjs/server": "0.70.161"
|
|
69
68
|
}
|
|
70
69
|
}
|