@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 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 — the `bun create` starter for apps that run on Glassly
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
- > **Bun-only.** The scaffolder and the generated project's tooling run under
17
- > [Bun](https://bun.sh) — use `bun` / `bunx`, not `npx`.
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) (`glassly login` /
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 kebabName = toKebabCase(projectName)
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 miniapp in ./${projectName}`)
190
+ outro(`Created ${packageName} in ./${projectName}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/create-miniapp",
3
- "version": "0.1.0-dev.2",
3
+ "version": "0.1.0-dev.3",
4
4
  "description": "Scaffold a new Glassly miniapp project",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -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 # 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/
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
@@ -6,8 +6,8 @@
6
6
  "description": "A Glassly miniapp",
7
7
  "icon": "icon.png",
8
8
  "type": "standard",
9
- "sdkVersion": "0.3.0",
10
- "minHostVersion": "1.42.0",
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"
@@ -9,7 +9,7 @@
9
9
  "pack": "glassly-miniapp pack"
10
10
  },
11
11
  "dependencies": {
12
- "@glassly/miniapp": "0.1.0-dev.62",
12
+ "@glassly/miniapp": "0.3.1-dev.509",
13
13
  "react": "^19.0.0",
14
14
  "react-dom": "^19.0.0"
15
15
  },
@@ -31,7 +31,7 @@ registerMiniapp<Channels>(async (session) => {
31
31
  type: "text",
32
32
  id: "hello",
33
33
  box: {x: 0, y: 0, w: d?.width ?? 576, h: d?.height ?? 288},
34
- text: "Hello from GlasslyJS",
34
+ text: "Hello from JSRuntime",
35
35
  },
36
36
  ])
37
37
  })