flare-plugin-sdk 0.1.0-beta.5 → 0.1.0-beta.8

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
@@ -13,6 +13,7 @@ With Node 22 or newer, run:
13
13
  ```sh
14
14
  npx flare-plugin-sdk@next init my-plugin
15
15
  cd my-plugin
16
+ npm install
16
17
  ```
17
18
 
18
19
  This copies the GitHub template bundled with that SDK version into `my-plugin`:
@@ -22,6 +23,11 @@ my-plugin/
22
23
  manifest.json
23
24
  plugin.js
24
25
  assets/icon.png
26
+ src/plugin.ts
27
+ package.json
28
+ tsconfig.json
29
+ build.mjs
30
+ .gitignore
25
31
  README.md
26
32
  ```
27
33
 
@@ -33,11 +39,20 @@ them. The command uses bundled files only and does not install dependencies,
33
39
  initialize Git, contact your website, or publish anything. Run
34
40
  `npx flare-plugin-sdk@next --help` for usage.
35
41
 
36
- Edit `plugin.js` to use your website's real authentication, endpoints, response
42
+ The generated `manifest.json` includes a `$schema` URL pinned to the SDK version
43
+ used to create it. Open it in VS Code or another JSON Schema editor for property
44
+ completion, descriptions, enum choices, and static validation. The complete schema
45
+ is bundled at `skill/flare-plugin/manifest.schema.json` inside the SDK.
46
+
47
+ The root already contains an installable, prebuilt `plugin.js`. Install development
48
+ dependencies to edit and rebuild the TypeScript source. The private project pins
49
+ the SDK to the version used by `init`; TypeScript and esbuild are development tools.
50
+
51
+ Edit `src/plugin.ts` to use your website's real authentication, endpoints, response
37
52
  mapping, and pagination. The starter's `/api/me` and `/api/posts` are illustrative;
38
53
  no demonstration server is included. Update the description, login methods and
39
- capabilities in `manifest.json`, and replace the placeholder icon. The plain
40
- JavaScript starter needs no dependency installation or build step.
54
+ capabilities in `manifest.json`, and replace the placeholder icon. Keep `plugin.js`
55
+ as generated output; edit the TS source and rebuild instead of editing both copies.
41
56
 
42
57
  For manual template copies, replace the example IDs and names before use. For
43
58
  `npx` projects, keep the generated IDs. Read the
@@ -46,13 +61,16 @@ for authentication, Host APIs, capabilities, and Wire data mapping.
46
61
 
47
62
  ## Develop and debug in Flare
48
63
 
49
- 1. From `my-plugin`, check the source syntax:
64
+ 1. From `my-plugin`, run:
50
65
 
51
66
  ```sh
52
- node --check plugin.js
67
+ npm run check
68
+ npm run build
53
69
  node -e 'JSON.parse(require("node:fs").readFileSync("manifest.json", "utf8")); console.log("Manifest JSON parses")'
