stovesdk.js 0.0.0-stage → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ // Inspects the file list of `npm pack --dry-run --json` for what must and must not ship.
5
+ //
6
+ // node tools/check-tarball.js <pack.json> source tree: no binary at all, nothing from the SDK (ci.yml)
7
+ // node tools/check-tarball.js <pack.json> --with-prebuild publish: exactly prebuilds/win32-x64/stovesdk.node (+ SHA256SUMS), nothing from the SDK (publish.yml)
8
+ //
9
+ // The SDK is bring-your-own and is never redistributed: no sdk/ path, no .dll/.lib/.pdb/.h.
10
+ // The one binary a published tarball may carry is the maintainer-built addon, and only there.
11
+ //
12
+ // `npm pack --json` is an array of one entry up to npm 11 and an object keyed by package name
13
+ // from npm 12; packFiles() takes either.
14
+
15
+ const PREBUILD = 'prebuilds/win32-x64/stovesdk.node'
16
+ const SUMS = 'prebuilds/win32-x64/SHA256SUMS'
17
+ const SDK_FILE = /^sdk\/|\.(dll|lib|pdb|exe|h|hpp)$/i
18
+ const BINARY = /\.node$/i
19
+
20
+ // The file paths of the one package in `npm pack --dry-run --json` output, whichever shape.
21
+ function packFiles(json) {
22
+ const entry = Array.isArray(json) ? json[0] : json && typeof json === 'object' ? Object.values(json)[0] : undefined
23
+ if (!entry || !Array.isArray(entry.files)) throw new TypeError('not the output of `npm pack --json`: no package entry with a files list')
24
+ return { name: entry.name, version: entry.version, files: entry.files.map(f => f.path) }
25
+ }
26
+
27
+ function checkTarball(files, { withPrebuild = false } = {}) {
28
+ const problems = []
29
+ const sdk = files.filter(p => SDK_FILE.test(p))
30
+ if (sdk.length) problems.push(`would ship SDK material or a binary of the kind: ${sdk.join(', ')}`)
31
+ const binaries = files.filter(p => BINARY.test(p))
32
+ if (withPrebuild) {
33
+ for (const must of [PREBUILD, SUMS, 'index.js', 'index.d.ts', 'package.json']) {
34
+ if (!files.includes(must)) problems.push(`missing from the tarball: ${must}`)
35
+ }
36
+ const stray = binaries.filter(p => p !== PREBUILD)
37
+ if (stray.length) problems.push(`a binary other than the prebuild: ${stray.join(', ')}`)
38
+ } else if (binaries.length) {
39
+ problems.push(`a binary in the source tarball: ${binaries.join(', ')}`)
40
+ }
41
+ return problems
42
+ }
43
+
44
+ module.exports = { checkTarball, packFiles, PREBUILD, SUMS }
45
+
46
+ if (require.main === module) {
47
+ const [file, flag] = process.argv.slice(2)
48
+ if (!file) {
49
+ console.error('usage: node tools/check-tarball.js <pack.json> [--with-prebuild]')
50
+ process.exit(2)
51
+ }
52
+ const { name, version, files } = packFiles(require(require('path').resolve(file)))
53
+ const withPrebuild = flag === '--with-prebuild'
54
+ const problems = checkTarball(files, { withPrebuild })
55
+ if (problems.length) {
56
+ for (const p of problems) console.error(`::error::${p}`)
57
+ process.exit(1)
58
+ }
59
+ console.log(`npm pack: ${files.length} files, ${name}@${version}, ${withPrebuild ? 'prebuild in, ' : 'no binary, '}nothing from the SDK`)
60
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ // Called by binding.gyp (`<!(node tools/sdk-dir.js)`) to print the SDK directory, after checking
5
+ // that the layout lib/sdk-dir.js documents is present. Fails loudly otherwise — node-gyp would
6
+ // fail a few seconds later anyway, but with a far less readable error.
7
+ //
8
+ // node tools/sdk-dir.js → prints the resolved SDK directory
9
+ // node tools/sdk-dir.js version → prints the SDK version string ('unknown' if no VERSION file)
10
+ // node tools/sdk-dir.js check → exit 0 if the layout is complete, 1 otherwise (prints what is missing)
11
+
12
+ const { resolveSdkDir, missingSdkFiles, readSdkVersion } = require('../lib/sdk-dir')
13
+
14
+ const sdkDir = resolveSdkDir()
15
+ const mode = process.argv[2] || 'dir'
16
+
17
+ if (mode === 'version') {
18
+ process.stdout.write(readSdkVersion(sdkDir))
19
+ process.exit(0)
20
+ }
21
+
22
+ const missing = missingSdkFiles(sdkDir)
23
+ if (missing.length) {
24
+ process.stderr.write(`[stovesdk] STOVE PC SDK not found at ${sdkDir}\n`)
25
+ process.stderr.write('[stovesdk] Place the SDK under sdk/ or set STOVE_PCSDK_DIR (see sdk/README.md). Missing:\n')
26
+ for (const file of missing) process.stderr.write(` - ${file}\n`)
27
+ process.exit(1)
28
+ }
29
+
30
+ if (mode === 'check') {
31
+ process.stdout.write(`[stovesdk] SDK layout OK at ${sdkDir} (version ${readSdkVersion(sdkDir)})\n`)
32
+ process.exit(0)
33
+ }
34
+
35
+ // gyp reads this verbatim; forward slashes keep it portable inside the generated project files.
36
+ process.stdout.write(sdkDir.replace(/\\/g, '/'))
package/tools/smoke.js ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ // Load-only smoke for a freshly built addon (Windows). Proves, without a STOVE client:
5
+ // 1. the .node loads (its DLL imports resolve — the SDK DLLs must sit next to it or next to node.exe),
6
+ // 2. the export surface matches index.d.ts,
7
+ // 3. the pure-addon paths behave: refusals before initialize(), argument validation, no-op pump,
8
+ // no-op uninitialize().
9
+ // Nothing initializes the SDK — that needs a STOVE client session on the machine.
10
+ //
11
+ // node tools/smoke.js [path/to/stovesdk.node]
12
+
13
+ const path = require('path')
14
+ const { load } = require('..')
15
+
16
+ const EXPECTED_EXPORTS = [
17
+ 'getAddonInfo', 'getVersion', 'initialize', 'runCallbacks', 'isInitialized', 'uninitialize',
18
+ 'getOwnershipList', 'getUser', 'getGds', 'getAchievement', 'getAllAchievements', 'getStat', 'setStat',
19
+ ]
20
+
21
+ function fail(message) {
22
+ console.error(`[smoke] ${message}`)
23
+ process.exit(1)
24
+ }
25
+
26
+ const sdk = load(process.argv[2] ? path.resolve(process.argv[2]) : undefined)
27
+
28
+ const missing = EXPECTED_EXPORTS.filter(name => typeof sdk[name] !== 'function')
29
+ if (missing.length) fail(`missing exports: ${missing.join(', ')}`)
30
+ if (!sdk.constants || !sdk.constants.OwnershipCode || !sdk.constants.OwnershipGameCode || !sdk.constants.ErrorCode) {
31
+ fail('constants are missing')
32
+ }
33
+
34
+ const info = sdk.getAddonInfo()
35
+ console.log('[smoke] addon info:', JSON.stringify(info))
36
+ console.log('[smoke] constants:', JSON.stringify(sdk.constants))
37
+
38
+ if (sdk.isInitialized() !== false) fail('expected isInitialized() === false on a fresh load')
39
+
40
+ // Informational — exercises a real call into BaseSDK.dll.
41
+ console.log('[smoke] Base_GetVersion:', JSON.stringify(sdk.getVersion()))
42
+
43
+ // Synchronous identity reads must answer, not throw, before initialize().
44
+ for (const name of ['getUser', 'getGds']) {
45
+ const answer = sdk[name]()
46
+ console.log(`[smoke] ${name}() before init:`, JSON.stringify(answer))
47
+ if (!answer || answer.ok !== false || answer.code !== sdk.constants.ErrorCode.NOT_INITIALIZED) {
48
+ fail(`${name}() before init must report { ok: false, code: ErrorCode.NOT_INITIALIZED }`)
49
+ }
50
+ }
51
+
52
+ // runCallbacks() must be a no-op when nothing is in flight.
53
+ sdk.runCallbacks()
54
+
55
+ // uninitialize() on a fresh addon must be a harmless no-op with all three slots null.
56
+ const down = sdk.uninitialize()
57
+ console.log('[smoke] uninitialize() on fresh addon:', JSON.stringify(down))
58
+ if (down.base !== null || down.ownership !== null || down.gameSupport !== null) {
59
+ fail('uninitialize() reported an UnInitialize on a never-initialized SDK')
60
+ }
61
+
62
+ // Argument validation is synchronous (TypeError / RangeError), never a crash and never an SDK call.
63
+ function expectThrow(label, fn, ctor) {
64
+ try {
65
+ fn()
66
+ } catch (err) {
67
+ if (err instanceof ctor) {
68
+ console.log(`[smoke] ${label} threw ${ctor.name} as expected:`, err.message)
69
+ return
70
+ }
71
+ fail(`${label} threw the wrong error type: ${err}`)
72
+ }
73
+ fail(`${label} did not throw`)
74
+ }
75
+ expectThrow('initialize() without options', () => sdk.initialize(), TypeError)
76
+ expectThrow('setStat("x") without a value', () => sdk.setStat('x'), TypeError)
77
+ expectThrow('setStat("x", 1.5)', () => sdk.setStat('x', 1.5), RangeError)
78
+ expectThrow('getAchievement(42)', () => sdk.getAchievement(42), TypeError)
79
+ expectThrow('getStat(null)', () => sdk.getStat(null), TypeError)
80
+
81
+ // Before initialize() every asynchronous entry point must reject through its Promise —
82
+ // never crash the process, never reach the SDK.
83
+ function expectReject(label, promise, step) {
84
+ return promise.then(
85
+ () => fail(`${label} resolved without the SDK initialized`),
86
+ err => {
87
+ if (!err || err.step !== step || err.code !== sdk.constants.ErrorCode.NOT_INITIALIZED) {
88
+ fail(`${label} rejected with an unexpected shape: step=${err && err.step} code=${err && err.code}`)
89
+ }
90
+ console.log(`[smoke] ${label} rejected as expected:`, err.message)
91
+ }
92
+ )
93
+ }
94
+
95
+ expectReject('getOwnershipList()', sdk.getOwnershipList(), 'ownershipList')
96
+ .then(() => expectReject('setStat("x", 1)', sdk.setStat('x', 1), 'modifyStat'))
97
+ .then(() => expectReject('getStat("x")', sdk.getStat('x'), 'stat'))
98
+ .then(() => expectReject('getAchievement("x")', sdk.getAchievement('x'), 'achievement'))
99
+ .then(() => expectReject('getAllAchievements()', sdk.getAllAchievements(), 'allAchievements'))
100
+ .then(() => {
101
+ console.log('[smoke] OK')
102
+ })
@@ -0,0 +1,34 @@
1
+ #!/bin/sh
2
+ # Parses src/stovesdk.cc with clang `-fsyntax-only` on a non-Windows machine, against the real
3
+ # SDK headers (STOVE_PCSDK_DIR or ./sdk) and a stub <windows.h>. It catches typos, wrong SDK
4
+ # accessor names and N-API misuse before the code reaches a Windows toolchain. It is NOT a build:
5
+ # nothing is linked, and MSVC-only behaviour is not exercised.
6
+ #
7
+ # sh tools/syntax-check.sh (needs: clang++, node_modules/node-addon-api, Node headers)
8
+ set -eu
9
+
10
+ ROOT=$(cd "$(dirname "$0")/.." && pwd)
11
+ SDK_DIR=$(node "$ROOT/tools/sdk-dir.js")
12
+ NAPI_INC=$(node -p "require('$ROOT/node_modules/node-addon-api').include.replace(/\"/g, '')")
13
+ NODE_INC="${NODE_INCLUDE_DIR:-$(dirname "$(dirname "$(command -v node)")")/include/node}"
14
+ [ -f "$NODE_INC/node_api.h" ] || { echo "node_api.h not found under $NODE_INC (set NODE_INCLUDE_DIR)" >&2; exit 1; }
15
+
16
+ STUB=$(mktemp -d)
17
+ trap 'rm -rf "$STUB"' EXIT
18
+ cat > "$STUB/windows.h" <<'EOF'
19
+ // Minimal stand-in for <windows.h>: only what src/stovesdk.cc and the SDK headers touch.
20
+ #pragma once
21
+ #include <cstdint>
22
+ #define __cdecl
23
+ #define CP_UTF8 65001
24
+ #define __out
25
+ extern "C" int WideCharToMultiByte(unsigned, unsigned long, const wchar_t*, int, char*, int, const char*, int*);
26
+ EOF
27
+
28
+ clang++ -fsyntax-only -std=c++17 -fshort-wchar -fdeclspec -fms-extensions \
29
+ -Wno-ignored-attributes -Wno-microsoft -Wno-pragma-pack \
30
+ -DSTOVE_PCSDK_SYNTAX_CHECK -DNAPI_VERSION=8 -DNAPI_DISABLE_CPP_EXCEPTIONS \
31
+ -I "$STUB" -I "$NAPI_INC" -I "$NODE_INC" \
32
+ -I "$SDK_DIR" -I "$SDK_DIR/BaseSDK/Deploy/Include" -I "$SDK_DIR/OwnershipSDK/Deploy/Include" -I "$SDK_DIR/GameSupportSDK/Deploy/Include" \
33
+ "$ROOT/src/stovesdk.cc"
34
+ echo "[syntax-check] OK: src/stovesdk.cc parses against the SDK headers at $SDK_DIR"