@owncast/plugin-sdk 0.6.0 → 0.10.1
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 +6 -6
- package/bin/owncast-plugin.js +39 -37
- package/index.d.ts +172 -89
- package/index.js +212 -279
- package/package.json +2 -1
- package/scripts/postinstall.js +5 -5
- package/slug.js +26 -0
- package/testing.js +15 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owncast/plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "SDK for authoring Owncast plugins in JavaScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Owncast",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"index.js",
|
|
30
30
|
"index.d.ts",
|
|
31
|
+
"slug.js",
|
|
31
32
|
"testing.js",
|
|
32
33
|
"bin/owncast-plugin.js",
|
|
33
34
|
"scripts/postinstall.js"
|
package/scripts/postinstall.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// That's all an author needs: plugins ship source and run on the interpreter
|
|
8
8
|
// engine the host already embeds, so the wasm compiler toolchain (extism-js,
|
|
9
|
-
// binaryen) is NOT downloaded here
|
|
9
|
+
// binaryen) is NOT downloaded here. It's a maintainer-only dependency of the
|
|
10
10
|
// engine build (see engines/install-toolchain.mjs).
|
|
11
11
|
//
|
|
12
12
|
// PoC scope: linux-x86_64 + darwin-arm64 + darwin-x86_64 covered.
|
|
@@ -23,15 +23,15 @@ const HOST_BINARIES_REPO = "owncast/plugin-sdk";
|
|
|
23
23
|
|
|
24
24
|
// The host binaries (owncast-plugin-test/serve) implement the host-function
|
|
25
25
|
// contract that the bundled JS runtime imports. That contract is additive
|
|
26
|
-
// within a major version
|
|
27
|
-
// renamed (a removal is a breaking change that requires a major bump)
|
|
26
|
+
// within a major version. Host functions are only ever added, never removed or
|
|
27
|
+
// renamed (a removal is a breaking change that requires a major bump), so the
|
|
28
28
|
// NEWEST published binary is compatible with every plugin runtime. We therefore
|
|
29
29
|
// fetch the latest release tag rather than deriving one from the npm version.
|
|
30
30
|
//
|
|
31
31
|
// This keeps the binary in lockstep with `@owncast/plugin-sdk@^x` (which npm
|
|
32
32
|
// already floats to the newest compatible runtime) and fixes the old "zero the
|
|
33
33
|
// patch" guess: that fetched v<major>.<minor>.0, which 404'd on JS-only patches
|
|
34
|
-
// and
|
|
34
|
+
// and, when a host change shipped in a patch (e.g. timer support in 0.4.2),
|
|
35
35
|
// fetched a binary too old to satisfy the runtime's imports, breaking
|
|
36
36
|
// `npm test`.
|
|
37
37
|
//
|
|
@@ -156,7 +156,7 @@ async function main() {
|
|
|
156
156
|
fs.chmodSync(dest, 0o755);
|
|
157
157
|
fs.unlinkSync(gz);
|
|
158
158
|
} catch (e) {
|
|
159
|
-
// 404 is expected before the first release
|
|
159
|
+
// 404 is expected before the first release. Other errors get a soft
|
|
160
160
|
// warning so the user sees them but the install still succeeds.
|
|
161
161
|
console.warn(
|
|
162
162
|
`[plugin-sdk] could not fetch ${binary}: ${e.message}\n` +
|
package/slug.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// slugify mirrors the host's Go slugify and the Python SDK's: ASCII letters and
|
|
2
|
+
// digits pass through lowercased, everything else collapses to a single hyphen,
|
|
3
|
+
// and trailing hyphens are trimmed. Shared by the build CLI and the JS test API
|
|
4
|
+
// so a manifest that omits `slug` resolves to the same `<slug>.js` artifact name
|
|
5
|
+
// everywhere. Non-ASCII names (e.g. "Café") degrade noisily (-> "caf"), so pin
|
|
6
|
+
// `slug` in the manifest for accented or non-Latin display names.
|
|
7
|
+
function slugify(input) {
|
|
8
|
+
let out = "";
|
|
9
|
+
let prevHyphen = false;
|
|
10
|
+
for (const ch of input) {
|
|
11
|
+
const code = ch.codePointAt(0);
|
|
12
|
+
let lower = ch;
|
|
13
|
+
if (code >= 65 && code <= 90) lower = String.fromCodePoint(code + 32);
|
|
14
|
+
const lc = lower.codePointAt(0);
|
|
15
|
+
if ((lc >= 97 && lc <= 122) || (lc >= 48 && lc <= 57)) {
|
|
16
|
+
out += lower;
|
|
17
|
+
prevHyphen = false;
|
|
18
|
+
} else if (!prevHyphen && out.length > 0) {
|
|
19
|
+
out += "-";
|
|
20
|
+
prevHyphen = true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return out.replace(/-+$/, "");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { slugify };
|
package/testing.js
CHANGED
|
@@ -28,35 +28,12 @@ const fs = require("fs");
|
|
|
28
28
|
const os = require("os");
|
|
29
29
|
const path = require("path");
|
|
30
30
|
const { execFileSync } = require("child_process");
|
|
31
|
+
const { slugify } = require("./slug");
|
|
31
32
|
|
|
32
33
|
// Find the directory holding the owncast-plugin-test binary. Check for that
|
|
33
34
|
// binary specifically (not just any toolchain file) so we correctly fall
|
|
34
35
|
// through to the dev tools/ dir when postinstall has only fetched part of the
|
|
35
36
|
// toolchain (e.g., on a not-yet-released SDK version).
|
|
36
|
-
// slugifyForTest mirrors the slugify in the build CLI + host SDKs so
|
|
37
|
-
// this entrypoint can locate the .wasm file when a manifest omits
|
|
38
|
-
// `slug`. ASCII letters and digits pass through lowercased;
|
|
39
|
-
// everything else collapses to a single hyphen; trailing hyphens are
|
|
40
|
-
// trimmed.
|
|
41
|
-
function slugifyForTest(input) {
|
|
42
|
-
let out = "";
|
|
43
|
-
let prevHyphen = false;
|
|
44
|
-
for (const ch of input) {
|
|
45
|
-
const code = ch.codePointAt(0);
|
|
46
|
-
let lower = ch;
|
|
47
|
-
if (code >= 65 && code <= 90) lower = String.fromCodePoint(code + 32);
|
|
48
|
-
const lc = lower.codePointAt(0);
|
|
49
|
-
if ((lc >= 97 && lc <= 122) || (lc >= 48 && lc <= 57)) {
|
|
50
|
-
out += lower;
|
|
51
|
-
prevHyphen = false;
|
|
52
|
-
} else if (!prevHyphen && out.length > 0) {
|
|
53
|
-
out += "-";
|
|
54
|
-
prevHyphen = true;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return out.replace(/-+$/, "");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
37
|
function findCacheDir() {
|
|
61
38
|
const candidates = [
|
|
62
39
|
path.join(__dirname, "bin", ".cache"), // installed under node_modules
|
|
@@ -81,7 +58,7 @@ function findCacheDir() {
|
|
|
81
58
|
* Sets `process.exitCode` to non-zero if any scenario failed (never resets a
|
|
82
59
|
* previously-failed code), and returns true on success / false on failure
|
|
83
60
|
* WITHOUT exiting the process. That lets one node process run several test
|
|
84
|
-
* files in a row (see runScenarioFiles)
|
|
61
|
+
* files in a row (see runScenarioFiles). The process ends with the right code
|
|
85
62
|
* once the event loop drains.
|
|
86
63
|
*
|
|
87
64
|
* @param {Array<object>} scenarios, scenario objects: { name, given?, events, expect? }
|
|
@@ -113,20 +90,22 @@ function runScenarios(scenarios, opts = {}) {
|
|
|
113
90
|
console.error("manifest.name is required");
|
|
114
91
|
return fail(2);
|
|
115
92
|
}
|
|
116
|
-
//
|
|
117
|
-
// display name. Derive the slug here the same way the build CLI
|
|
118
|
-
//
|
|
119
|
-
const slug = manifest.slug ||
|
|
93
|
+
// The built artifact + symlink filenames key off slug (the identifier), not
|
|
94
|
+
// the display name. Derive the slug here the same way the build CLI does so
|
|
95
|
+
// this entrypoint works on manifests that omit `slug`.
|
|
96
|
+
const slug = manifest.slug || slugify(manifest.name);
|
|
120
97
|
if (!slug) {
|
|
121
98
|
console.error(
|
|
122
99
|
`could not derive slug from manifest.name ${JSON.stringify(manifest.name)}; set manifest.slug explicitly`,
|
|
123
100
|
);
|
|
124
101
|
return fail(2);
|
|
125
102
|
}
|
|
126
|
-
|
|
127
|
-
|
|
103
|
+
// Plugins ship as source: `owncast-plugin build` bundles src/plugin.{js,ts}
|
|
104
|
+
// into <slug>.js, which is what the host loads. (`npm test` builds first.)
|
|
105
|
+
const scriptPath = path.join(cwd, `${slug}.js`);
|
|
106
|
+
if (!fs.existsSync(scriptPath)) {
|
|
128
107
|
console.error(
|
|
129
|
-
`${slug}.
|
|
108
|
+
`${slug}.js not found at ${scriptPath}, run \`owncast-plugin build\` first`,
|
|
130
109
|
);
|
|
131
110
|
return fail(2);
|
|
132
111
|
}
|
|
@@ -141,32 +120,20 @@ function runScenarios(scenarios, opts = {}) {
|
|
|
141
120
|
return fail(2);
|
|
142
121
|
}
|
|
143
122
|
|
|
144
|
-
// Build a temp project dir that links to the
|
|
145
|
-
// only the scenarios we're running. The binary
|
|
123
|
+
// Build a temp project dir that links to the built script + manifest and
|
|
124
|
+
// contains only the scenarios we're running. The binary auto-discovers them.
|
|
146
125
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "owncast-plugin-test-"));
|
|
147
126
|
try {
|
|
148
127
|
fs.symlinkSync(manifestPath, path.join(tmp, "plugin.manifest.json"));
|
|
149
|
-
fs.symlinkSync(
|
|
128
|
+
fs.symlinkSync(scriptPath, path.join(tmp, `${slug}.js`));
|
|
150
129
|
fs.mkdirSync(path.join(tmp, "__tests__"));
|
|
151
130
|
fs.writeFileSync(
|
|
152
131
|
path.join(tmp, "__tests__", "scenarios.test.json"),
|
|
153
132
|
JSON.stringify(scenarios, null, 2),
|
|
154
133
|
);
|
|
155
134
|
|
|
156
|
-
// Match the build CLI: extism-js (and its wasm-merge/wasm-opt
|
|
157
|
-
// children) needs LD_LIBRARY_PATH on Linux and DYLD_LIBRARY_PATH +
|
|
158
|
-
// DYLD_FALLBACK_LIBRARY_PATH on macOS to find libbinaryen via
|
|
159
|
-
// @rpath. Setting all three is safe on both OSes; the inactive
|
|
160
|
-
// ones are ignored.
|
|
161
|
-
const libDir = path.join(cache, "lib");
|
|
162
|
-
const env = {
|
|
163
|
-
...process.env,
|
|
164
|
-
LD_LIBRARY_PATH: `${libDir}:${process.env.LD_LIBRARY_PATH || ""}`,
|
|
165
|
-
DYLD_LIBRARY_PATH: `${libDir}:${process.env.DYLD_LIBRARY_PATH || ""}`,
|
|
166
|
-
DYLD_FALLBACK_LIBRARY_PATH: `${libDir}:${process.env.DYLD_FALLBACK_LIBRARY_PATH || "/usr/local/lib:/usr/lib"}`,
|
|
167
|
-
};
|
|
168
135
|
try {
|
|
169
|
-
execFileSync(bin, [tmp], { stdio: "inherit"
|
|
136
|
+
execFileSync(bin, [tmp], { stdio: "inherit" });
|
|
170
137
|
} catch (e) {
|
|
171
138
|
return fail(typeof e.status === "number" ? e.status : 1);
|
|
172
139
|
}
|