SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
This commit is contained in:
commit
fa9c73363d
1367 files changed
+355735
No files matched your search
@@ -0,0 +1,261 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
copyFileSync,
|
||||
cpSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
symlinkSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import { createRequire } from 'node:module';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { afterAll, beforeAll, describe, it } from 'vitest';
|
||||
|
||||
const sdkRoot = fileURLToPath(new URL('..', import.meta.url));
|
||||
const compiler = createRequire(import.meta.url).resolve('typescript/bin/tsc');
|
||||
const manifest = JSON.parse(readFileSync(path.join(sdkRoot, 'package.json'), 'utf8')) as {
|
||||
name: string;
|
||||
exports: Record<string, string | { import: string; types: string }>;
|
||||
};
|
||||
const entrypoints = Object.entries(manifest.exports)
|
||||
.filter(([, target]) => typeof target !== 'string')
|
||||
.map(([entry]) => `${manifest.name}${entry === '.' ? '' : entry.slice(1)}`);
|
||||
let consumerRoot: string;
|
||||
|
||||
function node(args: string[]): void {
|
||||
const result = spawnSync(process.execPath, args, {
|
||||
cwd: consumerRoot,
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
timeout: 30_000,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`Node exited with status ${result.status} (signal ${result.signal}):\n${result.stdout}\n${result.stderr}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
consumerRoot = mkdtempSync(path.join(tmpdir(), 'snowluma-sdk-package-'));
|
||||
const installed = path.join(consumerRoot, 'node_modules', '@snowluma', 'sdk');
|
||||
mkdirSync(installed, { recursive: true });
|
||||
copyFileSync(path.join(sdkRoot, 'package.json'), path.join(installed, 'package.json'));
|
||||
// Install the declared runtime dependency into this isolated consumer. Node
|
||||
// type declarations belong to the consuming Node project's dev environment.
|
||||
const require = createRequire(import.meta.url);
|
||||
cpSync(
|
||||
path.dirname(require.resolve('ws/package.json')),
|
||||
path.join(consumerRoot, 'node_modules', 'ws'),
|
||||
{ recursive: true },
|
||||
);
|
||||
mkdirSync(path.join(consumerRoot, 'node_modules', '@types'), { recursive: true });
|
||||
symlinkSync(
|
||||
path.dirname(require.resolve('@types/node/package.json')),
|
||||
path.join(consumerRoot, 'node_modules', '@types', 'node'),
|
||||
'dir',
|
||||
);
|
||||
|
||||
copyFileSync(
|
||||
path.join(sdkRoot, 'tests', 'fixtures', 'action-types.mts'),
|
||||
path.join(consumerRoot, 'action-types.mts'),
|
||||
);
|
||||
copyFileSync(
|
||||
path.join(sdkRoot, 'tests', 'fixtures', 'action-contracts.ts'),
|
||||
path.join(consumerRoot, 'action-contracts.ts'),
|
||||
);
|
||||
copyFileSync(
|
||||
path.join(sdkRoot, 'tests', 'fixtures', 'messaging-types.mts'),
|
||||
path.join(consumerRoot, 'messaging-types.mts'),
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
path.join(consumerRoot, 'reverse-types.mts'),
|
||||
`import { text } from '@snowluma/sdk';
|
||||
import { createReverseWebSocketServer, type SnowLumaReverseWebSocketSession } from '@snowluma/sdk/node';
|
||||
const reverse = createReverseWebSocketServer({ port: 0, selfIds: ['10001'] });
|
||||
reverse.on('connection', (bot: SnowLumaReverseWebSocketSession) => {
|
||||
const id: string = bot.selfId;
|
||||
const ready: boolean = bot.canCallActions;
|
||||
void bot.getStatus();
|
||||
bot.onMessage(async (_event, context) => { await context.reply(text(id)); });
|
||||
void ready;
|
||||
});
|
||||
`,
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
path.join(consumerRoot, 'browser-types.mts'),
|
||||
`
|
||||
import { SnowLumaWebSocketClient, text } from '@snowluma/sdk';
|
||||
new SnowLumaWebSocketClient().onMessage(async (_event, context) => { await context.reply(text('hello')); });
|
||||
// @ts-expect-error Node globals must not leak through the browser entry.
|
||||
process.cwd();
|
||||
`,
|
||||
);
|
||||
writeFileSync(
|
||||
path.join(consumerRoot, 'tsconfig.browser.json'),
|
||||
JSON.stringify({
|
||||
compilerOptions: {
|
||||
strict: true,
|
||||
noEmit: true,
|
||||
target: 'ES2022',
|
||||
lib: ['ES2022', 'DOM'],
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'Bundler',
|
||||
types: [],
|
||||
},
|
||||
files: ['browser-types.mts'],
|
||||
}),
|
||||
);
|
||||
|
||||
// Compile fresh output outside the workspace so neither stale dist files nor
|
||||
// Vitest's source resolver can hide a broken published entry point.
|
||||
node([
|
||||
compiler,
|
||||
'--project',
|
||||
path.join(sdkRoot, 'tsconfig.json'),
|
||||
'--outDir',
|
||||
path.join(installed, 'dist'),
|
||||
]);
|
||||
writeFileSync(
|
||||
path.join(consumerRoot, 'consumer.mts'),
|
||||
`import { SnowLumaHttpClient, text, type SnowLumaKnownAction as RootKnownAction } from '@snowluma/sdk';
|
||||
import { SnowLumaWebSocketClient, type SnowLumaHttpClientOptions } from '@snowluma/sdk/client';
|
||||
import { MessageChain } from '@snowluma/sdk/messages';
|
||||
import { isMessageEvent, type EventMiddleware } from '@snowluma/sdk/events';
|
||||
import { SnowLumaApiError } from '@snowluma/sdk/errors';
|
||||
import { SNOWLUMA_ACTIONS, isSnowLumaAction, type SnowLumaKnownAction } from '@snowluma/sdk/actions';
|
||||
import type { ActionParams, StatusInfo } from '@snowluma/sdk/types';
|
||||
|
||||
const options: SnowLumaHttpClientOptions = { baseUrl: 'http://127.0.0.1:3000/' };
|
||||
const client = new SnowLumaHttpClient(options);
|
||||
const status: Promise<StatusInfo> = client.getStatus();
|
||||
const message: MessageChain = text('hello').reply(-42);
|
||||
const params: ActionParams<'send_group_msg'> = { group_id: 123, message: [] };
|
||||
client.raw('send_group_msg', params);
|
||||
client.raw('consumer_extension', { custom: true });
|
||||
client.sendGroupMessage(123, message);
|
||||
const middleware: EventMiddleware = async (event, context, next) => {
|
||||
if (isMessageEvent(event)) await context.reply(text('received'));
|
||||
await next();
|
||||
};
|
||||
new SnowLumaWebSocketClient().use(middleware);
|
||||
const action: RootKnownAction = SNOWLUMA_ACTIONS[0];
|
||||
function known(value: string): SnowLumaKnownAction | null {
|
||||
return isSnowLumaAction(value) ? value : null;
|
||||
}
|
||||
const error = new SnowLumaApiError({ status: 'failed', retcode: 1, data: null });
|
||||
export { status, action, known, error };
|
||||
`,
|
||||
);
|
||||
}, 30_000);
|
||||
|
||||
afterAll(() => {
|
||||
if (consumerRoot) {
|
||||
rmSync(consumerRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe('SDK package entry points', () => {
|
||||
it('keeps browser consumers independent of Node server declarations', () => {
|
||||
node([compiler, '-p', path.join(consumerRoot, 'tsconfig.browser.json')]);
|
||||
}, 30_000);
|
||||
|
||||
it('starts the published Node reverse server without workspace resolution', () => {
|
||||
node([
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import assert from 'node:assert/strict';
|
||||
import { createReverseWebSocketServer } from '@snowluma/sdk/node';
|
||||
const server = createReverseWebSocketServer({ port: 0 });
|
||||
await server.listen();
|
||||
assert.ok(server.address.port > 0);
|
||||
await server.close();
|
||||
`,
|
||||
]);
|
||||
});
|
||||
|
||||
it.each(entrypoints)('loads %s in native Node without a bundler', (entrypoint) => {
|
||||
node(['--input-type=module', '--eval', `await import(${JSON.stringify(entrypoint)});`]);
|
||||
});
|
||||
|
||||
it('preserves runtime identities across the root and subpath exports', () => {
|
||||
node([
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`import assert from 'node:assert/strict';
|
||||
import * as sdk from '@snowluma/sdk';
|
||||
import * as client from '@snowluma/sdk/client';
|
||||
import * as messages from '@snowluma/sdk/messages';
|
||||
import * as events from '@snowluma/sdk/events';
|
||||
import * as errors from '@snowluma/sdk/errors';
|
||||
import * as actions from '@snowluma/sdk/actions';
|
||||
assert.equal(sdk.SnowLumaHttpClient, client.SnowLumaHttpClient);
|
||||
assert.equal(sdk.SnowLumaWebSocketClient, client.SnowLumaWebSocketClient);
|
||||
assert.equal(sdk.MessageChain, messages.MessageChain);
|
||||
assert.equal(sdk.quoteMessage, messages.quoteMessage);
|
||||
assert.equal(sdk.isMessageEvent, events.isMessageEvent);
|
||||
assert.equal(sdk.SnowLumaApiError, errors.SnowLumaApiError);
|
||||
assert.equal(sdk.SnowLumaMediaSendError, errors.SnowLumaMediaSendError);
|
||||
assert.equal(sdk.SNOWLUMA_ACTIONS, actions.SNOWLUMA_ACTIONS);
|
||||
assert.equal(sdk.isSnowLumaAction, actions.isSnowLumaAction);
|
||||
assert.ok(sdk.text('hello') instanceof messages.MessageChain);
|
||||
assert.equal(actions.isSnowLumaAction('send_group_msg'), true);
|
||||
assert.equal(actions.isSnowLumaAction('consumer_extension'), false);
|
||||
`,
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['NodeNext', 'NodeNext'],
|
||||
['ESNext', 'Bundler'],
|
||||
])(
|
||||
'typechecks a consumer using %s / %s',
|
||||
(module, resolution) => {
|
||||
node([
|
||||
compiler,
|
||||
'--noEmit',
|
||||
'--strict',
|
||||
'--target',
|
||||
'ES2022',
|
||||
'--lib',
|
||||
'ES2022,DOM',
|
||||
'--module',
|
||||
module,
|
||||
'--moduleResolution',
|
||||
resolution,
|
||||
path.join(consumerRoot, 'consumer.mts'),
|
||||
path.join(consumerRoot, 'action-types.mts'),
|
||||
path.join(consumerRoot, 'messaging-types.mts'),
|
||||
]);
|
||||
node([
|
||||
compiler,
|
||||
'--noEmit',
|
||||
'--strict',
|
||||
'--target',
|
||||
'ES2022',
|
||||
'--lib',
|
||||
'ES2022,DOM',
|
||||
'--types',
|
||||
'node',
|
||||
'--module',
|
||||
module,
|
||||
'--moduleResolution',
|
||||
resolution,
|
||||
path.join(consumerRoot, 'reverse-types.mts'),
|
||||
]);
|
||||
},
|
||||
30_000,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user