54
70
  ```
55
71
 
72
+ `check` uses the SDK's ambient types with strict TypeScript checking. `build`
73
+ checks types and bundles local TS modules into one ES2020 IIFE at root.
56
74
  Actual execution and manifest/registration validation happen in Flare, which
57
75
  provides `definePlugin` and `flare`.
58
76
  2. In Compose Desktop or native macOS, open **Settings → Plugins → More → Plugin
@@ -62,8 +80,11 @@ for authentication, Host APIs, capabilities, and Wire data mapping.
62
80
  website's HTTPS API origin, and import a real access token using the starter's
63
81
  login form. Adapt this step if you change the login method. Test login,
64
82
  timeline refresh, older pages, end-of-list behavior, and website link clicks.
65
- 4. Edit and save the files. Flare automatically validates changes; click **Reload
66
- plugin** to apply the validated version. Invalid edits keep the last working
83
+ 4. Run `npm run dev` in a terminal, then edit `src/plugin.ts` or its local modules.
84
+ Watch mode rebuilds `plugin.js`; syntax/build errors preserve the previous
85
+ script. It transpiles without type checking, so use editor diagnostics or run
86
+ `npm run check` separately. Flare automatically validates runtime-file changes;
87
+ click **Reload plugin** to apply them. Invalid edits keep the last working
67
88
  version running. The development panel shows validation errors and recent-call
68
89
  diagnostics; recent calls contain method names and outcomes, without HTTP
69
90
  request/response payloads.
@@ -73,8 +94,8 @@ for authentication, Host APIs, capabilities, and Wire data mapping.
73
94
  Development uses real accounts and data, and remote writes are not rolled back.
74
95
 
75
96
  Finish active writes before reloading; credential-schema changes may require
76
- logging in again. For TypeScript, run your own compiler/watch command and
77
- select its output directory; Flare loads one self-contained classic `plugin.js`.
97
+ logging in again. Keep Flare pointed at the project root, where the build writes
98
+ `plugin.js`. Flare loads that classic script and does not execute TS or build tools.
78
99
  See [development and testing details](https://github.com/DimensionDev/Flare/blob/feature/plugin/feature/plugin/sdk/skill/flare-plugin/references/testing.md#develop-locally-in-desktop).
79
100
 
80
101
  ## Publish and install through GitHub (recommended)
@@ -84,12 +105,15 @@ See [development and testing details](https://github.com/DimensionDev/Flare/blob
84
105
  and optional `locales/*.json`. Update the project README with the actual website,
85
106
  login instructions, supported features, and verification results. Flare reads
86
107
  these files directly; commit any compiled output at the root before tagging.
87
- 2. Commit and push the plugin files. For a new project and an empty GitHub
108
+ 2. Run `npm run build`, then commit both source and the resulting root `plugin.js`.
109
+ The generated `.gitignore` excludes dependencies, temporary files and `.fpp`
110
+ archives, while retaining `plugin.js`. Commit the `package-lock.json` created
111
+ by `npm install`; use `npm ci` on later clean checkouts. For a new project and an empty GitHub
88
112
  repository, replace `OWNER/REPO` and run from `my-plugin`:
89
113
 
90
114
  ```sh
91
115
  git init
92
- git add manifest.json plugin.js assets README.md
116
+ git add manifest.json plugin.js assets src package.json package-lock.json tsconfig.json build.mjs .gitignore README.md
93
117
  # If used, also stage translations with: git add locales
94
118
  git commit -m "Add Flare plugin"
95
119
  git branch -M main
@@ -125,23 +149,42 @@ See [GitHub publication and update rules](https://github.com/DimensionDev/Flare/
125
149
  ## Optional: package and install a local file
126
150
 
127
151
  Use `.fpp` for offline transfer or local testing when GitHub distribution is not
128
- needed. From your project, create a new, empty `dist` directory and copy only
129
- `manifest.json`, `plugin.js`, one `assets/icon.<extension>`, and any `locales/*.json`
130
- into it, preserving those paths. Keep source, `.git`, `package.json`, `node_modules`,
131
- and previous archives outside `dist`. Then run from the project root:
152
+ needed. From the generated project root:
132
153
 
133
154
  ```sh
134
- npx --package=flare-plugin-sdk@next flare-plugin-pack ./dist ../my-plugin.fpp
155
+ npm run package
135
156
  ```
136
157
 
158
+ This checks types, rebuilds `plugin.js`, stages only manifest/script/icon/locale
159
+ files in a temporary directory, reuses the SDK packer, and writes `plugin.fpp` in
160
+ the project root. Temporary staging is cleaned up. Source and development files
161
+ are excluded. The standalone `flare-plugin-pack` command still requires a clean
162
+ runtime directory; passing the entire TS project is not supported.
163
+
137
164
  Stop development for this plugin. In **Settings → Plugins → Add**, choose file
138
165
  installation, select the `.fpp`, review, and confirm. Installation is immediate.
139
166
  Packaging checks archive structure; Flare still validates the manifest,
140
167
  registration, permissions, and behavior. File installs have no GitHub update tracking.
141
168
 
142
- ## Optional: editor types and TypeScript
169
+ ## SDK types and editor support
170
+
171
+ The generated `tsconfig.json` already sets `types: ["flare-plugin-sdk"]`, strict
172
+ checking, and an ES2020 library without browser or Node globals. `definePlugin`
173
+ contextually types its handlers; `PostV1`, `ProfileV1`, and other SDK types are
174
+ available directly. The SDK is a development dependency, not a runtime import.
175
+ Local TS imports are bundled into `plugin.js`; imported code must also work
176
+ without Node/browser APIs. `build` and `package` stop on type errors.
177
+
178
+ For JSON Schema use without network access, install the project's dependencies and set the
179
+ manifest's `$schema` to `./node_modules/flare-plugin-sdk/skill/flare-plugin/manifest.schema.json`.
180
+ Keep these development files outside the optional `.fpp` staging directory.
181
+ Flare treats `$schema` as editor metadata and never downloads it. With an older
182
+ Flare build that rejects this field, remove it and use a VS Code `json.schemas`
183
+ association in workspace settings instead. See the
184
+ [manifest editor guide](https://github.com/DimensionDev/Flare/blob/feature/plugin/feature/plugin/sdk/skill/flare-plugin/references/manifest.md#json-schema-and-editor-support)
185
+ and [VS Code schema settings](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings).
143
186
 
144
- Install editor types in your authoring project:
187
+ For an existing project, install editor types:
145
188
 
146
189
  ```sh
147
190
  npm install --save-dev flare-plugin-sdk@next
@@ -177,11 +220,18 @@ All documentation is in English. The complete Pixelfed adapter is bundled at
177
220
  From `feature/plugin/sdk`, run `npm ci` and `npm test` with Node 22 or newer.
178
221
  The tests build an npm tarball, check its complete resource set and license,
179
222
  install it offline in an empty project, invoke the actual npx initializer,
180
- type-check a consumer, and package both the generated project and bundled template.
223
+ type-check a consumer, and install/build/package the generated TS project. The
224
+ SDK tarball is installed offline; development tools use cached packages where
225
+ available and fetch missing npm metadata or packages. Checks compare the built and
226
+ prebuilt script, exercise local-module bundling, watch errors/recovery and missing
227
+ Node APIs, and verify the package contains only runtime files.
181
228
  Initializer tests cover valid unique IDs, empty/nested/Unicode paths, invalid
182
229
  arguments, and preserving existing directories, files, and symlinks. They do not
183
230
  contact a website or publish to npm. Flare's Host and platform acceptance checks are
184
231
  documented in `skill/flare-plugin/references/testing.md`.
232
+ The draft-07 schema is compiled with Ajv in strict mode. Shared manifest cases
233
+ exercise both Schema and Host validation; Host tests also compare every field,
234
+ required property, enum, and known capability operation against Kotlin descriptors.
185
235
 
186
236
  To try the SDK from a checkout before npm publication, run
187
237
  `npm pack --pack-destination /tmp` after those checks, then install the resulting `.tgz` in
package/bin/cli.mjs CHANGED
@@ -32,25 +32,31 @@ async function main() {
32
32
  const template = fileURLToPath(new URL("../skill/flare-plugin/assets/github-template/", import.meta.url));
33
33
  const manifestPath = join(template, "manifest.json");
34
34
  const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
35
+ const { version } = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
36
+ manifest.$schema = `https://unpkg.com/flare-plugin-sdk@${version}/skill/flare-plugin/manifest.schema.json`;
35
37
  const identity = randomUUID().replaceAll("-", "");
36
38
  manifest.id = `local.plugin.p${identity}`;
37
39
  manifest.name = name;
38
40
  manifest.platform.id = `Plugin${identity}`;
39
41
  manifest.platform.name = name;
42
+ const project = JSON.parse(await readFile(join(template, "package.json"), "utf8"));
43
+ project.name = manifest.id;
44
+ project.devDependencies["flare-plugin-sdk"] = version;
40
45
 
41
46
  await mkdir(target, { recursive: true });
42
47
  for (const entry of await readdir(template)) {
43
- if (entry === "manifest.json") continue;
44
- await cp(join(template, entry), join(target, entry), {
48
+ if (["manifest.json", "package.json"].includes(entry)) continue;
49
+ await cp(join(template, entry), join(target, entry === "_gitignore" ? ".gitignore" : entry), {
45
50
  recursive: true,
46
51
  force: false,
47
52
  errorOnExist: true,
48
53
  });
49
54
  }
50
55
  await writeFile(join(target, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, { flag: "wx" });
56
+ await writeFile(join(target, "package.json"), `${JSON.stringify(project, null, 2)}\n`, { flag: "wx" });
51
57
 
52
58
  console.log(`Created ${target}\nPlugin ID: ${manifest.id}\nPlatform ID: ${manifest.platform.id}`);
53
- console.log("Adapt the example API in plugin.js and README.md, then load this directory in Flare's Plugin development.");
59
+ console.log("Run npm install, edit src/plugin.ts, and use npm run dev to update plugin.js. Load this directory in Flare's Plugin development.");
54
60
  }
55
61
 
56
62
  main().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flare-plugin-sdk",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.8",
4
4
  "description": "Types, tooling, templates and authoring references for Flare plugins",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://github.com/DimensionDev/Flare#plugins-beta",
@@ -32,6 +32,8 @@
32
32
  "test": "node --test scripts/*.test.mjs"
33
33
  },
34
34
  "devDependencies": {
35
+ "ajv": "8.20.0",
36
+ "esbuild": "0.28.2",
35
37
  "typescript": "6.0.3"
36
38
  }
37
39
  }
@@ -168,10 +168,12 @@ cases matter. It should identify gaps rather than invent website endpoints.
168
168
  cd my-plugin
169
169
  ```
170
170
 
171
- It generates unique plugin/platform IDs and needs no dependency installation
172
- or build step for plain JavaScript. For manual copies of
171
+ It generates a TS project with SDK types and unique plugin/platform IDs. A
172
+ prebuilt root `plugin.js` makes it installable before dependencies are installed.
173
+ Run `npm install`, edit `src/plugin.ts`, and use `npm run dev` while developing;
174
+ `npm run build` checks types before rebuilding the root script. For manual copies of
173
175
  [assets/github-template](assets/github-template/README.md), replace its example
174
- IDs and names first. The example API needs adaptation; no demonstration server
176
+ IDs and names first and rename `_gitignore` to `.gitignore`. The example API needs adaptation; no demonstration server
175
177
  is provided.
176
178
  2. Keep the generated IDs stable, replace the placeholder icon, and adapt
177
179
  authentication and data mapping to your website. See [manifest](references/manifest.md)
@@ -183,8 +185,9 @@ cases matter. It should identify gaps rather than invent website endpoints.
183
185
  to start the local directory, test an account, and apply saved edits with
184
186
  **Reload plugin** after automatic validation. Follow the syntax, behavior and
185
187
  platform checks. Stop development before installing/updating the same plugin.
186
- 5. Prefer GitHub distribution: commit the ready-to-run files at a public repository
187
- root, then push a stable tag matching `manifest.version`. In Flare's plugin
188
+ 5. Prefer GitHub distribution: run `npm run build`, commit the TS source, build
189
+ configuration, lockfile and root `plugin.js`, then push a stable tag matching
190
+ `manifest.version`. In Flare's plugin
188
191
  management, enter `https://github.com/OWNER/REPO` and click **Install**. Review
189
192
  source and permissions, then confirm to use the plugin immediately. Follow the
190
193
  plugin's README to select the platform, origin, and login method.
@@ -204,6 +207,7 @@ is not a plugin installation URL. A GitHub Release or `.fpp` upload is unnecessa
204
207
  | Boundary contract and registration | [ABI.md](ABI.md) |
205
208
  | Exact JavaScript signatures and field comments | [flare-plugin.d.ts](flare-plugin.d.ts) |
206
209
  | Manifest fields, defaults, enums, and validation | [Manifest](references/manifest.md) |
210
+ | JSON editor completion and static manifest checks | [JSON Schema](manifest.schema.json), [editor setup](references/manifest.md#json-schema-and-editor-support) |
207
211
  | All twelve capabilities, minimum methods, and UI requirements | [Capabilities](references/capabilities.md) |
208
212
  | Platform engines, async completion, HTTP, permissions, credentials, and state | [Runtime and HTTP](references/runtime-http.md) |
209
213
  | Detection and all five login methods | [Authentication](references/authentication.md) |
@@ -29,8 +29,13 @@ to the user's plugin repository.
29
29
 
30
30
  ## Implement the requested adapter
31
31
 
32
- Start a new repository from [the GitHub template](assets/github-template/README.md).
33
- Copy the **contents** of `assets/github-template/` to the new repository root.
32
+ Start with `npx flare-plugin-sdk@next init my-plugin` and follow the
33
+ [project workflow](assets/github-template/README.md). For manual copies, put the
34
+ **contents** of `assets/github-template/` at root, replace example IDs and rename
35
+ `_gitignore` to `.gitignore`. Install development dependencies, edit `src/plugin.ts`
36
+ using the ambient SDK types, and run `npm run build`. Commit the resulting root
37
+ `plugin.js` with the TS source and lockfile. `npm run dev` watches without type
38
+ checking; `npm run check` and the type-checked build are the release gates.
34
39
  Its `/api/me` and `/api/posts` contract is illustrative, not a live service. Adapt
35
40
  it from verified website evidence before presenting it as an integration.
36
41
  Use [Pixelfed](examples/pixelfed/README.md) as a complete real-service example,
@@ -12,10 +12,18 @@ If you created this project with `npx flare-plugin-sdk@next init my-plugin`, wor
12
12
  inside `my-plugin` and keep its generated plugin/platform IDs. If you copied the
13
13
  template manually, first copy its contents to your repository root and replace
14
14
  the example `manifest.id`, `platform.id`, and names. Keep IDs stable for updates.
15
+ For manual copies, also rename `_gitignore` to `.gitignore`; `init` does this for you.
16
+
17
+ The generated manifest's `$schema` is pinned to the SDK version that created it.
18
+ Open `manifest.json` in VS Code or another JSON Schema editor for completion,
19
+ field descriptions, enum choices and static errors. Flare treats the URL as
20
+ metadata and never downloads it; actual registration and behavior are checked in
21
+ Flare. See the [manifest editor guide](https://github.com/DimensionDev/Flare/blob/feature/plugin/feature/plugin/sdk/skill/flare-plugin/references/manifest.md#json-schema-and-editor-support)
22
+ for offline use and older Flare builds that reject `$schema`.
15
23
 
16
24
  1. Update the description, login methods, and declared capabilities in
17
25
  `manifest.json`. Replace the one-pixel placeholder PNG with your own icon.
18
- 2. Adapt `plugin.js` using the
26
+ 2. Adapt `src/plugin.ts` using the
19
27
  [developer guide](https://github.com/DimensionDev/Flare/tree/feature/plugin/feature/plugin/sdk/skill/flare-plugin)
20
28
  and your website's API evidence.
21
29
  It currently verifies an imported bearer token with `/api/me`, then reads
@@ -30,14 +38,23 @@ authenticated profile returned at login supplies the saved account identity.
30
38
 
31
39
  ## 2. Develop and debug locally
32
40
 
33
- The plain JavaScript starter needs Node 22 or newer for tooling, with no dependency
34
- installation or build step. From this project directory, check syntax:
41
+ The root contains a prebuilt, installable `plugin.js`, alongside TS source and
42
+ development tools. With Node 22 or newer, run from this project directory:
35
43
 
36
44
  ```sh
37
- node --check plugin.js
45
+ npm install
46
+ npm run check
47
+ npm run build
38
48
  node -e 'JSON.parse(require("node:fs").readFileSync("manifest.json", "utf8")); console.log("Manifest JSON parses")'
39
49
  ```
40
50
 
51
+ `npm install` creates a lockfile; commit it and use `npm ci` on later checkouts.
52
+ The private package uses the SDK for ambient types, TypeScript for strict checking,
53
+ and esbuild to bundle local TS modules into one ES2020 IIFE at root. The generated
54
+ SDK dependency is version-pinned. Types such as `PostV1`, `ProfileV1`, and
55
+ `PluginErrorCodeV1` are available directly, and `definePlugin` types its handlers.
56
+ The SDK is not imported at runtime; plugin code has no Node or browser globals.
57
+
41
58
  Flare supplies the `definePlugin` and `flare` globals and performs the actual
42
59
  manifest, registration, and runtime checks. Use a Flare build with Plugins in Settings.
43
60
 
@@ -47,9 +64,12 @@ manifest, registration, and runtime checks. Use a Flare build with Plugins in Se
47
64
  2. Add an account, explicitly select this plugin platform, and enter your website's
48
65
  HTTPS API origin. Import a real access token. After adapting the endpoints,
49
66
  verify the account identity, Home refresh, older/empty/end pages, and link clicks.
50
- 3. Edit and save `plugin.js`, `manifest.json`, the icon, or translations. Flare
51
- automatically validates them; click **Reload plugin** to apply the validated
52
- version. Invalid edits leave the previous working version running.
67
+ 3. Run `npm run dev` and edit `src/plugin.ts` or its local modules. Watch mode
68
+ rebuilds `plugin.js`, preserving the previous script on syntax/build errors.
69
+ It does not check types; use editor diagnostics or `npm run check` separately.
70
+ Flare automatically validates the built script, manifest, icon and translations;
71
+ click **Reload plugin** to apply the validated version. Invalid changes leave
72
+ the previous working version running.
53
73
  4. Check validation errors and recent-call diagnostics in the development panel.
54
74
  Recent calls show method names and outcomes, without HTTP request/response
55
75
  payloads. Reproduce authentication, HTTP, response-mapping, and paging failures
@@ -59,9 +79,9 @@ manifest, registration, and runtime checks. Use a Flare build with Plugins in Se
59
79
  automatically mount this directory. Development uses real accounts and data,
60
80
  and remote writes are not rolled back.
61
81
 
62
- For TypeScript, run your own compiler/watch process and select its output directory.
63
- Emit one self-contained classic `plugin.js`; Flare provides its Host APIs rather
64
- than Node/browser APIs. Commit that built script at the repository root for release.
82
+ Keep the project root selected in Flare. Edit TS source, not the generated
83
+ `plugin.js`; `npm run build` checks types before rebuilding it. Commit that built
84
+ script at root so GitHub installation works without running npm on the device.
65
85
  See [development and testing details](https://github.com/DimensionDev/Flare/blob/feature/plugin/feature/plugin/sdk/skill/flare-plugin/references/testing.md#develop-locally-in-desktop)
66
86
  for reload restrictions, credential changes, and platform acceptance checks.
67
87
 
@@ -80,13 +100,16 @@ locales/en.json # optional; other locale catalogs as needed
80
100
  The icon can also be JPEG, WebP, GIF, or ICO; keep exactly one supported icon.
81
101
  Development files may coexist in the repository, but keep credentials,
82
102
  `node_modules`, build caches, and private fixtures out of Git. Flare reads the
83
- committed plugin files directly, so build TypeScript before committing/tagging.
103
+ committed plugin files directly. Run `npm run build` before committing/tagging;
104
+ it must pass and the latest `plugin.js` must be committed. Keep the TS source,
105
+ build configuration and lockfile in Git too. `.gitignore` excludes dependencies
106
+ and local archives while retaining the runtime script.
84
107
 
85
108
  For a new project and an empty GitHub repository, replace `OWNER/REPO` and run:
86
109
 
87
110
  ```sh
88
111
  git init
89
- git add manifest.json plugin.js assets README.md
112
+ git add manifest.json plugin.js assets src package.json package-lock.json tsconfig.json build.mjs .gitignore README.md
90
113
  # If used, also stage translations with: git add locales
91
114
  git commit -m "Add Flare plugin"
92
115
  git branch -M main
@@ -118,16 +141,17 @@ review/confirm the new version. See the
118
141
 
119
142
  ## Alternative: package a local file
120
143
 
121
- Use `.fpp` for offline transfer or local testing. Create a new, empty `dist`
122
- directory and copy only `manifest.json`, `plugin.js`, one supported
123
- `assets/icon.<extension>`, and any `locales/*.json` into it, keeping those paths.
124
- Keep `.git`, `package.json`, `node_modules`, tests, and old archives outside `dist`.
125
- From this project directory, run:
144
+ Use `.fpp` for offline transfer or local testing. From this project directory:
126
145
 
127
146
  ```sh
128
- npx --package=flare-plugin-sdk@next flare-plugin-pack ./dist ../my-plugin.fpp
147
+ npm run package
129
148
  ```
130
149
 
150
+ This checks types, rebuilds the root script, copies only manifest/script/icon/locale
151
+ files to temporary staging, and uses the SDK packer to write `plugin.fpp` at root.
152
+ Staging is cleaned up and development files are excluded. The standalone packer
153
+ still accepts only a clean runtime directory, not this whole project.
154
+
131
155
  Stop development for this plugin, then choose file installation in **Settings →
132
156
  Plugins → Add**, select the `.fpp`, review, and confirm. It becomes available
133
157
  immediately. The packer checks archive structure; Flare still validates the
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ *.fpp
4
+ .DS_Store
5
+ .plugin-build-*/
@@ -0,0 +1,76 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { cp, mkdir, mkdtemp, readdir, rename, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { build, context } from "esbuild";
7
+
8
+ const root = fileURLToPath(new URL(".", import.meta.url));
9
+ const mode = process.argv[2];
10
+ if (process.argv.length > 3 || (mode && !["--watch", "--package"].includes(mode))) {
11
+ throw new Error("Usage: node build.mjs [--watch|--package]");
12
+ }
13
+ const options = {
14
+ absWorkingDir: root,
15
+ entryPoints: ["src/plugin.ts"],
16
+ outfile: "plugin.js",
17
+ bundle: true,
18
+ format: "iife",
19
+ platform: "neutral",
20
+ mainFields: ["module", "main"],
21
+ target: "es2020",
22
+ logLevel: "info",
23
+ write: false,
24
+ plugins: [{
25
+ name: "plugin-output",
26
+ setup(builder) {
27
+ builder.onEnd(async (result) => {
28
+ if (result.errors.length) return;
29
+ if (result.outputFiles.length !== 1) throw new Error("A plugin build must emit one JavaScript file");
30
+ const temporary = await mkdtemp(join(root, ".plugin-build-"));
31
+ try {
32
+ // Preserve the installable script while edits are invalid or a build is interrupted.
33
+ await writeFile(join(temporary, "plugin.js"), result.outputFiles[0].contents, { flag: "wx" });
34
+ await rename(join(temporary, "plugin.js"), join(root, "plugin.js"));
35
+ } finally {
36
+ await rm(temporary, { recursive: true, force: true });
37
+ }
38
+ });
39
+ },
40
+ }],
41
+ };
42
+
43
+ if (mode === "--watch") {
44
+ const watcher = await context(options);
45
+ await watcher.watch();
46
+ } else {
47
+ await build(options);
48
+ if (mode === "--package") {
49
+ const staging = await mkdtemp(join(tmpdir(), "flare-plugin-package-"));
50
+ try {
51
+ for (const name of ["manifest.json", "plugin.js"]) {
52
+ await cp(join(root, name), join(staging, name));
53
+ }
54
+ await mkdir(join(staging, "assets"));
55
+ for (const name of await readdir(join(root, "assets"))) {
56
+ if (/^icon\.(?:png|jpe?g|webp|gif|ico)$/.test(name)) {
57
+ await cp(join(root, "assets", name), join(staging, "assets", name));
58
+ }
59
+ }
60
+ const catalogs = await readdir(join(root, "locales")).catch((error) => {
61
+ if (error.code !== "ENOENT") throw error;
62
+ return [];
63
+ });
64
+ if (catalogs.length) {
65
+ await mkdir(join(staging, "locales"));
66
+ for (const name of catalogs.filter((name) => name.endsWith(".json"))) {
67
+ await cp(join(root, "locales", name), join(staging, "locales", name));
68
+ }
69
+ }
70
+ const packer = fileURLToPath(import.meta.resolve("flare-plugin-sdk/skill/flare-plugin/pack.mjs"));
71
+ execFileSync(process.execPath, [packer, staging, join(root, "plugin.fpp")], { stdio: "inherit" });
72
+ } finally {
73
+ await rm(staging, { recursive: true, force: true });
74
+ }
75
+ }
76
+ }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "$schema": "https://unpkg.com/flare-plugin-sdk@next/skill/flare-plugin/manifest.schema.json",
2
3
  "schemaVersion": 1,
3
4
  "apiVersion": 1,
4
5
  "id": "dev.example.flare.feed",
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "flare-plugin",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "check": "tsc --noEmit",
7
+ "build": "npm run check && node build.mjs",
8
+ "dev": "node build.mjs --watch",
9
+ "package": "npm run check && node build.mjs --package"
10
+ },
11
+ "devDependencies": {
12
+ "esbuild": "0.28.2",
13
+ "flare-plugin-sdk": "next",
14
+ "typescript": "6.0.3"
15
+ }
16
+ }