sammi-next 1.5.0 → 1.5.2
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/.sammi/config.schema.json +78 -78
- package/.sammi/preview.blueprint.html +33 -33
- package/README.md +24 -21
- package/bin/sammi-next.mjs +3 -3
- package/dist/node/cli.mjs +1 -1
- package/dist/node/cli.mjs.map +1 -1
- package/dist/runtime/index.d.ts +348 -2
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -20
- package/dist/runtime/index.js.map +1 -1
- package/dist/shared/config-types.d.ts +0 -1
- package/dist/shared/config-types.d.ts.map +1 -1
- package/dist/shared/config-types.js +0 -1
- package/dist/shared/config-types.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
{
|
|
2
|
-
"type": "object",
|
|
3
|
-
"properties": {
|
|
4
|
-
"$schema": { "type": "string" },
|
|
5
|
-
"id": {
|
|
6
|
-
"description": "Specify a unique id for your extension here. Please use alphanumeric characters, dashes, and underscores only.",
|
|
7
|
-
"type": "string",
|
|
8
|
-
"minLength": 1,
|
|
9
|
-
"pattern": "^[a-z0-9-_]+$"
|
|
10
|
-
},
|
|
11
|
-
"name": {
|
|
12
|
-
"description": "This section names your extension, and is visible in SAMMI Bridge and SAMMI Core. Please use alphanumeric characters and spaces only.",
|
|
13
|
-
"type": "string",
|
|
14
|
-
"minLength": 1,
|
|
15
|
-
"pattern": "^[a-zA-Z0-9 -_]+$"
|
|
16
|
-
},
|
|
17
|
-
"info": {
|
|
18
|
-
"description": "This section is for descriptive text about the extension, e.g. what the extension does. This information is displayed to the users in SAMMI Bridge-Extensions tab when they hover over the extension name inside the list of installed extensions.",
|
|
19
|
-
"type": "string",
|
|
20
|
-
"default": ""
|
|
21
|
-
},
|
|
22
|
-
"version": {
|
|
23
|
-
"description": "Specify your extension version here, using numbers and dots (e.g., 2.01). This is crucial for the automatic version checker in Bridge, which can notify users of updates.",
|
|
24
|
-
"type": "string",
|
|
25
|
-
"minLength": 1,
|
|
26
|
-
"pattern": "^\\d+(?:\\.\\d+)*(?:-.*)?$"
|
|
27
|
-
},
|
|
28
|
-
"entry": {
|
|
29
|
-
"description": "Specify your script.ts path here. In your [insert_script] section, you’re encouraged to write your own TypeScript code.",
|
|
30
|
-
"type": "string",
|
|
31
|
-
"minLength": 1,
|
|
32
|
-
"default": "src/script.ts"
|
|
33
|
-
},
|
|
34
|
-
"external": {
|
|
35
|
-
"description": "Specify your external.html path here. Your [insert_external] section appears inside the extension’s tab in Bridge, and it provides a visual interface for the extension if needed. It’s written in HTML and CSS.",
|
|
36
|
-
"type": "string",
|
|
37
|
-
"minLength": 1,
|
|
38
|
-
"default": "src/external.html"
|
|
39
|
-
},
|
|
40
|
-
"over": {
|
|
41
|
-
"description": "Specify your over.json path here. In your [insert_over] section you simply copy and paste your deck from SAMMI Core you wish to distribute with your extension. When users install your extension, the deck will be automatically added to their SAMMI Core.",
|
|
42
|
-
"type": "string",
|
|
43
|
-
"minLength": 1,
|
|
44
|
-
"default": "src/over.json"
|
|
45
|
-
},
|
|
46
|
-
"out": {
|
|
47
|
-
"description": "Configuration related with the extension building.",
|
|
48
|
-
"type": "object",
|
|
49
|
-
"properties": {
|
|
50
|
-
"dir": {
|
|
51
|
-
"description": "The path where the extension will build to.",
|
|
52
|
-
"type": "string",
|
|
53
|
-
"minLength": 1,
|
|
54
|
-
"pattern": "^[^<>:\"|?*]+$",
|
|
55
|
-
"default": "dist"
|
|
56
|
-
},
|
|
57
|
-
"js": {
|
|
58
|
-
"description": "The file name of the final JavaScript file.",
|
|
59
|
-
"type": "string",
|
|
60
|
-
"minLength": 4,
|
|
61
|
-
"pattern": "^[\\w\\-. ]+\\.js$",
|
|
62
|
-
"default": "extension.js"
|
|
63
|
-
},
|
|
64
|
-
"sef": {
|
|
65
|
-
"description": "The file name of the final SAMMI Extension File.",
|
|
66
|
-
"type": "string",
|
|
67
|
-
"minLength": 5,
|
|
68
|
-
"pattern": "^[\\w\\-. ]+\\.sef$",
|
|
69
|
-
"default": "extension.sef"
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
"required": [],
|
|
73
|
-
"additionalProperties": false
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"required": ["name", "id", "version", "entry"],
|
|
77
|
-
"additionalProperties": false
|
|
78
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"properties": {
|
|
4
|
+
"$schema": { "type": "string" },
|
|
5
|
+
"id": {
|
|
6
|
+
"description": "Specify a unique id for your extension here. Please use alphanumeric characters, dashes, and underscores only.",
|
|
7
|
+
"type": "string",
|
|
8
|
+
"minLength": 1,
|
|
9
|
+
"pattern": "^[a-z0-9-_]+$"
|
|
10
|
+
},
|
|
11
|
+
"name": {
|
|
12
|
+
"description": "This section names your extension, and is visible in SAMMI Bridge and SAMMI Core. Please use alphanumeric characters and spaces only.",
|
|
13
|
+
"type": "string",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"pattern": "^[a-zA-Z0-9 -_]+$"
|
|
16
|
+
},
|
|
17
|
+
"info": {
|
|
18
|
+
"description": "This section is for descriptive text about the extension, e.g. what the extension does. This information is displayed to the users in SAMMI Bridge-Extensions tab when they hover over the extension name inside the list of installed extensions.",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"default": ""
|
|
21
|
+
},
|
|
22
|
+
"version": {
|
|
23
|
+
"description": "Specify your extension version here, using numbers and dots (e.g., 2.01). This is crucial for the automatic version checker in Bridge, which can notify users of updates.",
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"pattern": "^\\d+(?:\\.\\d+)*(?:-.*)?$"
|
|
27
|
+
},
|
|
28
|
+
"entry": {
|
|
29
|
+
"description": "Specify your script.ts path here. In your [insert_script] section, you’re encouraged to write your own TypeScript code.",
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1,
|
|
32
|
+
"default": "src/script.ts"
|
|
33
|
+
},
|
|
34
|
+
"external": {
|
|
35
|
+
"description": "Specify your external.html path here. Your [insert_external] section appears inside the extension’s tab in Bridge, and it provides a visual interface for the extension if needed. It’s written in HTML and CSS.",
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1,
|
|
38
|
+
"default": "src/external.html"
|
|
39
|
+
},
|
|
40
|
+
"over": {
|
|
41
|
+
"description": "Specify your over.json path here. In your [insert_over] section you simply copy and paste your deck from SAMMI Core you wish to distribute with your extension. When users install your extension, the deck will be automatically added to their SAMMI Core.",
|
|
42
|
+
"type": "string",
|
|
43
|
+
"minLength": 1,
|
|
44
|
+
"default": "src/over.json"
|
|
45
|
+
},
|
|
46
|
+
"out": {
|
|
47
|
+
"description": "Configuration related with the extension building.",
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"dir": {
|
|
51
|
+
"description": "The path where the extension will build to.",
|
|
52
|
+
"type": "string",
|
|
53
|
+
"minLength": 1,
|
|
54
|
+
"pattern": "^[^<>:\"|?*]+$",
|
|
55
|
+
"default": "dist"
|
|
56
|
+
},
|
|
57
|
+
"js": {
|
|
58
|
+
"description": "The file name of the final JavaScript file.",
|
|
59
|
+
"type": "string",
|
|
60
|
+
"minLength": 4,
|
|
61
|
+
"pattern": "^[\\w\\-. ]+\\.js$",
|
|
62
|
+
"default": "extension.js"
|
|
63
|
+
},
|
|
64
|
+
"sef": {
|
|
65
|
+
"description": "The file name of the final SAMMI Extension File.",
|
|
66
|
+
"type": "string",
|
|
67
|
+
"minLength": 5,
|
|
68
|
+
"pattern": "^[\\w\\-. ]+\\.sef$",
|
|
69
|
+
"default": "extension.sef"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"required": [],
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"required": ["name", "id", "version", "entry"],
|
|
77
|
+
"additionalProperties": false
|
|
78
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
<!--For Extension info, read How to make extensions.txt in Extension folder-->
|
|
2
|
-
<!--Bridge V7.30-->
|
|
3
|
-
|
|
4
|
-
<!DOCTYPE html>
|
|
5
|
-
<html lang="en">
|
|
6
|
-
<head>
|
|
7
|
-
<title>SAMMI Bridge Preview</title>
|
|
8
|
-
<meta charset="utf-8">
|
|
9
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
10
|
-
<link rel="shortcut icon" type="image/x-icon" href="https://raw.githubusercontent.com/SAMMISolutions/SAMMI-Bridge/main/favicon.ico">
|
|
11
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/SAMMISolutions/SAMMI-Bridge@main/lib/bootstrap.min.css">
|
|
12
|
-
<style>body{font-family:Lato,sans-serif;background-color:#040b15}h1{font-size:calc(1.2em + 1vw)}a{color:#ffac7c;text-decoration:underline}a:hover{color:#ff6810;text-decoration:underline}.tslCollapse,.tslCollapse a,.tslCollapse:hover{text-decoration:none;color:#fff}.tslCollapse.collapsed:before{content:'Show Options \01F847';width:15px}.tslCollapse:before{content:'Hide Options \01F845';width:15px}.SAMMITestTriggers .form-check-input[type=checkbox]{vertical-align:text-bottom}#footer .tslCollapse.collapsed:before{content:'Show Tabs \01F847';width:15px}#footer .tslCollapse:before{content:'Hide Tabs \01F845';width:15px}#SAMMIcorelog{background:#040b15;padding:5px 15px;display:block;position:relative;float:left;text-align:left;max-height:50%;overflow-y:auto}#debugLog,#debugLogContent{border:none;background-color:rgba(0,0,0,0);box-shadow:none}#debugLog .nav-link{padding:.2rem 1rem}#SAMMIBridge{overflow:hidden;padding:5px;margin:2px;padding:1px}samp{width:200px;word-break:break-all;white-space:normal}input{padding:0;margin:1px 1px}button{padding:2px 5px;margin:3px 0;box-shadow:2 2px #c5c5c5}button:active{background-color:#797979;color:#fff;box-shadow:0 0 #dfdfdf;transform:translateY(1px)}.nav{padding-left:0;margin-bottom:0}.nav-pills .nav-link{font-family:Arial;padding:.4em .6em .1em .6em;margin:2px 1px 0 1px!important;background:#afb1b8;background-color:linear-gradient(0deg,#afb1b8 15%,#cbcbd5 61%);font-weight:700;color:#1f2036;border-radius:5px 5px 0 0;text-shadow:0 1px 2px #c3c3c3;transition:10ms}.nav-pills>li>.nav-link.active{background-image:linear-gradient(to bottom,#761a2c ,#761a2c);border:1px solid #761a2c;color:#fff;border:1px solid #761a2c;text-shadow:1px 1px 3px rgb(0 0 0 / 100%)}.nav-item{z-index:998}.tab-content{background-color:rgba(22,53,100,.769);box-shadow:.5rem .5rem .5rem #00000080;min-height:50px;width:100%;border-radius:3px;overflow:hidden;padding:20px}.draggable-source--is-dragging{opacity:0}@media (max-width:576px){.nav-pills .nav-link{border-radius:5px 5px 0 0;font-weight:700;padding:.2em .4em .1em .4em;font-size:.92em}button{font-size:.93em;padding:2px 5px;margin:3px 0;box-shadow:2 2px #c5c5c5}button:active{background-color:#797979;color:#fff;box-shadow:0 0 #dfdfdf;transform:translateY(1px)}}.btn-primary{background-color:#535861b5;border-color:rgb(72 78 89)}.btn-primary:hover{background-color:#60646ad9;border-color:#60646ad9}.notabs>.tab-pane{display:block!important;opacity:1!important}.notabs button,.notabs input{color:#fff;background-color:#4c4c4c;border-color:#464546}#close-tab-button{display:none;position:absolute;top:0;left:50%;transform:translateX(-50%);font-size:40px;color:rgba(255,255,255,.9);background-color:rgba(5,11,20,.9);padding:10px;border-radius:50%;width:60px;height:60px;line-height:40px;text-align:center;z-index:9998;pointer-events:auto}#close-tab-button.hovered{color:rgba(255,0,0,.9)}.connected{color:#4ad84a}.disconnected{color:#fb4848}.connecting{color:#fbbd48}.opacity-90{opacity:.9}@keyframes shake{0%{transform:translate(0,0) rotate(0)}20%{transform:translate(0,0) rotate(-3deg)}40%{transform:translate(0,0) rotate(3deg)}60%{transform:translate(0,0) rotate(-3deg)}80%{transform:translate(0,0) rotate(3deg)}100%{transform:translate(0,0) rotate(0)}}.shaking{animation:shake .82s cubic-bezier(.36,.07,.19,.97) infinite both;transform-origin:center center}#ExtensionsSortAuthor,#ExtensionsSortDownload,#ExtensionsSortName{cursor:pointer}</style>
|
|
13
|
-
</head>
|
|
14
|
-
|
|
15
|
-
<body><div class="container"><h1 class="text-center mt-1">SAMMI Bridge Preview</h1><div class="row justify-content-center"><div class="col col-auto"><svg id="toclient_circle" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="red" class="bi bi-circle-fill d-md-none me-1" viewBox="0 0 16 16"><circle cx="8" cy="8" r="8"></circle></svg> <span>SAMMI Core</span><span class="d-none d-md-inline-flex me-1">: </span><span id="toclient" class="disconnected d-none d-md-inline-flex">Not connected.</span></div><div class="col col-auto"><i id="extensions_icon" class="fas fa-times-circle d-md-none me-1"></i> <span>Extensions</span><span class="d-none d-md-inline-flex me-1">: </span><span id="extensions_status" class="d-none d-md-inline-flex">No extensions installed.</span></div></div><br><div class="modal fade" id="sammiModalElem" tabindex="-1" aria-labelledby="sammiModalElemLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="sammiModalElemTitle">SAMMI</h5><button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button></div><div class="modal-body" id="sammiModalElemContent">..</div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">OK</button></div></div></div></div><span id="close-tab-button">×</span><div class="row justify-content-center g-0"><ul class="nav nav-pills mb-0" id="extensions-tab" role="tablist"></ul></div><div class="tab-content" id="extensions-tabContent"><div class="tab-pane" id="content-settings" role="tabpanel" title="Settings" data-type="settings"><div class="row pt-3"><h5>Bridge Connection</h5><div class="fs-7 mb-2"><span class="me-2">Version:</span><span name="version"></span></div><div class="fs-7 mb-0"><span class="me-2">Current IP:</span><span name="currentIP"></span></div><div class="fs-7 mb-2"><span class="me-2">Current Port:</span><span name="currentPort"></span></div><div class="col"><div class="row"><label for="inputPassword" class="col-sm-5 col-form-label">IP Address:</label><div class="col-sm-4 w-auto"><input type="text" name="nIPbox" value="127.0.0.1" class="form-control form-control-sm"></div></div><div class="row"><label for="inputPassword" class="col-sm-5 col-form-label">Port:</label><div class="col-sm-4 w-auto"><input type="number" min="0" max="65535" name="nPortBox" value="9425" class="form-control form-control-sm"></div></div><div class="row"><label for="inputPassword" class="col-sm-5 col-form-label">Password (optional):</label><div class="col-sm-4 w-auto"><input type="password" name="nPassBox" size="20" value="" class="form-control form-control-sm"></div></div><div class="mt-1 row"><button type="button" id="cnctbutton" class="btn btn-primary btn-sm mb-2 w-auto ms-3">Connect</button></div></div></div><div class="row pt-3 d-block"><h5>Message Logging</h5><div class="pl-5 ml-5"><div class="form-check form-switch"><input class="form-check-input" type="checkbox" id="dbgBridge" onclick="SAMMIDebugLog(this)"> <label class="form-check-label" for="flexSwitchCheckDefault">SAMMI</label></div><div class="tab-content p-0 mt-2" id="debugLogContent"><div class="tab-pane fade show active" id="SAMMIBridge" role="tabpanel" aria-labelledby="SAMMIBridge-tab"><div id="SAMMIcorelog" class="col col-10 text-wrap">Logging is disabled.</div></div></div></div></div></div><div class="tab-pane non-draggable" id="content-extensions" role="tabpanel" title="Extensions" data-type="default"><div class="row pt-3"><h5>Extensions</h5></div><div class="table-responsive"><table class="table table-sm table-hover table-dark" id="extensionTable" style="color:#afb1b8"><thead style="background-color:#182b50"><tr><th class="pl-5 d-none d-sm-table-cell" id="ExtensionsSortName">Name</th><th class="pl-5" id="ExtensionsSortAuthor">Author</th><th class="pl-5"><span class="d-none d-sm-inline">Current</span> <i class="fas fa-clock d-sm-none"></i></th><th class="pl-5"><span class="d-none d-sm-inline">Latest</span> <i class="fas fa-rocket d-sm-none"></i></th><th class="pl-5" id="ExtensionsSortDownload"><span class="d-none d-sm-inline">Download</span> <i class="fas fa-download d-sm-none"></i></th></tr></thead><tbody id="extensionTableBody"></tbody></table></div></div><div class="tab-pane" id="content-triggers" role="tabpanel" title="Twitch Triggers" data-type="default"><form id="SAMMITestTwitchFollow" class="SAMMITestTriggers"><div class="form-check ps-0 d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Follower</button> <label class="form-check-label me-1">Username:</label> <input type="text" class="form-control form-control-sm w-auto me-2" name="name"></div></form><form id="SAMMITestTwitchSubs" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Sub</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidesubs" role="button" aria-expanded="false" aria-controls="hidesubs"></a><div class="collapse" id="hidesubs"><div class="form-group"><label>Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><label class="me-2">Tier:</label><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Tier 1" checked=""> <label class="form-check-label">Tier 1</label></div><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Tier 2"> <label class="form-check-label">Tier 2</label></div><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Tier 3"> <label class="form-check-label">Tier 3</label></div><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Prime"> <label class="form-check-label">Prime</label></div></div><div class="form-group mb-2"><label class="me-2">Gift Options:</label><div class="form-check form-check-inline"><input type="checkbox" class="form-check-input" name="subgift" value="subgift"> <label class="form-check-label">SubGift</label></div><div class="form-check form-check-inline"><input type="checkbox" class="form-check-input" name="anongift" value="anongift"> <label class="form-check-label">AnonGift</label></div></div><div class="form-group mb-2"><label>Months:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="999" name="submonths" value="1"></div><div class="form-group mb-2"><label>Message:</label> <textarea class="form-control form-control-sm w-75" rows="1" name="submessage"></textarea></div></div></form><form id="SAMMITestTwitchSubGift" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Subs Gift Amount</button> <label class="me-2">Amount:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="999" name="subGiftAmount" value="1"></div></form><form id="SAMMITestTwitchBits" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Bits</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidebits" role="button" aria-expanded="false" aria-controls="hidebits"></a><div class="collapse" id="hidebits"><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Username:</label> <input type="text" name="name" class="form-control form-control-sm w-auto me-2" value=""></div><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Amount of bits:</label> <input type="number" min="1" max="99999" name="bitsamount" class="form-control form-control-sm w-auto me-2" value="10"></div><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Message:</label> <textarea name="bitsmessage" class="form-control form-control-sm w-75" rows="1" style="margin:5px 0">Hello World!</textarea></div></div></form><form id="SAMMITestTwitchPoints" class="SAMMITestTriggers"><div><button type="submit" class="btn btn-primary btn-sm me-1">Test Channel Points</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidepoints" role="button" aria-expanded="false" aria-controls="hidebits"></a></div><div class="collapse" id="hidepoints"><div class="form-group mb-2"><label>Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><label>Redeem Name:</label> <input type="text" class="form-control form-control-sm w-auto mb-1" name="channelPointsName"></div><div class="form-group mb-2"><label>Redeem Cost:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="9999" name="channelPointsCost" value="50"></div><div class="form-group mb-2"><label>Redeem Message:</label> <textarea class="form-control form-control-sm w-75" rows="1" name="channelPointsMsg"></textarea><div class="form-check mt-1"><input type="checkbox" class="form-check-input" name="channelPointsInput"> <label class="form-check-label">User Input Required</label></div></div></div></form><form id="SAMMITestTwitchRaid" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Raid</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hideraid" role="button" aria-expanded="false" aria-controls="hideraid"></a><div class="collapse" id="hideraid"><div class="form-group mb-2"><label>Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><label>Amount:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="999" name="raidAmount" value="5"></div></div></form><form id="SAMMITestTwitchPoll" class="SAMMITestTriggers form-inline"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-2">Test Poll</button> <select class="form-select-sm w-auto me-2" style="height:30px" aria-label="Poll Type" name="pollType"><option value="begin" selected="">Created</option><option value="progress">Voted</option><option value="end">Ended</option><option value="archive">Archived</option></select> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidepoll" role="button" aria-expanded="false" aria-controls="hidepoll"></a></div><div class="collapse mt-2 mb-1" id="hidepoll">Choice amount: <input class="mb-2" type="number" min="2" max="5" name="pollChoiceAmount" value="3"><br>Duration: <input class="mb-2" type="number" min="1" max="3600" name="pollDuration" value="60"><br><input type="checkbox" class="form-check-input" name="pollAllowPoints"> Allow Channel Points</div></form><form id="SAMMITestTwitchPrediction" class="form-inline SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-2">Test Prediction</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Poll Type" name="predictType"><option value="begin" selected="">Created</option><option value="progress">Voted</option><option value="end">Locked</option><option value="archive">Resolved</option></select> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hideprediction" role="button" aria-expanded="false" aria-controls="hideprediction"></a></div><div class="collapse" id="hideprediction"><div class="form-group mb-2"><label>Outcome amount:</label> <input type="number" class="form-control form-control-sm w-auto" min="2" max="10" name="predictChoiceAmount" value="3"></div><div class="form-group mb-2"><label>Duration:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="3600" name="predictionDuration" value="60"></div></div></form><form id="SAMMITestTwitchHypeTrain" class="SAMMITestTriggers"><div class="d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-2">Test Hype Train</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Hype Train Type" name="hypeTrainType"><option value="begin">Begin</option><option value="progress">Progress</option><option value="end">End</option></select> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidehypetrain" role="button" aria-expanded="false" aria-controls="hidebits"></a></div><div class="collapse mb-1" id="hidehypetrain"><div class="form-group mb-2"><label>Current level:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="50" name="hypeTrainLevel" value="3"></div><div class="form-group mb-2"><label>Current goal:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="10000" name="hypeTrainGoal" value="3"></div><div class="form-group mb-2"><label>Goal progress:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="10000" name="hypeTrainProgress" value="3"></div></div></form><form id="SAMMITestTwitchShoutout" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Shoutout Created</button> <label class="me-2">Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div></form><form id="SAMMITestTwitchShoutoutReceive" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Shoutout Received</button> <label class="me-2">From Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div></form><form id="SAMMITestTwitchAdbreak" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Ad Break</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hideadbreak" role="button" aria-expanded="false" aria-controls="hideadbreak"></a><div class="collapse" id="hideadbreak"><div class="form-group mb-2"><label>Requester Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><input type="checkbox" class="form-check-input" name="adbreakAuto"> Is Automatic</div></div></form><form id="SAMMITestTwitchChat" class="SAMMITestTriggers"><div><button type="submit" class="btn btn-primary btn-sm me-1">Test Chat Message</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidechatmsg" role="button" aria-expanded="false" aria-controls="hidechatmsg"></a></div><div class="collapse" id="hidechatmsg"><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Username:</label> <input type="text" class="form-control form-control-sm w-auto me-2" name="name"></div><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Message:</label> <textarea class="form-control form-control-sm w-75" name="chatMsg">Hello World</textarea></div><input type="checkbox" class="form-check-input" name="chatWhisper"> Whisper <input type="checkbox" class="form-check-input" name="chatFirstTime"> 1st Message<br><input type="checkbox" class="form-check-input" name="chatBroadcaster"> Broadcaster <input type="checkbox" class="form-check-input" name="chatMod"> Mod <input type="checkbox" class="form-check-input" name="chatSub"> Subscriber <input type="checkbox" class="form-check-input" name="chatVip"> VIP <input type="checkbox" class="form-check-input" name="chatFounder"> Founder<br>Subscriber Tier: <select name="chatMsgSubTier"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select> Month: <input type="number" min="0" max="12" maxlength="2" oninput="parseInt(this.value)>parseInt(this.max)&&(this.value=this.max)" name="chatMsgSubMonth" value="1"></div></form><form id="SAMMITestTwitchStream" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Stream</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Type" name="streamType"><option value="started" selected="">Started</option><option value="ended">Ended</option></select></div></form><form id="SAMMITestTwitchCharity" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Charity</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Type" name="charityType"><option value="start" selected="">Started</option><option value="progress">Progressed</option><option value="stop">Stopped</option><option value="donate">Donated</option></select></div></form></div><div class="tab-pane" id="content-yttriggers" role="tabpanel" title="YouTube Triggers" data-type="default"><div><h5>YouTube Live Test Triggers</h5></div><div class="align-middle align-items-center"><div><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestSub">Test Subscriber</button></div><div><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestMember">Test Member</button></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestMilestone">Test Member Renewal</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideMilestone" role="button" aria-expanded="false" aria-controls="HideMilestone"></a></div><br><div class="collapse" id="YTLiveHideMilestone"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Level name:</label> <input type="text" class="form-control form-control-sm w-auto me-2" id="YTLiveMilestoneLevel"> <label class="form-check-label me-1">Months:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="99" id="YTLiveMilestoneMonth"></div><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Message:</label> <textarea type="text" rows="1" class="form-control form-control-sm w-75" id="YTLiveMilestoneMsg"></textarea></div></div></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestSuperChat">Test Super Chat</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideSuperChat" role="button" aria-expanded="false" aria-controls="HideSuperChat"></a></div><br><div class="collapse" id="YTLiveHideSuperChat"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Amount:</label> <input type="number" min="1" max="500000000" class="form-control form-control-sm w-auto me-2" id="YTLiveSuperChatAmount"> <label class="form-check-label me-1">Tier:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="9" id="YTLiveSuperChatTier"></div><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Message:</label> <textarea type="text" rows="1" class="form-control form-control-sm w-75" id="YTLiveSuperChatMsg"></textarea></div></div></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestSuperSticker">Test Super Sticker</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideSuperSticker" role="button" aria-expanded="false" aria-controls="HideSuperSticker"></a></div><br><div class="collapse" id="YTLiveHideSuperSticker"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Amount:</label> <input type="number" min="1" max="100000" class="form-control form-control-sm w-auto me-2" id="YTLiveSuperStickerAmount"></div></div></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestChatMessage">Test Chat Message</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideChatMessage" role="button" aria-expanded="false" aria-controls="HideChatMessage"></a></div><br><div class="collapse" id="YTLiveHideChatMessage"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Display Name:</label> <input type="text" class="form-control form-control-sm w-auto me-2" id="YTLiveChatMessageName"></div><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Message:</label> <textarea type="text" rows="1" class="form-control form-control-sm w-75" id="YTLiveChatMessageMsg"></textarea></div><input type="checkbox" id="YTLiveChatMessageBroadcaster"> Broadcaster <input type="checkbox" id="YTLiveChatMessageMod"> Mod <input type="checkbox" id="YTLiveChatMessageMember"> Member <input type="checkbox" id="YTLiveChatMessageVerified"> Verified</div></div></div></div>
|
|
16
|
-
|
|
17
|
-
<!--Your external script will be inserted here-->
|
|
18
|
-
<!--<script src="example.js"></script>-->
|
|
19
|
-
{{EXTERNAL}}
|
|
20
|
-
</div>
|
|
21
|
-
<div class="row justify-content-center mt-3 px-2 mb-5" id="footer"> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#installedextensions" role="button" aria-expanded="false" aria-controls="installedextensions"></a>
|
|
22
|
-
<span class="collapse" id="installedextensions"></span>
|
|
23
|
-
<span id="extensions-tab-buttons" class="mt-2">
|
|
24
|
-
<button id="extensionsshow" class="btn btn-primary btn-sm me-1">Show All Tabs</button> <button id="extensionshide" class="btn btn-primary btn-sm me-1">Hide All Tabs</button> <button id="extensionsresetorder" class="btn btn-primary btn-sm me-1">Reset Tab Order</button> <button onclick="location.reload()" class="btn btn-primary btn-sm me-1">Refresh</button></span>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<script>
|
|
29
|
-
// Your main script will be inserted here
|
|
30
|
-
{{SCRIPT}}
|
|
31
|
-
</script>
|
|
32
|
-
</body></html>
|
|
33
|
-
<!DOCTYPE html>
|
|
1
|
+
<!--For Extension info, read How to make extensions.txt in Extension folder-->
|
|
2
|
+
<!--Bridge V7.30-->
|
|
3
|
+
|
|
4
|
+
<!DOCTYPE html>
|
|
5
|
+
<html lang="en">
|
|
6
|
+
<head>
|
|
7
|
+
<title>SAMMI Bridge Preview</title>
|
|
8
|
+
<meta charset="utf-8">
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
10
|
+
<link rel="shortcut icon" type="image/x-icon" href="https://raw.githubusercontent.com/SAMMISolutions/SAMMI-Bridge/main/favicon.ico">
|
|
11
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/SAMMISolutions/SAMMI-Bridge@main/lib/bootstrap.min.css">
|
|
12
|
+
<style>body{font-family:Lato,sans-serif;background-color:#040b15}h1{font-size:calc(1.2em + 1vw)}a{color:#ffac7c;text-decoration:underline}a:hover{color:#ff6810;text-decoration:underline}.tslCollapse,.tslCollapse a,.tslCollapse:hover{text-decoration:none;color:#fff}.tslCollapse.collapsed:before{content:'Show Options \01F847';width:15px}.tslCollapse:before{content:'Hide Options \01F845';width:15px}.SAMMITestTriggers .form-check-input[type=checkbox]{vertical-align:text-bottom}#footer .tslCollapse.collapsed:before{content:'Show Tabs \01F847';width:15px}#footer .tslCollapse:before{content:'Hide Tabs \01F845';width:15px}#SAMMIcorelog{background:#040b15;padding:5px 15px;display:block;position:relative;float:left;text-align:left;max-height:50%;overflow-y:auto}#debugLog,#debugLogContent{border:none;background-color:rgba(0,0,0,0);box-shadow:none}#debugLog .nav-link{padding:.2rem 1rem}#SAMMIBridge{overflow:hidden;padding:5px;margin:2px;padding:1px}samp{width:200px;word-break:break-all;white-space:normal}input{padding:0;margin:1px 1px}button{padding:2px 5px;margin:3px 0;box-shadow:2 2px #c5c5c5}button:active{background-color:#797979;color:#fff;box-shadow:0 0 #dfdfdf;transform:translateY(1px)}.nav{padding-left:0;margin-bottom:0}.nav-pills .nav-link{font-family:Arial;padding:.4em .6em .1em .6em;margin:2px 1px 0 1px!important;background:#afb1b8;background-color:linear-gradient(0deg,#afb1b8 15%,#cbcbd5 61%);font-weight:700;color:#1f2036;border-radius:5px 5px 0 0;text-shadow:0 1px 2px #c3c3c3;transition:10ms}.nav-pills>li>.nav-link.active{background-image:linear-gradient(to bottom,#761a2c ,#761a2c);border:1px solid #761a2c;color:#fff;border:1px solid #761a2c;text-shadow:1px 1px 3px rgb(0 0 0 / 100%)}.nav-item{z-index:998}.tab-content{background-color:rgba(22,53,100,.769);box-shadow:.5rem .5rem .5rem #00000080;min-height:50px;width:100%;border-radius:3px;overflow:hidden;padding:20px}.draggable-source--is-dragging{opacity:0}@media (max-width:576px){.nav-pills .nav-link{border-radius:5px 5px 0 0;font-weight:700;padding:.2em .4em .1em .4em;font-size:.92em}button{font-size:.93em;padding:2px 5px;margin:3px 0;box-shadow:2 2px #c5c5c5}button:active{background-color:#797979;color:#fff;box-shadow:0 0 #dfdfdf;transform:translateY(1px)}}.btn-primary{background-color:#535861b5;border-color:rgb(72 78 89)}.btn-primary:hover{background-color:#60646ad9;border-color:#60646ad9}.notabs>.tab-pane{display:block!important;opacity:1!important}.notabs button,.notabs input{color:#fff;background-color:#4c4c4c;border-color:#464546}#close-tab-button{display:none;position:absolute;top:0;left:50%;transform:translateX(-50%);font-size:40px;color:rgba(255,255,255,.9);background-color:rgba(5,11,20,.9);padding:10px;border-radius:50%;width:60px;height:60px;line-height:40px;text-align:center;z-index:9998;pointer-events:auto}#close-tab-button.hovered{color:rgba(255,0,0,.9)}.connected{color:#4ad84a}.disconnected{color:#fb4848}.connecting{color:#fbbd48}.opacity-90{opacity:.9}@keyframes shake{0%{transform:translate(0,0) rotate(0)}20%{transform:translate(0,0) rotate(-3deg)}40%{transform:translate(0,0) rotate(3deg)}60%{transform:translate(0,0) rotate(-3deg)}80%{transform:translate(0,0) rotate(3deg)}100%{transform:translate(0,0) rotate(0)}}.shaking{animation:shake .82s cubic-bezier(.36,.07,.19,.97) infinite both;transform-origin:center center}#ExtensionsSortAuthor,#ExtensionsSortDownload,#ExtensionsSortName{cursor:pointer}</style>
|
|
13
|
+
</head>
|
|
14
|
+
|
|
15
|
+
<body><div class="container"><h1 class="text-center mt-1">SAMMI Bridge Preview</h1><div class="row justify-content-center"><div class="col col-auto"><svg id="toclient_circle" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="red" class="bi bi-circle-fill d-md-none me-1" viewBox="0 0 16 16"><circle cx="8" cy="8" r="8"></circle></svg> <span>SAMMI Core</span><span class="d-none d-md-inline-flex me-1">: </span><span id="toclient" class="disconnected d-none d-md-inline-flex">Not connected.</span></div><div class="col col-auto"><i id="extensions_icon" class="fas fa-times-circle d-md-none me-1"></i> <span>Extensions</span><span class="d-none d-md-inline-flex me-1">: </span><span id="extensions_status" class="d-none d-md-inline-flex">No extensions installed.</span></div></div><br><div class="modal fade" id="sammiModalElem" tabindex="-1" aria-labelledby="sammiModalElemLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="sammiModalElemTitle">SAMMI</h5><button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button></div><div class="modal-body" id="sammiModalElemContent">..</div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">OK</button></div></div></div></div><span id="close-tab-button">×</span><div class="row justify-content-center g-0"><ul class="nav nav-pills mb-0" id="extensions-tab" role="tablist"></ul></div><div class="tab-content" id="extensions-tabContent"><div class="tab-pane" id="content-settings" role="tabpanel" title="Settings" data-type="settings"><div class="row pt-3"><h5>Bridge Connection</h5><div class="fs-7 mb-2"><span class="me-2">Version:</span><span name="version"></span></div><div class="fs-7 mb-0"><span class="me-2">Current IP:</span><span name="currentIP"></span></div><div class="fs-7 mb-2"><span class="me-2">Current Port:</span><span name="currentPort"></span></div><div class="col"><div class="row"><label for="inputPassword" class="col-sm-5 col-form-label">IP Address:</label><div class="col-sm-4 w-auto"><input type="text" name="nIPbox" value="127.0.0.1" class="form-control form-control-sm"></div></div><div class="row"><label for="inputPassword" class="col-sm-5 col-form-label">Port:</label><div class="col-sm-4 w-auto"><input type="number" min="0" max="65535" name="nPortBox" value="9425" class="form-control form-control-sm"></div></div><div class="row"><label for="inputPassword" class="col-sm-5 col-form-label">Password (optional):</label><div class="col-sm-4 w-auto"><input type="password" name="nPassBox" size="20" value="" class="form-control form-control-sm"></div></div><div class="mt-1 row"><button type="button" id="cnctbutton" class="btn btn-primary btn-sm mb-2 w-auto ms-3">Connect</button></div></div></div><div class="row pt-3 d-block"><h5>Message Logging</h5><div class="pl-5 ml-5"><div class="form-check form-switch"><input class="form-check-input" type="checkbox" id="dbgBridge" onclick="SAMMIDebugLog(this)"> <label class="form-check-label" for="flexSwitchCheckDefault">SAMMI</label></div><div class="tab-content p-0 mt-2" id="debugLogContent"><div class="tab-pane fade show active" id="SAMMIBridge" role="tabpanel" aria-labelledby="SAMMIBridge-tab"><div id="SAMMIcorelog" class="col col-10 text-wrap">Logging is disabled.</div></div></div></div></div></div><div class="tab-pane non-draggable" id="content-extensions" role="tabpanel" title="Extensions" data-type="default"><div class="row pt-3"><h5>Extensions</h5></div><div class="table-responsive"><table class="table table-sm table-hover table-dark" id="extensionTable" style="color:#afb1b8"><thead style="background-color:#182b50"><tr><th class="pl-5 d-none d-sm-table-cell" id="ExtensionsSortName">Name</th><th class="pl-5" id="ExtensionsSortAuthor">Author</th><th class="pl-5"><span class="d-none d-sm-inline">Current</span> <i class="fas fa-clock d-sm-none"></i></th><th class="pl-5"><span class="d-none d-sm-inline">Latest</span> <i class="fas fa-rocket d-sm-none"></i></th><th class="pl-5" id="ExtensionsSortDownload"><span class="d-none d-sm-inline">Download</span> <i class="fas fa-download d-sm-none"></i></th></tr></thead><tbody id="extensionTableBody"></tbody></table></div></div><div class="tab-pane" id="content-triggers" role="tabpanel" title="Twitch Triggers" data-type="default"><form id="SAMMITestTwitchFollow" class="SAMMITestTriggers"><div class="form-check ps-0 d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Follower</button> <label class="form-check-label me-1">Username:</label> <input type="text" class="form-control form-control-sm w-auto me-2" name="name"></div></form><form id="SAMMITestTwitchSubs" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Sub</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidesubs" role="button" aria-expanded="false" aria-controls="hidesubs"></a><div class="collapse" id="hidesubs"><div class="form-group"><label>Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><label class="me-2">Tier:</label><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Tier 1" checked=""> <label class="form-check-label">Tier 1</label></div><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Tier 2"> <label class="form-check-label">Tier 2</label></div><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Tier 3"> <label class="form-check-label">Tier 3</label></div><div class="form-check form-check-inline"><input type="radio" class="form-check-input" name="tier" value="Prime"> <label class="form-check-label">Prime</label></div></div><div class="form-group mb-2"><label class="me-2">Gift Options:</label><div class="form-check form-check-inline"><input type="checkbox" class="form-check-input" name="subgift" value="subgift"> <label class="form-check-label">SubGift</label></div><div class="form-check form-check-inline"><input type="checkbox" class="form-check-input" name="anongift" value="anongift"> <label class="form-check-label">AnonGift</label></div></div><div class="form-group mb-2"><label>Months:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="999" name="submonths" value="1"></div><div class="form-group mb-2"><label>Message:</label> <textarea class="form-control form-control-sm w-75" rows="1" name="submessage"></textarea></div></div></form><form id="SAMMITestTwitchSubGift" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Subs Gift Amount</button> <label class="me-2">Amount:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="999" name="subGiftAmount" value="1"></div></form><form id="SAMMITestTwitchBits" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Bits</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidebits" role="button" aria-expanded="false" aria-controls="hidebits"></a><div class="collapse" id="hidebits"><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Username:</label> <input type="text" name="name" class="form-control form-control-sm w-auto me-2" value=""></div><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Amount of bits:</label> <input type="number" min="1" max="99999" name="bitsamount" class="form-control form-control-sm w-auto me-2" value="10"></div><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Message:</label> <textarea name="bitsmessage" class="form-control form-control-sm w-75" rows="1" style="margin:5px 0">Hello World!</textarea></div></div></form><form id="SAMMITestTwitchPoints" class="SAMMITestTriggers"><div><button type="submit" class="btn btn-primary btn-sm me-1">Test Channel Points</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidepoints" role="button" aria-expanded="false" aria-controls="hidebits"></a></div><div class="collapse" id="hidepoints"><div class="form-group mb-2"><label>Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><label>Redeem Name:</label> <input type="text" class="form-control form-control-sm w-auto mb-1" name="channelPointsName"></div><div class="form-group mb-2"><label>Redeem Cost:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="9999" name="channelPointsCost" value="50"></div><div class="form-group mb-2"><label>Redeem Message:</label> <textarea class="form-control form-control-sm w-75" rows="1" name="channelPointsMsg"></textarea><div class="form-check mt-1"><input type="checkbox" class="form-check-input" name="channelPointsInput"> <label class="form-check-label">User Input Required</label></div></div></div></form><form id="SAMMITestTwitchRaid" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Raid</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hideraid" role="button" aria-expanded="false" aria-controls="hideraid"></a><div class="collapse" id="hideraid"><div class="form-group mb-2"><label>Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><label>Amount:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="999" name="raidAmount" value="5"></div></div></form><form id="SAMMITestTwitchPoll" class="SAMMITestTriggers form-inline"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-2">Test Poll</button> <select class="form-select-sm w-auto me-2" style="height:30px" aria-label="Poll Type" name="pollType"><option value="begin" selected="">Created</option><option value="progress">Voted</option><option value="end">Ended</option><option value="archive">Archived</option></select> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidepoll" role="button" aria-expanded="false" aria-controls="hidepoll"></a></div><div class="collapse mt-2 mb-1" id="hidepoll">Choice amount: <input class="mb-2" type="number" min="2" max="5" name="pollChoiceAmount" value="3"><br>Duration: <input class="mb-2" type="number" min="1" max="3600" name="pollDuration" value="60"><br><input type="checkbox" class="form-check-input" name="pollAllowPoints"> Allow Channel Points</div></form><form id="SAMMITestTwitchPrediction" class="form-inline SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-2">Test Prediction</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Poll Type" name="predictType"><option value="begin" selected="">Created</option><option value="progress">Voted</option><option value="end">Locked</option><option value="archive">Resolved</option></select> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hideprediction" role="button" aria-expanded="false" aria-controls="hideprediction"></a></div><div class="collapse" id="hideprediction"><div class="form-group mb-2"><label>Outcome amount:</label> <input type="number" class="form-control form-control-sm w-auto" min="2" max="10" name="predictChoiceAmount" value="3"></div><div class="form-group mb-2"><label>Duration:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="3600" name="predictionDuration" value="60"></div></div></form><form id="SAMMITestTwitchHypeTrain" class="SAMMITestTriggers"><div class="d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-2">Test Hype Train</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Hype Train Type" name="hypeTrainType"><option value="begin">Begin</option><option value="progress">Progress</option><option value="end">End</option></select> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidehypetrain" role="button" aria-expanded="false" aria-controls="hidebits"></a></div><div class="collapse mb-1" id="hidehypetrain"><div class="form-group mb-2"><label>Current level:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="50" name="hypeTrainLevel" value="3"></div><div class="form-group mb-2"><label>Current goal:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="10000" name="hypeTrainGoal" value="3"></div><div class="form-group mb-2"><label>Goal progress:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="10000" name="hypeTrainProgress" value="3"></div></div></form><form id="SAMMITestTwitchShoutout" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Shoutout Created</button> <label class="me-2">Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div></form><form id="SAMMITestTwitchShoutoutReceive" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Shoutout Received</button> <label class="me-2">From Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div></form><form id="SAMMITestTwitchAdbreak" class="SAMMITestTriggers"><button type="submit" class="btn btn-primary btn-sm me-1">Test Ad Break</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hideadbreak" role="button" aria-expanded="false" aria-controls="hideadbreak"></a><div class="collapse" id="hideadbreak"><div class="form-group mb-2"><label>Requester Username:</label> <input type="text" class="form-control form-control-sm w-auto" name="name"></div><div class="form-group mb-2"><input type="checkbox" class="form-check-input" name="adbreakAuto"> Is Automatic</div></div></form><form id="SAMMITestTwitchChat" class="SAMMITestTriggers"><div><button type="submit" class="btn btn-primary btn-sm me-1">Test Chat Message</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#hidechatmsg" role="button" aria-expanded="false" aria-controls="hidechatmsg"></a></div><div class="collapse" id="hidechatmsg"><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Username:</label> <input type="text" class="form-control form-control-sm w-auto me-2" name="name"></div><div class="form-check ps-0 d-flex align-items-center mb-2"><label class="form-check-label me-1">Message:</label> <textarea class="form-control form-control-sm w-75" name="chatMsg">Hello World</textarea></div><input type="checkbox" class="form-check-input" name="chatWhisper"> Whisper <input type="checkbox" class="form-check-input" name="chatFirstTime"> 1st Message<br><input type="checkbox" class="form-check-input" name="chatBroadcaster"> Broadcaster <input type="checkbox" class="form-check-input" name="chatMod"> Mod <input type="checkbox" class="form-check-input" name="chatSub"> Subscriber <input type="checkbox" class="form-check-input" name="chatVip"> VIP <input type="checkbox" class="form-check-input" name="chatFounder"> Founder<br>Subscriber Tier: <select name="chatMsgSubTier"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select> Month: <input type="number" min="0" max="12" maxlength="2" oninput="parseInt(this.value)>parseInt(this.max)&&(this.value=this.max)" name="chatMsgSubMonth" value="1"></div></form><form id="SAMMITestTwitchStream" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Stream</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Type" name="streamType"><option value="started" selected="">Started</option><option value="ended">Ended</option></select></div></form><form id="SAMMITestTwitchCharity" class="SAMMITestTriggers"><div class="form-group d-flex align-items-center"><button type="submit" class="btn btn-primary btn-sm me-1">Test Charity</button> <select class="form-select form-select-sm w-auto me-2" aria-label="Type" name="charityType"><option value="start" selected="">Started</option><option value="progress">Progressed</option><option value="stop">Stopped</option><option value="donate">Donated</option></select></div></form></div><div class="tab-pane" id="content-yttriggers" role="tabpanel" title="YouTube Triggers" data-type="default"><div><h5>YouTube Live Test Triggers</h5></div><div class="align-middle align-items-center"><div><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestSub">Test Subscriber</button></div><div><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestMember">Test Member</button></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestMilestone">Test Member Renewal</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideMilestone" role="button" aria-expanded="false" aria-controls="HideMilestone"></a></div><br><div class="collapse" id="YTLiveHideMilestone"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Level name:</label> <input type="text" class="form-control form-control-sm w-auto me-2" id="YTLiveMilestoneLevel"> <label class="form-check-label me-1">Months:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="99" id="YTLiveMilestoneMonth"></div><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Message:</label> <textarea type="text" rows="1" class="form-control form-control-sm w-75" id="YTLiveMilestoneMsg"></textarea></div></div></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestSuperChat">Test Super Chat</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideSuperChat" role="button" aria-expanded="false" aria-controls="HideSuperChat"></a></div><br><div class="collapse" id="YTLiveHideSuperChat"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Amount:</label> <input type="number" min="1" max="500000000" class="form-control form-control-sm w-auto me-2" id="YTLiveSuperChatAmount"> <label class="form-check-label me-1">Tier:</label> <input type="number" class="form-control form-control-sm w-auto" min="1" max="9" id="YTLiveSuperChatTier"></div><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Message:</label> <textarea type="text" rows="1" class="form-control form-control-sm w-75" id="YTLiveSuperChatMsg"></textarea></div></div></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestSuperSticker">Test Super Sticker</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideSuperSticker" role="button" aria-expanded="false" aria-controls="HideSuperSticker"></a></div><br><div class="collapse" id="YTLiveHideSuperSticker"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Amount:</label> <input type="number" min="1" max="100000" class="form-control form-control-sm w-auto me-2" id="YTLiveSuperStickerAmount"></div></div></div><div><div class="btn-group align-items-center"><button style="margin-right:10px" type="button" class="btn btn-primary btn-sm" id="ytLiveTestChatMessage">Test Chat Message</button> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#YTLiveHideChatMessage" role="button" aria-expanded="false" aria-controls="HideChatMessage"></a></div><br><div class="collapse" id="YTLiveHideChatMessage"><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Display Name:</label> <input type="text" class="form-control form-control-sm w-auto me-2" id="YTLiveChatMessageName"></div><div class="form-check ps-0 d-flex align-items-center"><label class="form-check-label me-1">Message:</label> <textarea type="text" rows="1" class="form-control form-control-sm w-75" id="YTLiveChatMessageMsg"></textarea></div><input type="checkbox" id="YTLiveChatMessageBroadcaster"> Broadcaster <input type="checkbox" id="YTLiveChatMessageMod"> Mod <input type="checkbox" id="YTLiveChatMessageMember"> Member <input type="checkbox" id="YTLiveChatMessageVerified"> Verified</div></div></div></div>
|
|
16
|
+
|
|
17
|
+
<!--Your external script will be inserted here-->
|
|
18
|
+
<!--<script src="example.js"></script>-->
|
|
19
|
+
{{EXTERNAL}}
|
|
20
|
+
</div>
|
|
21
|
+
<div class="row justify-content-center mt-3 px-2 mb-5" id="footer"> <a class="tslCollapse collapsed" data-bs-toggle="collapse" href="#installedextensions" role="button" aria-expanded="false" aria-controls="installedextensions"></a>
|
|
22
|
+
<span class="collapse" id="installedextensions"></span>
|
|
23
|
+
<span id="extensions-tab-buttons" class="mt-2">
|
|
24
|
+
<button id="extensionsshow" class="btn btn-primary btn-sm me-1">Show All Tabs</button> <button id="extensionshide" class="btn btn-primary btn-sm me-1">Hide All Tabs</button> <button id="extensionsresetorder" class="btn btn-primary btn-sm me-1">Reset Tab Order</button> <button onclick="location.reload()" class="btn btn-primary btn-sm me-1">Refresh</button></span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
// Your main script will be inserted here
|
|
30
|
+
{{SCRIPT}}
|
|
31
|
+
</script>
|
|
32
|
+
</body></html>
|
|
33
|
+
<!DOCTYPE html>
|
package/README.md
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
# SAMMI Next
|
|
2
|
-
|
|
3
|
-
> Modern builder and runtime for SAMMI Extensions
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
- Extension Preview
|
|
7
|
-
- Helper functions
|
|
8
|
-
- Rich Features
|
|
9
|
-
- Optimize Build
|
|
10
|
-
- Fully Typed APIs
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- A
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
# SAMMI Next
|
|
2
|
+
|
|
3
|
+
> Modern builder and runtime for SAMMI Extensions
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- Extension Preview
|
|
7
|
+
- Helper functions
|
|
8
|
+
- Rich Features
|
|
9
|
+
- Optimize Build
|
|
10
|
+
- Fully Typed APIs
|
|
11
|
+
|
|
12
|
+
## Description
|
|
13
|
+
SAMMI Next is a [SAMMI] Bridge extensions build tool inspired on [Vite] that significantly improves the extension development experience. It consists of two major parts:
|
|
14
|
+
- A runtime API with helper functions to develop a [SAMMI] extension with the SAMMI Next format.
|
|
15
|
+
- A cli that bundles your code with [tsdown], pre-configured to output highly optimized code for production.
|
|
16
|
+
|
|
17
|
+
## Import Globals
|
|
18
|
+
```TypeScript
|
|
19
|
+
/// <reference types="sammi-next" />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
[SAMMI]: https://sammi.solutions/
|
|
23
|
+
[Vite]: https://vite.dev/
|
|
24
|
+
[tsdown]: https://tsdown.dev/
|
package/bin/sammi-next.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import '../dist/node/cli.mjs';
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import '../dist/node/cli.mjs';
|
package/dist/node/cli.mjs
CHANGED
package/dist/node/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["path","VERSION","res","VERSION"],"sources":["../../package.json","../../src/node/constants.ts","../../src/node/utils.ts","../../src/node/build.ts","../../src/node/config.ts","../../src/node/cli.ts"],"sourcesContent":["","import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport colors from 'picocolors'\n\nexport { version as VERSION } from '../../package.json';\n\nexport const DEFAULT_CONFIG_EXTENSIONS: string[] = [\n '.mjs',\n '.js',\n '.mts',\n '.ts',\n]\n\nexport const GLOBAL_NAME = \"SAMMIExtensions\";\n\nexport const BUILD_PREFIX = colors.blue(\"[sammi-next]\");\nexport const GREEN_CHECK = colors.green('✔');\nexport const RED_X = colors.red('✗');\n\nfunction findPackageDir() {\n let initPath = fileURLToPath(import.meta.url);\n while (!initPath.endsWith('sammi-next')) {\n initPath = path.resolve(initPath, '..');\n }\n return initPath;\n}\nexport const SAMMI_NEXT_PACKAGE_DIR = findPackageDir();\n","export function displayTime(time: number): string {\n if (time < 1_000)\n return `${time}ms`;\n\n time = time / 1_000;\n\n if (time < 60)\n return `${time.toFixed(2)}s`\n\n const mins = Math.floor(time / 60);\n const seconds = Math.round(time % 60);\n\n if (seconds === 60)\n return `${mins + 1}m`\n\n return `${mins}m${seconds < 1 ? '' : ` ${seconds}s`}`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport colors from 'picocolors';\nimport chokidar from 'chokidar';\nimport { ResolvedExtensionConfig } from \"@shared/config-types\";\nimport { build, TsdownBundle } from \"tsdown\";\nimport { BUILD_PREFIX, GLOBAL_NAME, GREEN_CHECK, RED_X, SAMMI_NEXT_PACKAGE_DIR, VERSION } from \"./constants\";\nimport { displayTime } from './utils';\n// import nodePolyfills from '@rolldown/plugin-node-polyfills';\n\nexport enum BuildMode {\n DEV,\n PRODUCTION,\n}\n\nexport const BuildModes = Object.keys(BuildMode).filter(key => isNaN(Number(key)));\n\nexport interface BuildOptions {\n config: ResolvedExtensionConfig;\n rootDir: string;\n mode?: BuildMode;\n}\n\nexport type ResolvedBuildOptions = Required<BuildOptions>;\n\nfunction readOptionalFile(path: string): string | undefined {\n if (!fs.existsSync(path)) return;\n\n return fs.readFileSync(path, 'utf-8');\n}\n\nconst CommandRE = /\\w+\\(\\w+,\\s*{\\s*default:/gm;\n\nfunction generateSEF(options: ResolvedBuildOptions): string {\n const { config, rootDir, mode } = options;\n const content = [];\n\n const required_files: {\n header: string,\n content: string,\n }[] = [\n {\n header: \"extension_name\",\n content: config.name,\n },\n {\n header: \"extension_info\",\n content: config.info,\n },\n {\n header: \"extension_version\",\n content: config.version,\n },\n ]\n\n for (const field of required_files) {\n content.push(`[${field.header}]`, field.content, \"\");\n }\n\n const external = readOptionalFile(config.external);\n content.push(\"[insert_external]\", external ? `<div id=\"${config.id}-external\">${external}</div>` : \"\", \"\");\n\n const js_script = fs.readFileSync(path.join(rootDir, config.out.dir, config.out.js), \"utf-8\");\n content.push(\"[insert_command]\", CommandRE.test(js_script) ? `${GLOBAL_NAME}.${config.id}.default()` : \"\", \"\");\n content.push(\"[insert_hook]\", \"\", \"\") // TODO: maybe add hook retro-compatibility\n content.push(\"[insert_script]\", js_script, \"\");\n\n let over = readOptionalFile(config.over);\n if (over && mode === BuildMode.PRODUCTION) {\n over = JSON.stringify(JSON.parse(over));\n }\n content.push(\"[insert_over]\", over && over != \"{}\" ? over : \"\", \"\");\n return content.join(\"\\n\");\n}\n\nfunction generatePreview(options: ResolvedBuildOptions): string {\n const { config, rootDir } = options;\n\n const external = readOptionalFile(config.external);\n const js_script = fs.readFileSync(path.join(rootDir, config.out.dir, config.out.js), \"utf-8\");\n return fs\n .readFileSync(path.join(SAMMI_NEXT_PACKAGE_DIR, \".sammi\", \"preview.blueprint.html\"), \"utf-8\")\n .replace(/{{EXTERNAL}}/g, external ? `<div id=\"${config.id}-external\">${external}</div>` : \"\")\n .replace(/{{SCRIPT}}/g, js_script);\n}\n\nasync function buildOnce(options: ResolvedBuildOptions) {\n const { config, rootDir, mode } = options;\n\n const startTime = Date.now();\n const bundle = await build({\n entry: [config.entry],\n outDir: path.join(rootDir, config.out.dir),\n platform: 'browser',\n format: 'iife',\n target: ['es2022'],\n sourcemap: false,\n minify: mode === BuildMode.PRODUCTION,\n banner: {\n js: `/* ${config.name} v${config.version} - Built with SAMMI Next v${VERSION} */`,\n },\n noExternal: ['**'],\n outputOptions: {\n entryFileNames: config.out.js,\n extend: true,\n name: `${GLOBAL_NAME}.${config.id}`,\n exports: 'named',\n },\n // plugins: [\n // nodePolyfills(),\n // ],\n });\n const tsdownTime = Date.now();\n console.info(GREEN_CHECK, BUILD_PREFIX, `built ${config.out.js} in ${displayTime(tsdownTime - startTime)}`);\n\n fs.writeFileSync(path.join(rootDir, config.out.dir, config.out.sef), generateSEF(options), 'utf-8');\n const sefTime = Date.now();\n console.info(GREEN_CHECK, BUILD_PREFIX, `built ${config.out.sef} in ${displayTime(sefTime - tsdownTime)}`);\n\n fs.writeFileSync(path.join(rootDir, config.out.dir, \"preview.html\"), generatePreview(options), 'utf-8');\n const previewTime = Date.now();\n console.info(GREEN_CHECK, BUILD_PREFIX, `built preview.html in ${displayTime(previewTime - sefTime)}`);\n return { bundle, startTime };\n}\n\nexport async function buildExtension(options: ResolvedBuildOptions) {\n const { config, mode } = options;\n\n console.info(\n colors.cyan(\n `SAMMI Next v${VERSION} ${colors.green(\n `building \"${config.name}\" extension in ${BuildMode[mode].toLowerCase()} mode...`\n )}`\n ),\n );\n\n let bundle: TsdownBundle[] | undefined;\n let startTime: number | undefined;\n\n try {\n const res = await buildOnce(options);\n bundle = res.bundle;\n startTime = res.startTime;\n\n if (options.mode !== BuildMode.DEV) return bundle;\n\n console.info(BUILD_PREFIX, colors.cyan(\"watching for file changes...\"));\n\n const watchPaths = [\n path.dirname(config.entry),\n config.external,\n config.over,\n ].filter(Boolean);\n\n const watcher = chokidar.watch(watchPaths, { ignoreInitial: true });\n let timer: NodeJS.Timeout | null = null;\n\n watcher.on('all', (event, p) => {\n console.info(colors.cyan(`${event}: ${p}`));\n if (timer)\n clearTimeout(timer);\n\n timer = setTimeout(() => {\n buildOnce(options).then(res => {\n bundle = res.bundle;\n startTime = res.startTime;\n }).catch(e => console.error(e));\n }, 100);\n });\n\n process.on('SIGINT', () => {\n console.info(\"\\nStopping watch mode...\");\n watcher\n .close()\n .then(() => process.exit(0))\n .catch(e => {\n console.error(e);\n process.exit(1);\n });\n });\n return watcher;\n } catch (error) {\n if (startTime) {\n console.error(RED_X, BUILD_PREFIX, `Build failed in ${displayTime(Date.now() - startTime)}`);\n startTime = undefined;\n }\n throw error;\n }\n}\n","\n\nimport { ExtensionConfig, ResolvedExtensionConfig } from \"@shared/config-types\";\nimport Ajv, { JSONSchemaType } from \"ajv\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { DEFAULT_CONFIG_EXTENSIONS } from \"./constants\";\nimport { BuildCLIOptions } from \"./cli\";\nimport { BuildModes, ResolvedBuildOptions } from \"./build\";\n\nconst ajv = new Ajv();\n\najv.addKeyword({\n keyword: \"fileExists\",\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n validate: (_schema: any, data: any) => {\n if (!data) return true;\n\n return fs.existsSync(data as string);\n },\n errors: false\n});\n\nconst schema: JSONSchemaType<ExtensionConfig> = {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n minLength: 1,\n pattern: \"^[a-zA-Z0-9-_]+$\",\n },\n name: {\n type: \"string\",\n minLength: 1,\n pattern: \"^[a-zA-Z0-9 -_]+$\",\n },\n info: {\n type: \"string\",\n default: \"\",\n nullable: true,\n },\n version: {\n type: \"string\",\n minLength: 1,\n pattern: \"^\\\\d+(?:\\\\.\\\\d+)*(?:-.*)?$\",\n },\n entry: {\n type: \"string\",\n minLength: 1,\n fileExists: true,\n },\n external: {\n type: \"string\",\n minLength: 1,\n fileExists: true,\n nullable: true,\n },\n over: {\n type: \"string\",\n minLength: 1,\n fileExists: true,\n nullable: true,\n },\n out: {\n type: \"object\",\n properties: {\n dir: {\n type: \"string\",\n minLength: 1,\n pattern: \"^[^<>:\\\"|?*]+$\",\n default: \"dist\",\n nullable: true,\n },\n js: {\n type: \"string\",\n minLength: 4,\n pattern: \"^[\\\\w\\\\-. ]+\\\\.js$\",\n default: \"extension.js\",\n nullable: true,\n },\n sef: {\n type: \"string\",\n minLength: 5,\n pattern: \"^[\\\\w\\\\-. ]+\\\\.sef$\",\n default: \"extension.sef\",\n nullable: true,\n }\n },\n required: [],\n nullable: true,\n additionalProperties: false,\n }\n },\n required: [\"name\", \"id\", \"version\", \"entry\"],\n additionalProperties: false,\n};\n\nconst configValidator = ajv.compile(schema);\n\nexport async function loadConfig(rootDir: string) {\n for (const ext of DEFAULT_CONFIG_EXTENSIONS) {\n const configPath = path.join(rootDir, `sammi.config${ext}`);\n\n if (!fs.existsSync(configPath)) continue;\n\n try {\n const { createJiti } = await import('jiti');\n const jiti = createJiti(rootDir, {\n interopDefault: true,\n moduleCache: true,\n });\n\n const config = await jiti.import(configPath, { default: true });\n\n return validateExtensionConfig(config, configPath);\n } catch (error) {\n throw new Error(`Error loading ${configPath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n const jsonPath = path.join(rootDir, 'sammi.config.json');\n if (fs.existsSync(jsonPath)) {\n try {\n const raw = fs.readFileSync(jsonPath, 'utf-8');\n const config = JSON.parse(raw) as unknown;\n return validateExtensionConfig(config, jsonPath);\n } catch (error) {\n throw new Error(`Error loading ${jsonPath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n throw new Error('SAMMI Next extension config file not found in the root dir.');\n}\n\n\nfunction validateExtensionConfig(config: unknown, configPath: string): ExtensionConfig {\n if (!configValidator(config)) {\n const errors = configValidator.errors?.map(err => ` - ${err.instancePath} ${err.message}`).join('\\n');\n throw new Error(`Invalid config from ${configPath}:\\n${errors}`);\n }\n\n return config;\n}\n\nexport function resolveExtensionConfig(config: ExtensionConfig, rootDir: string): ResolvedExtensionConfig {\n const resolved: ResolvedExtensionConfig = {\n id: config.id,\n name: config.name,\n version: config.version,\n info: config.info || '',\n entry: path.resolve(rootDir, config.entry),\n external: config.external ? path.resolve(rootDir, config.external) : '',\n over: config.over ? path.resolve(rootDir, config.over) : '',\n out: {\n dir: config.out?.dir || 'dist',\n js: config.out?.js || 'extension.js',\n sef: config.out?.sef || 'extension.sef',\n },\n };\n\n if (!fs.existsSync(resolved.entry))\n throw new Error(`Entry file not found: ${resolved.entry}`);\n\n if (resolved.external && !fs.existsSync(resolved.external))\n throw new Error(`External file not found: ${resolved.external}`);\n\n if (resolved.over && !fs.existsSync(resolved.over))\n throw new Error(`Over file not found: ${resolved.over}`);\n\n return resolved;\n}\n\nexport async function resolveBuildConfig(\n root: string | undefined,\n command: string,\n build_cli: BuildCLIOptions,\n) {\n const mode = BuildModes.findIndex(m => {\n return m.toLowerCase() === command.toLowerCase();\n });\n if (mode < 0)\n throw new Error(`Invalid mode: ${command}. It must be one of: ${BuildModes.join(', ')}`);\n\n const rootDir = root ?? process.cwd();\n const config = await loadConfig(rootDir);\n\n config.out ??= {};\n\n config.out.dir = build_cli.outDir ?? config.out.dir;\n config.out.js = build_cli.outJs ?? config.out.js;\n config.out.sef = build_cli.outSef ?? config.out.sef;\n\n const resolvedConfig = resolveExtensionConfig(config, rootDir);\n\n const resolved: ResolvedBuildOptions = {\n rootDir,\n mode,\n config: resolvedConfig\n }\n return resolved;\n}\n","import cac from \"cac\";\nimport { buildExtension, BuildMode } from \"./build\";\nimport colors from 'picocolors';\nimport { resolveBuildConfig } from \"./config\";\nimport { VERSION } from \"./constants\";\n\nconst cli = cac('sammi-next');\n\nexport interface GlobalCLIOptions {\n m?: string\n mode?: string\n}\n\nconst filterDuplicateOptions = <T extends object>(options: T) => {\n for (const [key, value] of Object.entries(options)) {\n if (!Array.isArray(value)) continue;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n options[key as keyof T] = value[value.length - 1];\n }\n};\n\nfunction cleanGlobalCLIOptions<Options extends GlobalCLIOptions>(options: Options): Omit<Options, keyof GlobalCLIOptions> {\n const ret = { ...options };\n delete ret.m\n delete ret.mode\n\n return ret;\n}\n\ncli.option('-m, --mode <mode>', '[string] set build mode');\n\nexport interface BuildCLIOptions {\n outDir?: string\n outJs?: string\n outSef?: string\n}\n\ncli\n .command('[root]', 'build extension')\n .option('--outDir <dir>', '[string] output directory (default: \"dist\")')\n .option('--outJs <name>', '[string] output file name for the JS (default: extension.js)')\n .option('--outSef <name>', '[string] output file name for the SEF (default: extension.sef)')\n .action(\n async (\n root: string | undefined,\n options: GlobalCLIOptions,\n ) => {\n filterDuplicateOptions(options);\n\n const buildOptions: BuildCLIOptions = cleanGlobalCLIOptions(options);\n\n try {\n const buildConfig = await resolveBuildConfig(\n root,\n options.mode || BuildMode[BuildMode.PRODUCTION],\n buildOptions,\n );\n\n await buildExtension(buildConfig);\n } catch (e) {\n const error = e as Error;\n console.error(colors.red(`error during build:\\n${error.stack}`));\n process.exit(1);\n }\n }\n )\n\ncli\n .command('dev [root]', 'build extension in dev mode')\n .option('--outDir <dir>', '[string] output directory (default: \"dist\")')\n .option('--outJs <name>', '[string] output file name for the JS (default: extension.js)')\n .option('--outSef <name>', '[string] output file name for the SEF (default: extension.sef)')\n .action(\n async (\n root: string | undefined,\n options: GlobalCLIOptions,\n ) => {\n filterDuplicateOptions(options);\n\n const buildOptions: BuildCLIOptions = cleanGlobalCLIOptions(options);\n\n try {\n const buildConfig = await resolveBuildConfig(\n root,\n options.mode || BuildMode[BuildMode.DEV],\n buildOptions,\n );\n\n await buildExtension(buildConfig);\n } catch (e) {\n const error = e as Error;\n console.error(colors.red(`error during build:\\n${error.stack}`));\n process.exit(1);\n }\n }\n )\n\ncli.help();\ncli.version(VERSION);\n\ncli.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["path","VERSION","res","VERSION"],"sources":["../../package.json","../../src/node/constants.ts","../../src/node/utils.ts","../../src/node/build.ts","../../src/node/config.ts","../../src/node/cli.ts"],"sourcesContent":["","import path from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport colors from 'picocolors'\r\n\r\nexport { version as VERSION } from '../../package.json';\r\n\r\nexport const DEFAULT_CONFIG_EXTENSIONS: string[] = [\r\n '.mjs',\r\n '.js',\r\n '.mts',\r\n '.ts',\r\n]\r\n\r\nexport const GLOBAL_NAME = \"SAMMIExtensions\";\r\n\r\nexport const BUILD_PREFIX = colors.blue(\"[sammi-next]\");\r\nexport const GREEN_CHECK = colors.green('✔');\r\nexport const RED_X = colors.red('✗');\r\n\r\nfunction findPackageDir() {\r\n let initPath = fileURLToPath(import.meta.url);\r\n while (!initPath.endsWith('sammi-next')) {\r\n initPath = path.resolve(initPath, '..');\r\n }\r\n return initPath;\r\n}\r\nexport const SAMMI_NEXT_PACKAGE_DIR = findPackageDir();\r\n","export function displayTime(time: number): string {\r\n if (time < 1_000)\r\n return `${time}ms`;\r\n\r\n time = time / 1_000;\r\n\r\n if (time < 60)\r\n return `${time.toFixed(2)}s`\r\n\r\n const mins = Math.floor(time / 60);\r\n const seconds = Math.round(time % 60);\r\n\r\n if (seconds === 60)\r\n return `${mins + 1}m`\r\n\r\n return `${mins}m${seconds < 1 ? '' : ` ${seconds}s`}`;\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport colors from 'picocolors';\r\nimport chokidar from 'chokidar';\r\nimport { ResolvedExtensionConfig } from \"@shared/config-types\";\r\nimport { build, TsdownBundle } from \"tsdown\";\r\nimport { BUILD_PREFIX, GLOBAL_NAME, GREEN_CHECK, RED_X, SAMMI_NEXT_PACKAGE_DIR, VERSION } from \"./constants\";\r\nimport { displayTime } from './utils';\r\n// import nodePolyfills from '@rolldown/plugin-node-polyfills';\r\n\r\nexport enum BuildMode {\r\n DEV,\r\n PRODUCTION,\r\n}\r\n\r\nexport const BuildModes = Object.keys(BuildMode).filter(key => isNaN(Number(key)));\r\n\r\nexport interface BuildOptions {\r\n config: ResolvedExtensionConfig;\r\n rootDir: string;\r\n mode?: BuildMode;\r\n}\r\n\r\nexport type ResolvedBuildOptions = Required<BuildOptions>;\r\n\r\nfunction readOptionalFile(path: string): string | undefined {\r\n if (!fs.existsSync(path)) return;\r\n\r\n return fs.readFileSync(path, 'utf-8');\r\n}\r\n\r\nconst CommandRE = /\\w+\\(\\w+,\\s*{\\s*default:/gm;\r\n\r\nfunction generateSEF(options: ResolvedBuildOptions): string {\r\n const { config, rootDir, mode } = options;\r\n const content = [];\r\n\r\n const required_files: {\r\n header: string,\r\n content: string,\r\n }[] = [\r\n {\r\n header: \"extension_name\",\r\n content: config.name,\r\n },\r\n {\r\n header: \"extension_info\",\r\n content: config.info,\r\n },\r\n {\r\n header: \"extension_version\",\r\n content: config.version,\r\n },\r\n ]\r\n\r\n for (const field of required_files) {\r\n content.push(`[${field.header}]`, field.content, \"\");\r\n }\r\n\r\n const external = readOptionalFile(config.external);\r\n content.push(\"[insert_external]\", external ? `<div id=\"${config.id}-external\">${external}</div>` : \"\", \"\");\r\n\r\n const js_script = fs.readFileSync(path.join(rootDir, config.out.dir, config.out.js), \"utf-8\");\r\n content.push(\"[insert_command]\", CommandRE.test(js_script) ? `${GLOBAL_NAME}.${config.id}.default()` : \"\", \"\");\r\n content.push(\"[insert_hook]\", \"\", \"\") // TODO: maybe add hook retro-compatibility\r\n content.push(\"[insert_script]\", js_script, \"\");\r\n\r\n let over = readOptionalFile(config.over);\r\n if (over && mode === BuildMode.PRODUCTION) {\r\n over = JSON.stringify(JSON.parse(over));\r\n }\r\n content.push(\"[insert_over]\", over && over != \"{}\" ? over : \"\", \"\");\r\n return content.join(\"\\n\");\r\n}\r\n\r\nfunction generatePreview(options: ResolvedBuildOptions): string {\r\n const { config, rootDir } = options;\r\n\r\n const external = readOptionalFile(config.external);\r\n const js_script = fs.readFileSync(path.join(rootDir, config.out.dir, config.out.js), \"utf-8\");\r\n return fs\r\n .readFileSync(path.join(SAMMI_NEXT_PACKAGE_DIR, \".sammi\", \"preview.blueprint.html\"), \"utf-8\")\r\n .replace(/{{EXTERNAL}}/g, external ? `<div id=\"${config.id}-external\">${external}</div>` : \"\")\r\n .replace(/{{SCRIPT}}/g, js_script);\r\n}\r\n\r\nasync function buildOnce(options: ResolvedBuildOptions) {\r\n const { config, rootDir, mode } = options;\r\n\r\n const startTime = Date.now();\r\n const bundle = await build({\r\n entry: [config.entry],\r\n outDir: path.join(rootDir, config.out.dir),\r\n platform: 'browser',\r\n format: 'iife',\r\n target: ['es2022'],\r\n sourcemap: false,\r\n minify: mode === BuildMode.PRODUCTION,\r\n banner: {\r\n js: `/* ${config.name} v${config.version} - Built with SAMMI Next v${VERSION} */`,\r\n },\r\n noExternal: ['**'],\r\n outputOptions: {\r\n entryFileNames: config.out.js,\r\n extend: true,\r\n name: `${GLOBAL_NAME}.${config.id}`,\r\n exports: 'named',\r\n },\r\n // plugins: [\r\n // nodePolyfills(),\r\n // ],\r\n });\r\n const tsdownTime = Date.now();\r\n console.info(GREEN_CHECK, BUILD_PREFIX, `built ${config.out.js} in ${displayTime(tsdownTime - startTime)}`);\r\n\r\n fs.writeFileSync(path.join(rootDir, config.out.dir, config.out.sef), generateSEF(options), 'utf-8');\r\n const sefTime = Date.now();\r\n console.info(GREEN_CHECK, BUILD_PREFIX, `built ${config.out.sef} in ${displayTime(sefTime - tsdownTime)}`);\r\n\r\n fs.writeFileSync(path.join(rootDir, config.out.dir, \"preview.html\"), generatePreview(options), 'utf-8');\r\n const previewTime = Date.now();\r\n console.info(GREEN_CHECK, BUILD_PREFIX, `built preview.html in ${displayTime(previewTime - sefTime)}`);\r\n return { bundle, startTime };\r\n}\r\n\r\nexport async function buildExtension(options: ResolvedBuildOptions) {\r\n const { config, mode } = options;\r\n\r\n console.info(\r\n colors.cyan(\r\n `SAMMI Next v${VERSION} ${colors.green(\r\n `building \"${config.name}\" extension in ${BuildMode[mode].toLowerCase()} mode...`\r\n )}`\r\n ),\r\n );\r\n\r\n let bundle: TsdownBundle[] | undefined;\r\n let startTime: number | undefined;\r\n\r\n try {\r\n const res = await buildOnce(options);\r\n bundle = res.bundle;\r\n startTime = res.startTime;\r\n\r\n if (options.mode !== BuildMode.DEV) return bundle;\r\n\r\n console.info(BUILD_PREFIX, colors.cyan(\"watching for file changes...\"));\r\n\r\n const watchPaths = [\r\n path.dirname(config.entry),\r\n config.external,\r\n config.over,\r\n ].filter(Boolean);\r\n\r\n const watcher = chokidar.watch(watchPaths, { ignoreInitial: true });\r\n let timer: NodeJS.Timeout | null = null;\r\n\r\n watcher.on('all', (event, p) => {\r\n console.info(colors.cyan(`${event}: ${p}`));\r\n if (timer)\r\n clearTimeout(timer);\r\n\r\n timer = setTimeout(() => {\r\n buildOnce(options).then(res => {\r\n bundle = res.bundle;\r\n startTime = res.startTime;\r\n }).catch(e => console.error(e));\r\n }, 100);\r\n });\r\n\r\n process.on('SIGINT', () => {\r\n console.info(\"\\nStopping watch mode...\");\r\n watcher\r\n .close()\r\n .then(() => process.exit(0))\r\n .catch(e => {\r\n console.error(e);\r\n process.exit(1);\r\n });\r\n });\r\n return watcher;\r\n } catch (error) {\r\n if (startTime) {\r\n console.error(RED_X, BUILD_PREFIX, `Build failed in ${displayTime(Date.now() - startTime)}`);\r\n startTime = undefined;\r\n }\r\n throw error;\r\n }\r\n}\r\n","\r\n\r\nimport { ExtensionConfig, ResolvedExtensionConfig } from \"@shared/config-types\";\r\nimport Ajv, { JSONSchemaType } from \"ajv\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { DEFAULT_CONFIG_EXTENSIONS } from \"./constants\";\r\nimport { BuildCLIOptions } from \"./cli\";\r\nimport { BuildModes, ResolvedBuildOptions } from \"./build\";\r\n\r\nconst ajv = new Ajv();\r\n\r\najv.addKeyword({\r\n keyword: \"fileExists\",\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n validate: (_schema: any, data: any) => {\r\n if (!data) return true;\r\n\r\n return fs.existsSync(data as string);\r\n },\r\n errors: false\r\n});\r\n\r\nconst schema: JSONSchemaType<ExtensionConfig> = {\r\n type: \"object\",\r\n properties: {\r\n id: {\r\n type: \"string\",\r\n minLength: 1,\r\n pattern: \"^[a-zA-Z0-9-_]+$\",\r\n },\r\n name: {\r\n type: \"string\",\r\n minLength: 1,\r\n pattern: \"^[a-zA-Z0-9 -_]+$\",\r\n },\r\n info: {\r\n type: \"string\",\r\n default: \"\",\r\n nullable: true,\r\n },\r\n version: {\r\n type: \"string\",\r\n minLength: 1,\r\n pattern: \"^\\\\d+(?:\\\\.\\\\d+)*(?:-.*)?$\",\r\n },\r\n entry: {\r\n type: \"string\",\r\n minLength: 1,\r\n fileExists: true,\r\n },\r\n external: {\r\n type: \"string\",\r\n minLength: 1,\r\n fileExists: true,\r\n nullable: true,\r\n },\r\n over: {\r\n type: \"string\",\r\n minLength: 1,\r\n fileExists: true,\r\n nullable: true,\r\n },\r\n out: {\r\n type: \"object\",\r\n properties: {\r\n dir: {\r\n type: \"string\",\r\n minLength: 1,\r\n pattern: \"^[^<>:\\\"|?*]+$\",\r\n default: \"dist\",\r\n nullable: true,\r\n },\r\n js: {\r\n type: \"string\",\r\n minLength: 4,\r\n pattern: \"^[\\\\w\\\\-. ]+\\\\.js$\",\r\n default: \"extension.js\",\r\n nullable: true,\r\n },\r\n sef: {\r\n type: \"string\",\r\n minLength: 5,\r\n pattern: \"^[\\\\w\\\\-. ]+\\\\.sef$\",\r\n default: \"extension.sef\",\r\n nullable: true,\r\n }\r\n },\r\n required: [],\r\n nullable: true,\r\n additionalProperties: false,\r\n }\r\n },\r\n required: [\"name\", \"id\", \"version\", \"entry\"],\r\n additionalProperties: false,\r\n};\r\n\r\nconst configValidator = ajv.compile(schema);\r\n\r\nexport async function loadConfig(rootDir: string) {\r\n for (const ext of DEFAULT_CONFIG_EXTENSIONS) {\r\n const configPath = path.join(rootDir, `sammi.config${ext}`);\r\n\r\n if (!fs.existsSync(configPath)) continue;\r\n\r\n try {\r\n const { createJiti } = await import('jiti');\r\n const jiti = createJiti(rootDir, {\r\n interopDefault: true,\r\n moduleCache: true,\r\n });\r\n\r\n const config = await jiti.import(configPath, { default: true });\r\n\r\n return validateExtensionConfig(config, configPath);\r\n } catch (error) {\r\n throw new Error(`Error loading ${configPath}: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n }\r\n\r\n const jsonPath = path.join(rootDir, 'sammi.config.json');\r\n if (fs.existsSync(jsonPath)) {\r\n try {\r\n const raw = fs.readFileSync(jsonPath, 'utf-8');\r\n const config = JSON.parse(raw) as unknown;\r\n return validateExtensionConfig(config, jsonPath);\r\n } catch (error) {\r\n throw new Error(`Error loading ${jsonPath}: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n }\r\n\r\n throw new Error('SAMMI Next extension config file not found in the root dir.');\r\n}\r\n\r\n\r\nfunction validateExtensionConfig(config: unknown, configPath: string): ExtensionConfig {\r\n if (!configValidator(config)) {\r\n const errors = configValidator.errors?.map(err => ` - ${err.instancePath} ${err.message}`).join('\\n');\r\n throw new Error(`Invalid config from ${configPath}:\\n${errors}`);\r\n }\r\n\r\n return config;\r\n}\r\n\r\nexport function resolveExtensionConfig(config: ExtensionConfig, rootDir: string): ResolvedExtensionConfig {\r\n const resolved: ResolvedExtensionConfig = {\r\n id: config.id,\r\n name: config.name,\r\n version: config.version,\r\n info: config.info || '',\r\n entry: path.resolve(rootDir, config.entry),\r\n external: config.external ? path.resolve(rootDir, config.external) : '',\r\n over: config.over ? path.resolve(rootDir, config.over) : '',\r\n out: {\r\n dir: config.out?.dir || 'dist',\r\n js: config.out?.js || 'extension.js',\r\n sef: config.out?.sef || 'extension.sef',\r\n },\r\n };\r\n\r\n if (!fs.existsSync(resolved.entry))\r\n throw new Error(`Entry file not found: ${resolved.entry}`);\r\n\r\n if (resolved.external && !fs.existsSync(resolved.external))\r\n throw new Error(`External file not found: ${resolved.external}`);\r\n\r\n if (resolved.over && !fs.existsSync(resolved.over))\r\n throw new Error(`Over file not found: ${resolved.over}`);\r\n\r\n return resolved;\r\n}\r\n\r\nexport async function resolveBuildConfig(\r\n root: string | undefined,\r\n command: string,\r\n build_cli: BuildCLIOptions,\r\n) {\r\n const mode = BuildModes.findIndex(m => {\r\n return m.toLowerCase() === command.toLowerCase();\r\n });\r\n if (mode < 0)\r\n throw new Error(`Invalid mode: ${command}. It must be one of: ${BuildModes.join(', ')}`);\r\n\r\n const rootDir = root ?? process.cwd();\r\n const config = await loadConfig(rootDir);\r\n\r\n config.out ??= {};\r\n\r\n config.out.dir = build_cli.outDir ?? config.out.dir;\r\n config.out.js = build_cli.outJs ?? config.out.js;\r\n config.out.sef = build_cli.outSef ?? config.out.sef;\r\n\r\n const resolvedConfig = resolveExtensionConfig(config, rootDir);\r\n\r\n const resolved: ResolvedBuildOptions = {\r\n rootDir,\r\n mode,\r\n config: resolvedConfig\r\n }\r\n return resolved;\r\n}\r\n","import cac from \"cac\";\r\nimport { buildExtension, BuildMode } from \"./build\";\r\nimport colors from 'picocolors';\r\nimport { resolveBuildConfig } from \"./config\";\r\nimport { VERSION } from \"./constants\";\r\n\r\nconst cli = cac('sammi-next');\r\n\r\nexport interface GlobalCLIOptions {\r\n m?: string\r\n mode?: string\r\n}\r\n\r\nconst filterDuplicateOptions = <T extends object>(options: T) => {\r\n for (const [key, value] of Object.entries(options)) {\r\n if (!Array.isArray(value)) continue;\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\r\n options[key as keyof T] = value[value.length - 1];\r\n }\r\n};\r\n\r\nfunction cleanGlobalCLIOptions<Options extends GlobalCLIOptions>(options: Options): Omit<Options, keyof GlobalCLIOptions> {\r\n const ret = { ...options };\r\n delete ret.m\r\n delete ret.mode\r\n\r\n return ret;\r\n}\r\n\r\ncli.option('-m, --mode <mode>', '[string] set build mode');\r\n\r\nexport interface BuildCLIOptions {\r\n outDir?: string\r\n outJs?: string\r\n outSef?: string\r\n}\r\n\r\ncli\r\n .command('[root]', 'build extension')\r\n .option('--outDir <dir>', '[string] output directory (default: \"dist\")')\r\n .option('--outJs <name>', '[string] output file name for the JS (default: extension.js)')\r\n .option('--outSef <name>', '[string] output file name for the SEF (default: extension.sef)')\r\n .action(\r\n async (\r\n root: string | undefined,\r\n options: GlobalCLIOptions,\r\n ) => {\r\n filterDuplicateOptions(options);\r\n\r\n const buildOptions: BuildCLIOptions = cleanGlobalCLIOptions(options);\r\n\r\n try {\r\n const buildConfig = await resolveBuildConfig(\r\n root,\r\n options.mode || BuildMode[BuildMode.PRODUCTION],\r\n buildOptions,\r\n );\r\n\r\n await buildExtension(buildConfig);\r\n } catch (e) {\r\n const error = e as Error;\r\n console.error(colors.red(`error during build:\\n${error.stack}`));\r\n process.exit(1);\r\n }\r\n }\r\n )\r\n\r\ncli\r\n .command('dev [root]', 'build extension in dev mode')\r\n .option('--outDir <dir>', '[string] output directory (default: \"dist\")')\r\n .option('--outJs <name>', '[string] output file name for the JS (default: extension.js)')\r\n .option('--outSef <name>', '[string] output file name for the SEF (default: extension.sef)')\r\n .action(\r\n async (\r\n root: string | undefined,\r\n options: GlobalCLIOptions,\r\n ) => {\r\n filterDuplicateOptions(options);\r\n\r\n const buildOptions: BuildCLIOptions = cleanGlobalCLIOptions(options);\r\n\r\n try {\r\n const buildConfig = await resolveBuildConfig(\r\n root,\r\n options.mode || BuildMode[BuildMode.DEV],\r\n buildOptions,\r\n );\r\n\r\n await buildExtension(buildConfig);\r\n } catch (e) {\r\n const error = e as Error;\r\n console.error(colors.red(`error during build:\\n${error.stack}`));\r\n process.exit(1);\r\n }\r\n }\r\n )\r\n\r\ncli.help();\r\ncli.version(VERSION);\r\n\r\ncli.parse();\r\n"],"mappings":"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"}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,8 +1,349 @@
|
|
|
1
|
-
/// <reference types="sammi-bridge-types" />
|
|
2
1
|
import { ExtensionConfig as ExtensionConfig$1 } from "../shared/config-types.js";
|
|
2
|
+
import EventEmitter from "events";
|
|
3
3
|
|
|
4
|
+
//#region ../sammi-websocket-types/dist/Socket.d.ts
|
|
5
|
+
declare class Socket extends EventEmitter {
|
|
6
|
+
_connected: boolean;
|
|
7
|
+
_socket: any;
|
|
8
|
+
connect(args: any): Promise<any>;
|
|
9
|
+
/**
|
|
10
|
+
* Opens a WebSocket connection to an sammi-websocket server, but does not attempt any authentication.
|
|
11
|
+
*
|
|
12
|
+
* @param {String} address url without ws:// or wss:// prefix.
|
|
13
|
+
* @param {Boolean} secure whether to us ws:// or wss://
|
|
14
|
+
* @returns {Promise}
|
|
15
|
+
* @private
|
|
16
|
+
* @return {Promise} on attempted creation of WebSocket connection.
|
|
17
|
+
*/
|
|
18
|
+
private _connect;
|
|
19
|
+
/**
|
|
20
|
+
* Authenticates to an sammi-websocket server. Must already have an active connection before calling this method.
|
|
21
|
+
*
|
|
22
|
+
* @param {String} [name=''] name of the client.
|
|
23
|
+
* @param {String} [password=''] authentication string.
|
|
24
|
+
* @private
|
|
25
|
+
* @return {Promise} on resolution of authentication call.
|
|
26
|
+
*/
|
|
27
|
+
private _authenticate;
|
|
28
|
+
/**
|
|
29
|
+
* Close and disconnect the WebSocket connection.
|
|
30
|
+
*
|
|
31
|
+
* @function
|
|
32
|
+
* @category request
|
|
33
|
+
*/
|
|
34
|
+
disconnect(): void;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region ../sammi-websocket-types/dist/SAMMIWebSocket.d.ts
|
|
38
|
+
declare class SAMMIWebSocket extends Socket {
|
|
39
|
+
/**
|
|
40
|
+
* Generic Socket request method. Returns a promise.
|
|
41
|
+
* Generates a messageId internally and will override any passed in the args.
|
|
42
|
+
* Note that the requestType here is pre-marshaling and currently must match exactly what the websocket plugin is expecting.
|
|
43
|
+
*
|
|
44
|
+
* @param {String} requestType sammi-websocket plugin expected request type.
|
|
45
|
+
* @param {Object} [arg={}] request arguments.
|
|
46
|
+
* @return {Promise} Promise, passes the plugin response object.
|
|
47
|
+
*/
|
|
48
|
+
send(requestType: string, args?: {}): Promise<any>;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region ../sammi-bridge-types/src/SAMMICommands.d.ts
|
|
52
|
+
/** SAMMI Core Helper Functions
|
|
53
|
+
* You can call them with SAMMI.{helperfunction}
|
|
54
|
+
* Use promises if you want to get a reply back from SAMMI
|
|
55
|
+
* No promise example: SAMMI.setVariable(myVariable, 'some value', 'someButtonID')
|
|
56
|
+
* Promise example: SAMMI.getVariable(myVariable, 'someButtonID').then(reply=>console.log(reply))
|
|
57
|
+
*/
|
|
58
|
+
declare class SAMMICommands$1 {
|
|
59
|
+
/**
|
|
60
|
+
* Get a variable from SAMMI
|
|
61
|
+
* @param {string} name - name of the variable
|
|
62
|
+
* @param {string} buttonId - button ID for local variable, default = global variable
|
|
63
|
+
*/
|
|
64
|
+
getVariable(name?: string, buttonId?: string): Promise<any>;
|
|
65
|
+
/**
|
|
66
|
+
* Set a variable in SAMMI
|
|
67
|
+
* @param {string} name - name of the variable
|
|
68
|
+
* @param {(string|number|object|array|null)} value - new value of the variable
|
|
69
|
+
* @param {string} buttonId - button ID for local variable, default = global variable
|
|
70
|
+
*/
|
|
71
|
+
setVariable(name?: string, value?: (string | number | object | any[] | null), buttonId?: string, instanceId?: number): Promise<any>;
|
|
72
|
+
/**
|
|
73
|
+
* Send a popup message to SAMMI
|
|
74
|
+
* @param {string} msg - message to send
|
|
75
|
+
*/
|
|
76
|
+
popUp(msg?: string): Promise<any>;
|
|
77
|
+
/**
|
|
78
|
+
* Send a yellow notification message to SAMMI
|
|
79
|
+
* @param {string} msg - message to send
|
|
80
|
+
*/
|
|
81
|
+
alert(msg?: string): Promise<any>;
|
|
82
|
+
/**
|
|
83
|
+
* send extension command to SAMMI
|
|
84
|
+
* @param {string} name - name of the extension command
|
|
85
|
+
* @param {string} color - box color, accepts hex/dec colors (include # for hex), default 3355443
|
|
86
|
+
* @param {string} height - height of the box in pixels, 52 for regular or 80 for resizable box, default 52
|
|
87
|
+
* @param {Object} boxes
|
|
88
|
+
* - one object per box, key = boxVariable, value = array of box params
|
|
89
|
+
* - boxVariable = variable to save the box value under
|
|
90
|
+
* - boxName = name of the box shown in the user interface
|
|
91
|
+
* - boxType = type of the box, 0 = resizable, 2 = checkbox (true/false), 14 = regular box, 15 = variable box, 18 = select box, see extension guide for more
|
|
92
|
+
* - defaultValue = default value of the variable
|
|
93
|
+
* - (optional) sizeModifier = horizontal box size, 1 is normal
|
|
94
|
+
* - (optional) [] selectOptions = array of options for the user to select (when using Select box type)
|
|
95
|
+
* @param {[boxName: string, boxType: number, defaultValue: (string | number), sizeModifier: (number|undefined), selectOptions: Array|undefined]} boxes.boxVariable
|
|
96
|
+
* */
|
|
97
|
+
extCommand(name?: string, color?: string, height?: string, boxes?: {
|
|
98
|
+
boxVariable: [boxName: string, boxType: number, defaultValue: (string | number), sizeModifier: (number | undefined), selectOptions: any[] | undefined];
|
|
99
|
+
}, triggerButton?: boolean, hidden?: boolean): Promise<any>;
|
|
100
|
+
/**
|
|
101
|
+
* Close SAMMI Bridge connection to SAMMI Core.
|
|
102
|
+
*/
|
|
103
|
+
close(): Promise<any>;
|
|
104
|
+
/**
|
|
105
|
+
* Get deck and button updates
|
|
106
|
+
* @param {boolean} enabled - enable or disable updates
|
|
107
|
+
*/
|
|
108
|
+
stayInformed(enabled: boolean): Promise<any>;
|
|
109
|
+
/**
|
|
110
|
+
* Request an array of all decks
|
|
111
|
+
* - Replies with an array ["Deck1 Name","Unique ID",crc32,"Deck2 Name","Unique ID",crc32,...]
|
|
112
|
+
* - Use crc32 value to verify deck you saved localy is the same
|
|
113
|
+
*/
|
|
114
|
+
getDeckList(): Promise<any>;
|
|
115
|
+
/**
|
|
116
|
+
* Request a deck params
|
|
117
|
+
* @param {string} id - Unique deck ID retrieved from getDeckList
|
|
118
|
+
* - Replies with an object containing a full deck
|
|
119
|
+
*/
|
|
120
|
+
getDeck(id?: string): Promise<any>;
|
|
121
|
+
/**
|
|
122
|
+
* Get deck status
|
|
123
|
+
* - Replies with either 0 (deck is disabled) or 1 (deck is enabled)
|
|
124
|
+
* @param {string} id - Unique deck ID retrieved from getDeckList
|
|
125
|
+
*/
|
|
126
|
+
getDeckStatus(deckID?: number): Promise<any>;
|
|
127
|
+
/**
|
|
128
|
+
* Change deck status
|
|
129
|
+
* @param {string} id - Unique deck ID retrieved from getDeckList
|
|
130
|
+
* @param {int} status - New deck status, 0 = disable, 1 = enable, 2 = toggle
|
|
131
|
+
*/
|
|
132
|
+
changeDeckStatus(deckID?: number, status?: int): Promise<any>;
|
|
133
|
+
/**
|
|
134
|
+
* Retrieve an image in base64
|
|
135
|
+
* @param {string} fileName - image file name without the path (image.png)
|
|
136
|
+
* - Replies with an object containing the Base64 string of the image
|
|
137
|
+
*/
|
|
138
|
+
getImage(fileName?: string): Promise<any>;
|
|
139
|
+
/**
|
|
140
|
+
* Retrieves CRC32 of a file
|
|
141
|
+
* @param {string} fileName - file name without the path (image.png)
|
|
142
|
+
*/
|
|
143
|
+
getSum(fileName?: string): Promise<any>;
|
|
144
|
+
/**
|
|
145
|
+
* Retrieves all currently active buttons
|
|
146
|
+
* - Replies with an array of button param objects
|
|
147
|
+
*/
|
|
148
|
+
getActiveButtons(): Promise<any>;
|
|
149
|
+
/**
|
|
150
|
+
* Retrieves params of all linked Twitch accounts
|
|
151
|
+
*/
|
|
152
|
+
getTwitchList(): Promise<any>;
|
|
153
|
+
/**
|
|
154
|
+
* Sends a trigger
|
|
155
|
+
* @param {number} type - type of trigger
|
|
156
|
+
* - trigger types: 0 Twitch chat, 1 Twitch Sub, 2 Twitch Gift, 3 Twitch redeem
|
|
157
|
+
* 4 Twitch Raid, 5 Twitch Bits, 6 Twitch Follower, 7 Hotkey
|
|
158
|
+
* 8 Timer, 9 OBS Trigger, 10 SAMMI Bridge, 11 twitch moderation, 12 extension trigger
|
|
159
|
+
* @param {object} data - whatever data is required for the trigger, see manual
|
|
160
|
+
*/
|
|
161
|
+
trigger(type?: number, data?: object): Promise<any>;
|
|
162
|
+
/**
|
|
163
|
+
* Sends a test trigger that will automatically include channel ID for from_channel_id pull value
|
|
164
|
+
* @param {number} type - type of trigger
|
|
165
|
+
* - trigger types: 0 Twitch chat, 1 Twitch Sub, 2 Twitch Gift, 3 Twitch redeem
|
|
166
|
+
* 4 Twitch Raid, 5 Twitch Bits, 6 Twitch Follower, 7 Hotkey
|
|
167
|
+
* 8 Timer, 9 OBS Trigger, 10 SAMMI Bridge, 11 twitch moderation, 12 extension trigger
|
|
168
|
+
* @param {object} data - whatever data is required for the trigger, see manual
|
|
169
|
+
*/
|
|
170
|
+
testTrigger(type: number, data: object): Promise<any>;
|
|
171
|
+
/**
|
|
172
|
+
* Triggers a button
|
|
173
|
+
* @param {string} id - button ID to trigger
|
|
174
|
+
*/
|
|
175
|
+
triggerButton(id?: string): Promise<any>;
|
|
176
|
+
/**
|
|
177
|
+
* Releases a button
|
|
178
|
+
* @param {string} id - button ID to release
|
|
179
|
+
*/
|
|
180
|
+
releaseButton(id?: string): Promise<any>;
|
|
181
|
+
/**
|
|
182
|
+
* Modifies a button
|
|
183
|
+
* @param {string} id - button ID to modify
|
|
184
|
+
* @param {number|undefined} color - decimal button color (BGR)
|
|
185
|
+
* @param {string|undefined} text - button text
|
|
186
|
+
* @param {string|undefined} image - button image file name
|
|
187
|
+
* @param {number|undefined} border - border size, 0-7
|
|
188
|
+
* - leave parameters empty to reset button back to default values
|
|
189
|
+
*/
|
|
190
|
+
modifyButton(id: string, color: number | undefined, text: string | undefined, image: string | undefined, border: number | undefined): Promise<any>;
|
|
191
|
+
/**
|
|
192
|
+
* Opens edit command screen in SAMMI for the selected button
|
|
193
|
+
* @param {string} deckId - deckId ID to edit
|
|
194
|
+
* @param {string} buttonId - button ID to edit
|
|
195
|
+
*/
|
|
196
|
+
editButton(deckId?: string, buttonId?: string): Promise<any>;
|
|
197
|
+
/**
|
|
198
|
+
* Retrieves all currently modified buttons
|
|
199
|
+
* - object of button objects that are currently modified
|
|
200
|
+
*/
|
|
201
|
+
getModifiedButtons(): Promise<any>;
|
|
202
|
+
/**
|
|
203
|
+
* Sends an extension trigger
|
|
204
|
+
* @param {string} trigger - name of the trigger
|
|
205
|
+
* @param {object} dats - object containing all trigger pull data
|
|
206
|
+
*/
|
|
207
|
+
triggerExt(trigger?: string, data?: {}): Promise<any>;
|
|
208
|
+
/**
|
|
209
|
+
* Deletes a variable
|
|
210
|
+
* @param {string} name - name of the variable
|
|
211
|
+
* @param {string} buttonId - button ID for local variable, default = global variable
|
|
212
|
+
*/
|
|
213
|
+
deleteVariable(name?: string, buttonId?: string): Promise<any>;
|
|
214
|
+
/**
|
|
215
|
+
* Inserts an array value
|
|
216
|
+
* @param {string} arrayName - name of the array
|
|
217
|
+
* @param {number} index - index to insert the new item at
|
|
218
|
+
* @param {string|number|object|array} value - item value
|
|
219
|
+
* @param {string} buttonId - button id, default is global
|
|
220
|
+
*/
|
|
221
|
+
insertArray(arrayName?: string, index?: number, value?: string | number | object | any[], buttonId?: string): Promise<any>;
|
|
222
|
+
/**
|
|
223
|
+
* Deletes an array value at specified index
|
|
224
|
+
* @param {string} arrayName - name of the array
|
|
225
|
+
* @param {number} index - index of the item to delete
|
|
226
|
+
* @param {string} buttonId - button id, default is global
|
|
227
|
+
*/
|
|
228
|
+
deleteArray(arrayName?: string, slot?: number, buttonId?: string): Promise<any>;
|
|
229
|
+
/**
|
|
230
|
+
* Saves a number/string into an ini file
|
|
231
|
+
* @param {string} fileName - name of the ini file
|
|
232
|
+
* @param {number} section - section name
|
|
233
|
+
* @param {string} key - key name
|
|
234
|
+
* @param {string|number} value - value to save
|
|
235
|
+
* @param {string} type - type of the value, text or number
|
|
236
|
+
*/
|
|
237
|
+
saveIni(fileName?: string, section?: number, key?: string, value?: string | number, type?: string): Promise<any>;
|
|
238
|
+
/**
|
|
239
|
+
* Loads a number/string from an ini file
|
|
240
|
+
* @param {string} fileName - name of the ini file
|
|
241
|
+
* @param {number} section - section name
|
|
242
|
+
* @param {string} key - key name
|
|
243
|
+
* @param {string} type - type of the value, text or number
|
|
244
|
+
*/
|
|
245
|
+
loadIni(fileName?: string, section?: number, key?: string, type?: string): Promise<any>;
|
|
246
|
+
/**
|
|
247
|
+
* Sends an HTTP request
|
|
248
|
+
* @param {string} url - url to send the request to
|
|
249
|
+
* @param {string} method - request method, GET, POST, PUT, DELETE
|
|
250
|
+
* @param {json} headers - stringified JSON object of headers (optional)
|
|
251
|
+
* @param {json} body - stringified request body (optional)
|
|
252
|
+
*/
|
|
253
|
+
httpRequest(url?: string, method?: string, headers?: json, body?: json): Promise<any>;
|
|
254
|
+
/**
|
|
255
|
+
* Sends a notification (tray icon bubble) message to SAMMI
|
|
256
|
+
* @param {string} msg - message to show
|
|
257
|
+
*/
|
|
258
|
+
notification(msg?: string): Promise<any>;
|
|
259
|
+
/**
|
|
260
|
+
* Opens a URL in the default browser from SAMMI
|
|
261
|
+
* @param {string} url - url to open
|
|
262
|
+
*/
|
|
263
|
+
openURL(url?: string): Promise<any>;
|
|
264
|
+
generateMessage(): string;
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region ../sammi-bridge-types/src/index.d.ts
|
|
268
|
+
declare class SAMMICommands extends SAMMICommands$1 {
|
|
269
|
+
/**
|
|
270
|
+
* send extension command to SAMMI
|
|
271
|
+
* @param name - name of the extension command
|
|
272
|
+
* @param color - box color, accepts hex/dec colors (include # for hex), default 3355443
|
|
273
|
+
* @param height - height of the box in pixels, 52 for regular or 80 for resizable box, default 52
|
|
274
|
+
* @param boxes
|
|
275
|
+
* - one object per box, key = boxVariable, value = array of box params
|
|
276
|
+
* - boxVariable = variable to save the box value under
|
|
277
|
+
* - boxName = name of the box shown in the user interface
|
|
278
|
+
* - boxType = type of the box, 0 = resizable, 2 = checkbox (true/false), 14 = regular box, 15 = variable box, 18 = select box, see extension guide for more
|
|
279
|
+
* - defaultValue = default value of the variable
|
|
280
|
+
* - (optional) sizeModifier = horizontal box size, 1 is normal
|
|
281
|
+
* - (optional) [] selectOptions = array of options for the user to select (when using Select box type)
|
|
282
|
+
* @param boxes.boxVariable
|
|
283
|
+
* */
|
|
284
|
+
extCommand(name?: string, color?: string | number, height?: string | number, boxes?: {
|
|
285
|
+
[boxVariable: string]: [boxName: string, boxType: number, defaultValue: (string | number), sizeModifier: (number | undefined), selectOptions: any[] | undefined];
|
|
286
|
+
}, triggerButton?: boolean, hidden?: boolean): Promise<any>;
|
|
287
|
+
} // declare global {
|
|
288
|
+
// /**
|
|
289
|
+
// * Bridge provides native helper functions to make your life easier, without the need to use SAMMI Websocket library directly.
|
|
290
|
+
// *
|
|
291
|
+
// * You can call all helper functions with SAMMI.method(arguments).
|
|
292
|
+
// *
|
|
293
|
+
// * To use promises, you can call them with SAMMI.method(arguments).then(response=>console.log(response)).
|
|
294
|
+
// */
|
|
295
|
+
// const SAMMI: SAMMICommands
|
|
296
|
+
// /**
|
|
297
|
+
// * When a user triggers an extension command, SAMMI will send the data to Bridge (unless the `sendAsExtensionTrigger` parameter is set to `true`).
|
|
298
|
+
// *
|
|
299
|
+
// * You can listen to this data by using `sammiclient.on('extension name', (payload) => {})`.
|
|
300
|
+
// *
|
|
301
|
+
// * You can also use `sammiclient.addListener('extension name', functionToExecute)`.
|
|
302
|
+
// *
|
|
303
|
+
// * For example, let's say your extension command is called Lucky Wheel:
|
|
304
|
+
// * @example
|
|
305
|
+
// * sammiclient.on('Lucky Wheel', (payload) => {
|
|
306
|
+
// * console.log(payload)
|
|
307
|
+
// * // DO SOMETHING WITH THE EXTENSION PAYLOAD
|
|
308
|
+
// * // FromButton - button ID the extension command was triggered in
|
|
309
|
+
// * // instanceId - instance ID of a button the extension command was triggered in
|
|
310
|
+
// * const { FromButton, instanceId } = payload.Data
|
|
311
|
+
// * });
|
|
312
|
+
// */
|
|
313
|
+
// const sammiclient: SAMMIWebSocket
|
|
314
|
+
// }
|
|
315
|
+
//#endregion
|
|
4
316
|
//#region src/runtime/types.d.ts
|
|
5
317
|
declare global {
|
|
318
|
+
/**
|
|
319
|
+
* Bridge provides native helper functions to make your life easier, without the need to use SAMMI Websocket library directly.
|
|
320
|
+
*
|
|
321
|
+
* You can call all helper functions with SAMMI.method(arguments).
|
|
322
|
+
*
|
|
323
|
+
* To use promises, you can call them with SAMMI.method(arguments).then(response=>console.log(response)).
|
|
324
|
+
*/
|
|
325
|
+
const SAMMI: SAMMICommands;
|
|
326
|
+
/**
|
|
327
|
+
* When a user triggers an extension command, SAMMI will send the data to Bridge (unless the `sendAsExtensionTrigger` parameter is set to `true`).
|
|
328
|
+
*
|
|
329
|
+
* You can listen to this data by using `sammiclient.on('extension name', (payload) => {})`.
|
|
330
|
+
*
|
|
331
|
+
* You can also use `sammiclient.addListener('extension name', functionToExecute)`.
|
|
332
|
+
*
|
|
333
|
+
* For example, let's say your extension command is called Lucky Wheel:
|
|
334
|
+
* @example
|
|
335
|
+
* sammiclient.on('Lucky Wheel', (payload) => {
|
|
336
|
+
* console.log(payload)
|
|
337
|
+
* // DO SOMETHING WITH THE EXTENSION PAYLOAD
|
|
338
|
+
* // FromButton - button ID the extension command was triggered in
|
|
339
|
+
* // instanceId - instance ID of a button the extension command was triggered in
|
|
340
|
+
* const { FromButton, instanceId } = payload.Data
|
|
341
|
+
* });
|
|
342
|
+
*/
|
|
343
|
+
const sammiclient: SAMMIWebSocket;
|
|
344
|
+
/**
|
|
345
|
+
* Namespace that stores extensions built with SAMMI Next.
|
|
346
|
+
*/
|
|
6
347
|
var SAMMIExtensions: Record<string, SAMMINextExtension | undefined>;
|
|
7
348
|
}
|
|
8
349
|
/**
|
|
@@ -52,7 +393,12 @@ interface initExtensionOptions {
|
|
|
52
393
|
* @param options Whether to skip the process of proxying your extension (allowing other scripts to modify your exports).
|
|
53
394
|
* @returns Parsed and cleaned config.
|
|
54
395
|
*/
|
|
55
|
-
declare function initExtension(config: Record<string, any>, options
|
|
396
|
+
declare function initExtension(config: Record<string, any>, options: Omit<initExtensionOptions, 'skipProxying'> & {
|
|
397
|
+
skipProxying: true;
|
|
398
|
+
}): ExtensionConfig;
|
|
399
|
+
declare function initExtension(config: Record<string, any>, options?: Omit<initExtensionOptions, 'skipProxying'> & {
|
|
400
|
+
skipProxying: false;
|
|
401
|
+
}): Readonly<ExtensionConfig>;
|
|
56
402
|
/**
|
|
57
403
|
* Retrieves an [insert_external] section by its extension id.
|
|
58
404
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/runtime/types.ts","../../src/runtime/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["Socket","default","Promise","EventEmitter","_connected","_socket","connect","args","_connect","_authenticate","disconnect","SAMMIWebSocket","default","Promise","Socket","send","requestType","args","SAMMICommands","Promise","int","json","getVariable","name","buttonId","setVariable","value","instanceId","popUp","msg","alert","extCommand","color","height","boxVariable","boxName","boxType","defaultValue","sizeModifier","selectOptions","boxes","triggerButton","hidden","close","stayInformed","enabled","getDeckList","getDeck","id","getDeckStatus","deckID","changeDeckStatus","status","getImage","fileName","getSum","getActiveButtons","getTwitchList","trigger","type","data","testTrigger","releaseButton","modifyButton","text","image","border","editButton","deckId","getModifiedButtons","triggerExt","deleteVariable","insertArray","arrayName","index","deleteArray","slot","saveIni","section","key","loadIni","httpRequest","url","method","headers","body","notification","openURL","generateMessage","SAMMIWebSocket","SAMMICommands","Commands","Promise","extCommand","name","color","height","boxVariable","boxName","boxType","defaultValue","sizeModifier","selectOptions","boxes","triggerButton","hidden"],"sources":["../../../sammi-websocket-types/dist/Socket.d.ts","../../../sammi-websocket-types/dist/SAMMIWebSocket.d.ts","../../../sammi-bridge-types/src/SAMMICommands.d.ts","../../../sammi-bridge-types/src/index.d.ts","../../src/runtime/types.ts","../../src/runtime/index.ts"],"mappings":"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"}
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
/// <reference types="sammi-bridge-types" />
|
|
2
1
|
//#region src/runtime/index.ts
|
|
3
2
|
window.SAMMIExtensions ??= {};
|
|
4
3
|
const PROXY_PREFIX = "[SAMMI-NEXT-PROXY]";
|
|
5
4
|
const defaultInitExtensionOptions = { skipProxying: false };
|
|
6
|
-
|
|
7
|
-
* Removes unnecessary data from a raw extension config object and parses it into an ExtensionConfig.
|
|
8
|
-
*
|
|
9
|
-
* Exports the config through the `SAMMIExtensions` namespace with the `_config` key.
|
|
10
|
-
*
|
|
11
|
-
* Generates a Proxy of the extension object to avoid other scripts trying to overwrite it.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const extension_config = SAMMIExtensions['extension-id']._config;
|
|
15
|
-
*
|
|
16
|
-
* @param config Unparsed config object.
|
|
17
|
-
* @param options Whether to skip the process of proxying your extension (allowing other scripts to modify your exports).
|
|
18
|
-
* @returns Parsed and cleaned config.
|
|
19
|
-
*/
|
|
20
|
-
function initExtension(config, options = { skipProxying: false }) {
|
|
5
|
+
function initExtension(config, options) {
|
|
21
6
|
let response = {
|
|
22
7
|
id: config.id,
|
|
23
8
|
name: config.name,
|
|
@@ -108,10 +93,7 @@ const defaultInsertCommandSectionOptions = {
|
|
|
108
93
|
* @param options The initialization options.
|
|
109
94
|
* @returns wrapped export default callback
|
|
110
95
|
*/
|
|
111
|
-
function insertCommandSection(callback, options
|
|
112
|
-
waitForSammiclient: true,
|
|
113
|
-
waitForSAMMIExtensions: false
|
|
114
|
-
}) {
|
|
96
|
+
function insertCommandSection(callback, options) {
|
|
115
97
|
const settings = Object.assign(defaultInsertCommandSectionOptions, options);
|
|
116
98
|
const wrapper = () => {
|
|
117
99
|
if (!sammiclient && settings.waitForSammiclient) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/runtime/index.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/runtime/index.ts"],"sourcesContent":["// <reference types=\"sammi-bridge-types\" />\nimport type { ExtensionConfig, SAMMINextExtension } from \"./types\";\n\nwindow.SAMMIExtensions ??= {};\nconst PROXY_PREFIX = \"[SAMMI-NEXT-PROXY]\";\n\ninterface initExtensionOptions {\n /**\n * Whether to skip the process of proxying your extension.\n *\n * Warning: this allows other scripts to modify your exports.\n *\n * @default false\n */\n skipProxying?: boolean\n}\nconst defaultInitExtensionOptions: initExtensionOptions = {\n skipProxying: false,\n}\n/**\n * Removes unnecessary data from a raw extension config object and parses it into an ExtensionConfig.\n *\n * Exports the config through the `SAMMIExtensions` namespace with the `_config` key.\n *\n * Generates a Proxy of the extension object to avoid other scripts trying to overwrite it.\n *\n * @example\n * const extension_config = SAMMIExtensions['extension-id']._config;\n *\n * @param config Unparsed config object.\n * @param options Whether to skip the process of proxying your extension (allowing other scripts to modify your exports).\n * @returns Parsed and cleaned config.\n */\nexport function initExtension(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config: Record<string, any>,\n options: Omit<initExtensionOptions, 'skipProxying'> & {\n skipProxying: true\n },\n): ExtensionConfig\nexport function initExtension(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config: Record<string, any>,\n options?: Omit<initExtensionOptions, 'skipProxying'> & {\n skipProxying: false\n },\n): Readonly<ExtensionConfig>\nexport function initExtension(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config: Record<string, any>,\n options?: initExtensionOptions\n): Readonly<ExtensionConfig> | ExtensionConfig {\n let response: ExtensionConfig = {\n id: config.id as string,\n name: config.name as string,\n version: config.version as string,\n info: config.info as string | undefined,\n };\n\n window.SAMMIExtensions = window.SAMMIExtensions || {};\n SAMMIExtensions[response.id] = SAMMIExtensions[response.id] || {};\n if (SAMMIExtensions[response.id]?._config)\n throw new Error('The extension with the provided id was already initialized');\n\n const settings = Object.assign(defaultInitExtensionOptions, options);\n if (!settings.skipProxying)\n response = Object.freeze(response);\n\n // @ts-expect-error This is before proxying the extension, so no error.\n SAMMIExtensions[response.id]!._config ??= response;\n if (settings.skipProxying) return response;\n\n SAMMIExtensions[response.id] = new Proxy(SAMMIExtensions[response.id]!, {\n set(target, prop) {\n console.error(PROXY_PREFIX, \": Blocked setting value of:\", prop, \"on:\", target._config?.id);\n return true;\n },\n defineProperty(target, property) {\n console.error(PROXY_PREFIX, \": Blocked property redefinition of:\", property, \"on:\", target._config?.id);\n return true;\n },\n deleteProperty(target, p) {\n console.error(PROXY_PREFIX, \": Blocked deletion of:\", p, \"on:\", target._config?.id);\n return true;\n },\n setPrototypeOf(target) {\n console.error(PROXY_PREFIX, \": Blocked changing prototype of:\", target._config?.id);\n return true;\n },\n });\n return response;\n}\n\n/**\n * Retrieves an [insert_external] section by its extension id.\n *\n * ### Original documentation from https://sammi.solutions/extensions/build\n * This section appears inside the extension’s tab in Bridge, and it provides a visual interface for the extension if needed. It’s written in HTML and CSS.\n *\n * @example\n * const EXTERNAL = getExternalSection(config.id);\n * EXTERNAL.querySelector(\"button\")?.addEventListener('click', () => console.log('Hello world!'));\n *\n * @param extension_id The id of the SAMMI Next extension to retrieve its [insert_external] section.\n * @returns the div element that wraps your extension.\n */\nexport function getExternalSection(extension_id: string): HTMLDivElement {\n return document.getElementById(`${extension_id}-external`) as HTMLDivElement;\n}\n\ninterface insertCommandSectionOptions {\n /**\n * Whether to wait for `sammiclient` to exist.\n * This is enabled by default because every extension that uses `SAMMI` depends on `sammiclient`.\n * You can disable it if your [insert_command] section doesn't use neither `SAMMI` nor `sammiclient`.\n *\n * @default true\n */\n waitForSammiclient?: boolean;\n /**\n * Whether to wait for `SAMMIExtensions` to exist.\n * You must set this to `true` if you use the `SAMMIExtensions` namespace inside your [insert_command] section.\n *\n * @default false\n */\n waitForSAMMIExtensions?: boolean;\n}\nconst defaultInsertCommandSectionOptions: insertCommandSectionOptions = {\n waitForSammiclient: true,\n waitForSAMMIExtensions: false,\n}\n/**\n * Generates your [insert_command] section.\n * This should be used in the default export of your extension.\n *\n * By default, it wraps your callback to wait for `sammiclient` and `SAMMI` are initialized.\n * Its behavior can be changed in the options param.\n *\n * ### Original documentation from https://sammi.solutions/extensions/build\n * In this section, you can define Extension Commands.\n * These commands will be available to users in SAMMI Core when they install your extension.\n * You can define multiple commands in this section.\n * Refer to the SAMMI Bridge documentation for Extension Command details.\n * In this section, you can also automatically call your main extension function that should run as soon as SAMMI connects to Bridge.\n *\n * @example\n * export default insertCommandSection(() => {\n * SAMMI.extCommand(\"Extension Sample Command\", 3355443, 52).catch(e => console.error(e));\n * sammiclient.on(\"Extension Sample Command\", () => {\n * const handler = async () => {\n * await SAMMI.notification('Command Sample');\n * };\n * handler().catch((e) => console.error(e));\n * });\n * });\n * export default insertCommandSection(\n * () => console.log(\"Hello world\"),\n * { waitForSammiclient: false }\n * );\n * export default insertCommandSection(\n * () => console.log(SAMMIExtensions[\"extension-id\"]),\n * { waitForSAMMIExtensions: true }\n * );\n *\n * @param callback A callback with the logic that you will insert in your [insert_command] section.\n * @param options The initialization options.\n * @returns wrapped export default callback\n */\nexport function insertCommandSection(\n callback: () => void,\n options?: insertCommandSectionOptions,\n): () => void {\n const settings = Object.assign(defaultInsertCommandSectionOptions, options);\n\n const wrapper = () => {\n if (!sammiclient && settings.waitForSammiclient) {\n setTimeout(wrapper, 0);\n return;\n }\n\n if (!SAMMIExtensions && settings.waitForSAMMIExtensions) {\n setTimeout(wrapper, 0);\n return;\n }\n\n callback();\n };\n\n return wrapper;\n}\n\nexport type { ExtensionConfig as FullExtensionConfig } from '@shared/config-types';\nexport type { ExtensionConfig, SAMMINextExtension };\n"],"mappings":";AAGA,OAAO,oBAAoB,EAAE;AAC7B,MAAM,eAAe;AAYrB,MAAM,8BAAoD,EACtD,cAAc,OACjB;AA6BD,SAAgB,cAEZ,QACA,SAC2C;CAC3C,IAAI,WAA4B;EAC5B,IAAI,OAAO;EACX,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,MAAM,OAAO;EAChB;AAED,QAAO,kBAAkB,OAAO,mBAAmB,EAAE;AACrD,iBAAgB,SAAS,MAAM,gBAAgB,SAAS,OAAO,EAAE;AACjE,KAAI,gBAAgB,SAAS,KAAK,QAC9B,OAAM,IAAI,MAAM,6DAA6D;CAEjF,MAAM,WAAW,OAAO,OAAO,6BAA6B,QAAQ;AACpE,KAAI,CAAC,SAAS,aACV,YAAW,OAAO,OAAO,SAAS;AAGtC,iBAAgB,SAAS,IAAK,YAAY;AAC1C,KAAI,SAAS,aAAc,QAAO;AAElC,iBAAgB,SAAS,MAAM,IAAI,MAAM,gBAAgB,SAAS,KAAM;EACpE,IAAI,QAAQ,MAAM;AACd,WAAQ,MAAM,cAAc,+BAA+B,MAAM,OAAO,OAAO,SAAS,GAAG;AAC3F,UAAO;;EAEX,eAAe,QAAQ,UAAU;AAC7B,WAAQ,MAAM,cAAc,uCAAuC,UAAU,OAAO,OAAO,SAAS,GAAG;AACvG,UAAO;;EAEX,eAAe,QAAQ,GAAG;AACtB,WAAQ,MAAM,cAAc,0BAA0B,GAAG,OAAO,OAAO,SAAS,GAAG;AACnF,UAAO;;EAEX,eAAe,QAAQ;AACnB,WAAQ,MAAM,cAAc,oCAAoC,OAAO,SAAS,GAAG;AACnF,UAAO;;EAEd,CAAC;AACF,QAAO;;;;;;;;;;;;;;;AAgBX,SAAgB,mBAAmB,cAAsC;AACrE,QAAO,SAAS,eAAe,GAAG,aAAa,WAAW;;AAoB9D,MAAM,qCAAkE;CACpE,oBAAoB;CACpB,wBAAwB;CAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCD,SAAgB,qBACZ,UACA,SACU;CACV,MAAM,WAAW,OAAO,OAAO,oCAAoC,QAAQ;CAE3E,MAAM,gBAAgB;AAClB,MAAI,CAAC,eAAe,SAAS,oBAAoB;AAC7C,cAAW,SAAS,EAAE;AACtB;;AAGJ,MAAI,CAAC,mBAAmB,SAAS,wBAAwB;AACrD,cAAW,SAAS,EAAE;AACtB;;AAGJ,YAAU;;AAGd,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-types.d.ts","names":[],"sources":["../../src/shared/config-types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config-types.d.ts","names":[],"sources":["../../src/shared/config-types.ts"],"mappings":";;AAKA;;;UAAiB,eAAA;EAKb;;;;EAAA,EAAA;EAyCA;;;;EAnCA,IAAA;EAmEI;;;AAoBR;;;;EA9EI,IAAA;EA8EyB;;;;AAS7B;;;;;EA5EI,OAAA;EA6Ec;;;;EAvEd,KAAA;EAsE6C;;;;;;;EA7D7C,QAAA;EA8DyC;;;;;;;EArDzC,IAAA;;EAGA,GAAA;;;;;;IAMI,GAAA;;;;;;IAOA,EAAA;;;;;;IAOA,GAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;iBAoBQ,YAAA,CAAa,MAAA,EAAQ,eAAA,GAAkB,eAAA;;;;;;UAStC,uBAAA,SAAgC,QAAA,CAAS,IAAA,CAAK,eAAA;EAC3D,GAAA,EAAK,QAAA,CAAS,WAAA,CAAY,eAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-types.js","names":[],"sources":["../../src/shared/config-types.ts"],"sourcesContent":["\n/**\n * Represents the full extension config from sammi.config.ts.\n * Used by the CLI to build the extension.\n*/\nexport interface ExtensionConfig {\n /**\n * Specify a unique id for your extension here.\n * Please use alphanumeric characters, dashes, and underscores only.\n */\n id: string;\n\n /**\n * This section names your extension, and is visible in SAMMI Bridge and SAMMI Core.\n * Please use alphanumeric characters and spaces only.\n */\n name: string;\n\n /**\n * This section is for descriptive text about the extension, e.g. what the extension does.\n * This information is displayed to the users in SAMMI Bridge-Extensions tab when they hover\n * over the extension name inside the list of installed extensions.\n *\n * @default \"\"\n */\n info?: string;\n\n /**\n * Specify your extension version here, using numbers and dots (e.g., 2.01).\n * This is crucial for the automatic version checker in Bridge, which can notify users of updates.\n *\n * Note: the regex pattern used for validation is `^\\d+(?:\\.\\d+)*(?:-.*)?$`.\n * Although SAMMI Next allows beta/alpha suffixes due to its RegExp,\n * please note that the official SAMMI Bridge automatic version checker ignores them.\n * In other words, the users only will be notified of release updates.\n */\n version: string;\n\n /**\n * Specify your script.ts path here.\n * In your [insert_script] section, you’re encouraged to write your own TypeScript code.\n */\n entry: string;\n\n /**\n * Specify your external.html path here.\n * Your [insert_external] section appears inside the extension’s tab in Bridge,\n * and it provides a visual interface for the extension if needed. It’s written in HTML and CSS.\n *\n * @default undefined\n */\n external?: string;\n\n /**\n * Specify your over.json path here.\n * In your [insert_over] section you simply copy and paste your deck from SAMMI Core you wish to distribute with your extension.\n * When users install your extension, the deck will be automatically added to their SAMMI Core.\n *\n * @default undefined\n */\n over?: string;\n\n /** Configuration related with the extension building. */\n out?: {\n /**\n * The path where the extension will build to.\n *\n * @default \"dist\"\n */\n dir?: string;\n\n /**\n * The file name of the final JavaScript file.\n *\n * @default \"extension.js\"\n */\n js?: string;\n\n /**\n * The file name of the final SAMMI Extension File.\n *\n * @default \"extension.sef\"\n */\n sef?: string;\n }\n}\n\n/**\n * Helper function to define a SAMMI Next extension config with type safety.\n *\n * @example\n * ```ts\n * // sammi.config.ts\n * import { defineConfig } from 'sammi-next/config';\n *\n * export default defineConfig({\n * id: 'my-extension',\n * name: 'My Extension',\n * version: '1.0.0',\n * entry: 'src/script.ts',\n * });\n * ```\n */\nexport function defineConfig(config: ExtensionConfig): ExtensionConfig {\n return config;\n}\n\n/**\n * Resolved extension config with defaults applied.\n * Used internally by the builder.\n * @internal\n */\nexport interface ResolvedExtensionConfig extends Required<Omit<ExtensionConfig, 'out'>> {\n out: Required<NonNullable<ExtensionConfig['out']>>;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config-types.js","names":[],"sources":["../../src/shared/config-types.ts"],"sourcesContent":["\r\n/**\r\n * Represents the full extension config from sammi.config.ts.\r\n * Used by the CLI to build the extension.\r\n*/\r\nexport interface ExtensionConfig {\r\n /**\r\n * Specify a unique id for your extension here.\r\n * Please use alphanumeric characters, dashes, and underscores only.\r\n */\r\n id: string;\r\n\r\n /**\r\n * This section names your extension, and is visible in SAMMI Bridge and SAMMI Core.\r\n * Please use alphanumeric characters and spaces only.\r\n */\r\n name: string;\r\n\r\n /**\r\n * This section is for descriptive text about the extension, e.g. what the extension does.\r\n * This information is displayed to the users in SAMMI Bridge-Extensions tab when they hover\r\n * over the extension name inside the list of installed extensions.\r\n *\r\n * @default \"\"\r\n */\r\n info?: string;\r\n\r\n /**\r\n * Specify your extension version here, using numbers and dots (e.g., 2.01).\r\n * This is crucial for the automatic version checker in Bridge, which can notify users of updates.\r\n *\r\n * Note: the regex pattern used for validation is `^\\d+(?:\\.\\d+)*(?:-.*)?$`.\r\n * Although SAMMI Next allows beta/alpha suffixes due to its RegExp,\r\n * please note that the official SAMMI Bridge automatic version checker ignores them.\r\n * In other words, the users only will be notified of release updates.\r\n */\r\n version: string;\r\n\r\n /**\r\n * Specify your script.ts path here.\r\n * In your [insert_script] section, you’re encouraged to write your own TypeScript code.\r\n */\r\n entry: string;\r\n\r\n /**\r\n * Specify your external.html path here.\r\n * Your [insert_external] section appears inside the extension’s tab in Bridge,\r\n * and it provides a visual interface for the extension if needed. It’s written in HTML and CSS.\r\n *\r\n * @default undefined\r\n */\r\n external?: string;\r\n\r\n /**\r\n * Specify your over.json path here.\r\n * In your [insert_over] section you simply copy and paste your deck from SAMMI Core you wish to distribute with your extension.\r\n * When users install your extension, the deck will be automatically added to their SAMMI Core.\r\n *\r\n * @default undefined\r\n */\r\n over?: string;\r\n\r\n /** Configuration related with the extension building. */\r\n out?: {\r\n /**\r\n * The path where the extension will build to.\r\n *\r\n * @default \"dist\"\r\n */\r\n dir?: string;\r\n\r\n /**\r\n * The file name of the final JavaScript file.\r\n *\r\n * @default \"extension.js\"\r\n */\r\n js?: string;\r\n\r\n /**\r\n * The file name of the final SAMMI Extension File.\r\n *\r\n * @default \"extension.sef\"\r\n */\r\n sef?: string;\r\n }\r\n}\r\n\r\n/**\r\n * Helper function to define a SAMMI Next extension config with type safety.\r\n *\r\n * @example\r\n * ```ts\r\n * // sammi.config.ts\r\n * import { defineConfig } from 'sammi-next/config';\r\n *\r\n * export default defineConfig({\r\n * id: 'my-extension',\r\n * name: 'My Extension',\r\n * version: '1.0.0',\r\n * entry: 'src/script.ts',\r\n * });\r\n * ```\r\n */\r\nexport function defineConfig(config: ExtensionConfig): ExtensionConfig {\r\n return config;\r\n}\r\n\r\n/**\r\n * Resolved extension config with defaults applied.\r\n * Used internally by the builder.\r\n * @internal\r\n */\r\nexport interface ResolvedExtensionConfig extends Required<Omit<ExtensionConfig, 'out'>> {\r\n out: Required<NonNullable<ExtensionConfig['out']>>;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;AAuGA,SAAgB,aAAa,QAA0C;AACnE,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sammi-next",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Benjas333",
|
|
6
6
|
"url": "https://github.com/Benjas333"
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"type": "module",
|
|
65
65
|
"types": "./dist/runtime/index.d.ts",
|
|
66
66
|
"devDependencies": {
|
|
67
|
+
"@rolldown/plugin-node-polyfills": "^1.0.0",
|
|
67
68
|
"@types/node": "^25.0.9",
|
|
68
69
|
"sammi-bridge-types": "sammi-bridge-types"
|
|
69
70
|
}
|