@owncast/plugin-sdk 0.3.1 → 0.4.0

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.
@@ -238,50 +238,18 @@ module.exports = { register, on_event, on_filter, on_http_request };
238
238
  env,
239
239
  });
240
240
 
241
- // If the project ships static assets in ./assets/, mirror them to the
242
- // canonical deployment layout (<name>-assets/) so plugin.Server finds them
243
- // without per-deployment renames. We use a symlink so edits to assets/
244
- // show up live during dev (no rebuild needed for HTML/CSS changes).
245
- const assetsSrc = path.join(cwd, "assets");
246
- if (fs.existsSync(assetsSrc) && fs.statSync(assetsSrc).isDirectory()) {
247
- const assetsDest = path.join(cwd, `${slug}-assets`);
248
- let needsLink = true;
249
- // Use lstatSync (not existsSync), existsSync follows symlinks and
250
- // returns false for a dangling link, but the link's inode is still
251
- // there and would make symlinkSync below fail with EEXIST. lstatSync
252
- // sees the link itself regardless of whether its target resolves.
253
- let st;
254
- try {
255
- st = fs.lstatSync(assetsDest);
256
- } catch {
257
- // path doesn't exist at all, fall through to create it.
258
- }
259
- if (st) {
260
- let target;
261
- if (st.isSymbolicLink()) {
262
- // realpathSync throws on dangling links; treat that as "doesn't
263
- // match, replace it" rather than letting it abort the build.
264
- try {
265
- target = fs.realpathSync(assetsDest);
266
- } catch {}
267
- }
268
- if (target && target === fs.realpathSync(assetsSrc)) {
269
- needsLink = false;
270
- } else {
271
- fs.rmSync(assetsDest, { recursive: true, force: true });
272
- }
273
- }
274
- if (needsLink) {
275
- fs.symlinkSync(path.resolve(assetsSrc), assetsDest, "dir");
276
- }
277
- }
241
+ // public/ and assets/ live at the source root; the host's
242
+ // loose-files loader picks them up as siblings of the built
243
+ // <slug>.wasm without any rename, so the build CLI doesn't need to
244
+ // create or mirror anything for them.
278
245
 
279
246
  console.log(`built ${path.relative(cwd, wasmOut)}`);
280
247
  }
281
248
 
282
249
  // `owncast-plugin package`, bundle the project into a single .ocpkg file
283
- // (zip archive with plugin.manifest.json, plugin.wasm, and optional assets/).
284
- // Builds the wasm first if it doesn't exist.
250
+ // (zip archive with plugin.manifest.json, plugin.wasm, and optional
251
+ // public/ and assets/ directories). Builds the wasm first if it
252
+ // doesn't exist.
285
253
  async function packageMain() {
286
254
  const cwd = process.cwd();
287
255
  const manifestPath = path.join(cwd, "plugin.manifest.json");
@@ -296,6 +264,7 @@ async function packageMain() {
296
264
  await buildMain();
297
265
  }
298
266
 
267
+ const publicDir = path.join(cwd, "public");
299
268
  const assetsDir = path.join(cwd, "assets");
300
269
  const zip = new JSZip();
301
270
  zip.file("plugin.manifest.json", fs.readFileSync(manifestPath));
@@ -310,6 +279,26 @@ async function packageMain() {
310
279
  zip.file("icon.png", fs.readFileSync(iconPath));
311
280
  fileCount++;
312
281
  }
282
+ // Bundle a top-level INSTRUCTIONS.md if the plugin source root has one.
283
+ // The host serves it to the admin (which renders it as markdown in a
284
+ // details tab); like icon.png it needs no manifest field and no
285
+ // http.serve permission. The filename is fixed for simplicity.
286
+ const instructionsPath = path.join(cwd, "INSTRUCTIONS.md");
287
+ if (fs.existsSync(instructionsPath) && fs.statSync(instructionsPath).isFile()) {
288
+ zip.file("INSTRUCTIONS.md", fs.readFileSync(instructionsPath));
289
+ fileCount++;
290
+ }
291
+ // public/ → /plugins/<slug>/<path>, served by the host.
292
+ if (fs.existsSync(publicDir) && fs.statSync(publicDir).isDirectory()) {
293
+ for (const file of walkFiles(publicDir)) {
294
+ const rel = path.relative(publicDir, file).split(path.sep).join("/");
295
+ zip.file(`public/${rel}`, fs.readFileSync(file));
296
+ fileCount++;
297
+ }
298
+ }
299
+ // assets/ → host reads internally for manifest fields that inline
300
+ // file contents (styles, scripts, extraPageContent). Not served at
301
+ // a URL.
313
302
  if (fs.existsSync(assetsDir) && fs.statSync(assetsDir).isDirectory()) {
314
303
  for (const file of walkFiles(assetsDir)) {
315
304
  const rel = path.relative(assetsDir, file).split(path.sep).join("/");
@@ -329,12 +318,29 @@ async function packageMain() {
329
318
  console.log(
330
319
  `packaged ${path.relative(cwd, outPath)} (${sizeKb} KB, ${fileCount} files)`,
331
320
  );
321
+
322
+ // Drop the intermediate <slug>.wasm now that it's bundled inside the
323
+ // .ocpkg. The .ocpkg is the only artifact authors care about: leaving
324
+ // the loose .wasm next to it just confuses "what do I ship". Only
325
+ // runs on a successful package so a mid-pipeline failure leaves the
326
+ // last good build in place for debugging.
327
+ try {
328
+ fs.unlinkSync(wasmPath);
329
+ } catch (e) {
330
+ // Don't fail the package step over a cleanup miss. The .ocpkg is
331
+ // already written; surface the warning so the author notices the
332
+ // straggler but treat the run as successful.
333
+ if (e.code !== "ENOENT") {
334
+ console.warn(`warning: could not clean up ${path.relative(cwd, wasmPath)}: ${e.message}`);
335
+ }
336
+ }
332
337
  }
333
338
 
334
339
  function* walkFiles(dir) {
335
340
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
336
- // Resolve symlinks so the assets/ <name>-assets/ link the build CLI
337
- // makes doesn't cause us to skip files. statSync follows.
341
+ // statSync (not lstatSync) so a symlinked file or directory in
342
+ // the source tree resolves to its target and we read its contents
343
+ // rather than skipping it.
338
344
  const full = path.join(dir, entry.name);
339
345
  let info;
340
346
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owncast/plugin-sdk",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "SDK for authoring Owncast plugins in JavaScript",
5
5
  "license": "MIT",
6
6
  "author": "Owncast",