@glassly/create-miniapp 0.1.0-dev.2 → 0.1.0-dev.3
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 +38 -5
- package/bin/index.ts +43 -3
- package/package.json +1 -1
- package/template/README.md +9 -3
- package/template/miniapp.json +2 -2
- package/template/package.json +1 -1
- package/template/src/background/index.ts +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @glassly/create-miniapp
|
|
2
2
|
|
|
3
3
|
Scaffold a new [Glassly](https://github.com/tetramo-labs/glassly)
|
|
4
|
-
miniapp project
|
|
4
|
+
miniapp project. This is the `bun create` starter for apps that run on Glassly
|
|
5
5
|
smart glasses.
|
|
6
6
|
|
|
7
7
|
## Usage
|
|
@@ -13,8 +13,41 @@ bun install
|
|
|
13
13
|
bun run dev
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
>
|
|
17
|
-
> [Bun](https://bun.sh)
|
|
16
|
+
> The scaffolder and the generated project's tooling run under
|
|
17
|
+
> [Bun](https://bun.sh) only. Use `bun` and `bunx`, not `npx`.
|
|
18
|
+
|
|
19
|
+
### `--prefix`
|
|
20
|
+
|
|
21
|
+
Every miniapp lives under its developer org's reverse-DNS package prefix (set
|
|
22
|
+
when you create the org in the Developer Console, for example `com.example`).
|
|
23
|
+
The scaffolder asks for it and writes `<prefix>.<project_name>` into
|
|
24
|
+
`miniapp.json` as the `packageName`, with dashes in the project name turned into
|
|
25
|
+
underscores so the name is valid. The Store rejects releases outside your org's
|
|
26
|
+
prefix, so use the real one. Pass `--prefix` to skip the prompt:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
bunx @glassly/create-miniapp my-app --prefix=com.example
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### `--type`
|
|
33
|
+
|
|
34
|
+
The scaffolder asks what kind of glasses the miniapp targets, which decides the
|
|
35
|
+
`hardwareRequirements` it writes into `miniapp.json`. Pass `--type` to skip the
|
|
36
|
+
prompt:
|
|
37
|
+
|
|
38
|
+
| Value | Glasses | `hardwareRequirements` |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `display` | Even Realities G1/G2, Vuzix Z100 | `DISPLAY` + `MICROPHONE`, both `REQUIRED` |
|
|
41
|
+
| `camera` | Mentra Live | `CAMERA` + `MICROPHONE`, both `REQUIRED` |
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
bunx @glassly/create-miniapp my-app --type=display
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Use the `--flag=value` form for both flags; a space-separated `--type value` is
|
|
48
|
+
not parsed.
|
|
49
|
+
Requirements are only a starting point — edit `miniapp.json` directly, or with
|
|
50
|
+
`glassly-miniapp hardware add`, as the miniapp grows.
|
|
18
51
|
|
|
19
52
|
The generated project comes wired with
|
|
20
53
|
[`@glassly/miniapp`](https://www.npmjs.com/package/@glassly/miniapp) (the SDK
|
|
@@ -25,8 +58,8 @@ SDK versions this scaffolder shipped with, so a generated project installs
|
|
|
25
58
|
on any release channel.
|
|
26
59
|
|
|
27
60
|
To publish a finished miniapp to the Glassly Miniapp Store, see
|
|
28
|
-
[`@glassly/cli`](https://www.npmjs.com/package/@glassly/cli)
|
|
29
|
-
`glassly publish
|
|
61
|
+
[`@glassly/cli`](https://www.npmjs.com/package/@glassly/cli), specifically `glassly login` and
|
|
62
|
+
`glassly publish`.
|
|
30
63
|
|
|
31
64
|
## Part of Glassly
|
|
32
65
|
|
package/bin/index.ts
CHANGED
|
@@ -9,8 +9,15 @@ type GlassesType = "display" | "camera"
|
|
|
9
9
|
interface CliFlags {
|
|
10
10
|
projectName?: string
|
|
11
11
|
type?: GlassesType
|
|
12
|
+
prefix?: string
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
// Package names are reverse-DNS: letters, digits, underscores, each segment
|
|
16
|
+
// starting with a letter. The Store rejects anything else, and every miniapp
|
|
17
|
+
// must sit under the developer org's package prefix.
|
|
18
|
+
const PACKAGE_SEGMENT = /^[a-z][a-z0-9_]*$/
|
|
19
|
+
const PACKAGE_PREFIX = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/
|
|
20
|
+
|
|
14
21
|
function parseArgs(argv: string[]): CliFlags {
|
|
15
22
|
const flags: CliFlags = {}
|
|
16
23
|
for (const arg of argv) {
|
|
@@ -22,6 +29,8 @@ function parseArgs(argv: string[]): CliFlags {
|
|
|
22
29
|
console.error(`Invalid --type value: "${value}". Expected "display" or "camera".`)
|
|
23
30
|
process.exit(1)
|
|
24
31
|
}
|
|
32
|
+
} else if (arg.startsWith("--prefix=")) {
|
|
33
|
+
flags.prefix = arg.slice("--prefix=".length)
|
|
25
34
|
} else if (!arg.startsWith("-") && !flags.projectName) {
|
|
26
35
|
flags.projectName = arg
|
|
27
36
|
}
|
|
@@ -36,17 +45,32 @@ function toKebabCase(str: string): string {
|
|
|
36
45
|
.toLowerCase()
|
|
37
46
|
}
|
|
38
47
|
|
|
48
|
+
function toPackageSegment(str: string): string {
|
|
49
|
+
return toKebabCase(str).replace(/-+/g, "_")
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
function validateProjectName(name: string): string | undefined {
|
|
40
53
|
if (!name.trim()) return "Project name is required"
|
|
41
54
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9-_ ]*$/.test(name)) {
|
|
42
55
|
return "Use letters, numbers, dashes, underscores, or spaces (cannot start with a symbol)"
|
|
43
56
|
}
|
|
57
|
+
if (!PACKAGE_SEGMENT.test(toPackageSegment(name))) {
|
|
58
|
+
return "Project name must start with a letter"
|
|
59
|
+
}
|
|
44
60
|
if (fs.existsSync(path.resolve(process.cwd(), name))) {
|
|
45
61
|
return `Directory "${name}" already exists`
|
|
46
62
|
}
|
|
47
63
|
return undefined
|
|
48
64
|
}
|
|
49
65
|
|
|
66
|
+
function validatePrefix(prefix: string): string | undefined {
|
|
67
|
+
if (!prefix.trim()) return "Package prefix is required"
|
|
68
|
+
if (!PACKAGE_PREFIX.test(prefix)) {
|
|
69
|
+
return "Use reverse-DNS form like com.example: lowercase letters, digits, underscores, at least two segments"
|
|
70
|
+
}
|
|
71
|
+
return undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
function bailIfCancelled<T>(value: T | symbol): T {
|
|
51
75
|
if (isCancel(value)) {
|
|
52
76
|
cancel("Cancelled.")
|
|
@@ -76,6 +100,23 @@ if (projectName) {
|
|
|
76
100
|
)
|
|
77
101
|
}
|
|
78
102
|
|
|
103
|
+
let prefix = flags.prefix
|
|
104
|
+
if (prefix) {
|
|
105
|
+
const err = validatePrefix(prefix)
|
|
106
|
+
if (err) {
|
|
107
|
+
cancel(err)
|
|
108
|
+
process.exit(1)
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
prefix = bailIfCancelled(
|
|
112
|
+
await text({
|
|
113
|
+
message: "Package prefix? (your developer org's prefix from the Developer Console)",
|
|
114
|
+
placeholder: "com.example",
|
|
115
|
+
validate: validatePrefix,
|
|
116
|
+
}),
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
79
120
|
let glassesType = flags.type
|
|
80
121
|
if (!glassesType) {
|
|
81
122
|
glassesType = bailIfCancelled(
|
|
@@ -100,8 +141,7 @@ if (!glassesType) {
|
|
|
100
141
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
101
142
|
const templateDir = path.resolve(import.meta.dirname, "..", "template")
|
|
102
143
|
|
|
103
|
-
const
|
|
104
|
-
const packageName = `com.glassly.${kebabName}`
|
|
144
|
+
const packageName = `${prefix}.${toPackageSegment(projectName)}`
|
|
105
145
|
const hardwareRequirements =
|
|
106
146
|
glassesType === "camera"
|
|
107
147
|
? [
|
|
@@ -147,4 +187,4 @@ copyDir(templateDir, targetDir)
|
|
|
147
187
|
|
|
148
188
|
note(`cd ${projectName}\nbun install\nbun dev`, "Next steps")
|
|
149
189
|
|
|
150
|
-
outro(`Created
|
|
190
|
+
outro(`Created ${packageName} in ./${projectName}`)
|
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -26,11 +26,17 @@ The layers do not share memory. Exchange serializable data through the typed
|
|
|
26
26
|
## Commands
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
bun run dev
|
|
30
|
-
bun run release
|
|
31
|
-
bun run build
|
|
29
|
+
bun run dev # live, temporary; keep the computer and CLI running
|
|
30
|
+
bun run release # persistent local install that runs without the computer
|
|
31
|
+
bun run build # build both layers into dist/
|
|
32
|
+
bun run pack # production-build, validate the manifest, zip to build/<pkg>-<version>.zip
|
|
33
|
+
bun run typecheck # tsc --noEmit, no bundle emitted
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
`pack` is the distributable artifact on its own: `release` builds and packs for
|
|
37
|
+
you, so reach for `pack` when you want the zip without serving it. `typecheck`
|
|
38
|
+
is types only — the unsupported-API checks live in `build`.
|
|
39
|
+
|
|
34
40
|
The Glassly App keeps one dev entry per manifest package name, so you can scan and
|
|
35
41
|
test multiple dev miniapps side by side. It also caches each entry's name and
|
|
36
42
|
icon. Rescanning the same package updates that entry. Use `bun run release` when
|
package/template/miniapp.json
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"description": "A Glassly miniapp",
|
|
7
7
|
"icon": "icon.png",
|
|
8
8
|
"type": "standard",
|
|
9
|
-
"sdkVersion": "0.3.
|
|
10
|
-
"minHostVersion": "
|
|
9
|
+
"sdkVersion": "0.3.1",
|
|
10
|
+
"minHostVersion": "0.3.1",
|
|
11
11
|
"entry": {
|
|
12
12
|
"background": "background/index.js",
|
|
13
13
|
"ui": "ui/index.html"
|
package/template/package.json
CHANGED