@yydb/yydb 0.0.1 → 0.0.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/README.md +77 -17
- package/dist/cli/cli.d.ts +1 -0
- package/dist/cli/cli.js +27 -0
- package/dist/cli/generate-cmd.d.ts +3 -0
- package/dist/cli/generate-cmd.js +25 -0
- package/dist/cli/info-cmd.d.ts +3 -0
- package/dist/cli/info-cmd.js +10 -0
- package/dist/cli/init-cmd.d.ts +3 -0
- package/dist/cli/init-cmd.js +18 -0
- package/dist/cli/options.d.ts +4 -0
- package/dist/cli/options.js +14 -0
- package/dist/cli/serve-cmd.d.ts +3 -0
- package/dist/cli/serve-cmd.js +14 -0
- package/dist/cli/version-cmd.d.ts +3 -0
- package/dist/cli/version-cmd.js +8 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +6 -0
- package/dist/database.d.ts +20 -2
- package/dist/database.js +70 -28
- package/dist/generator/emit-typescript.d.ts +3 -0
- package/dist/generator/emit-typescript.js +26 -0
- package/dist/generator/fixtures/setting.generated.d.ts +13 -0
- package/dist/generator/fixtures/setting.generated.js +3 -0
- package/dist/generator/index.d.ts +6 -0
- package/dist/generator/index.js +10 -0
- package/dist/generator/ir.d.ts +20 -0
- package/dist/generator/ir.js +12 -0
- package/dist/generator/parse-vos.d.ts +6 -0
- package/dist/generator/parse-vos.js +149 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/micro.d.ts +63 -0
- package/dist/micro.js +160 -0
- package/dist/native.d.ts +9 -0
- package/dist/native.js +66 -0
- package/dist/resolve-bin.d.ts +9 -4
- package/dist/resolve-bin.js +31 -47
- package/dist/udf-types.d.ts +26 -0
- package/dist/udf-types.js +16 -0
- package/package.json +25 -13
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
export class VosParseError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'VosParseError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Interim VOS subset parser for `yydb generate` (MVP). */
|
|
8
|
+
export function parseVosSubset(source, schemaVersion) {
|
|
9
|
+
if (source.includes('\0')) {
|
|
10
|
+
throw new VosParseError('VOS document must not contain NUL bytes');
|
|
11
|
+
}
|
|
12
|
+
let normalized = source.startsWith('\uFEFF') ? source.slice(1) : source;
|
|
13
|
+
normalized = normalized.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
14
|
+
const lexer = new Lexer(normalized);
|
|
15
|
+
const tables = [];
|
|
16
|
+
while (!lexer.eof()) {
|
|
17
|
+
lexer.skipWsAndComments();
|
|
18
|
+
if (lexer.eof()) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
tables.push(parseTable(lexer));
|
|
22
|
+
}
|
|
23
|
+
if (tables.length === 0) {
|
|
24
|
+
throw new VosParseError('expected at least one `table` declaration');
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
vosSource: normalized,
|
|
28
|
+
schemaVersion,
|
|
29
|
+
tables,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function parseTable(lexer) {
|
|
33
|
+
lexer.expectIdent('table');
|
|
34
|
+
const name = lexer.expectAnyIdent();
|
|
35
|
+
lexer.expectPunct('{');
|
|
36
|
+
const fields = [];
|
|
37
|
+
while (true) {
|
|
38
|
+
lexer.skipWsAndComments();
|
|
39
|
+
if (lexer.eatPunct('}')) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (lexer.eatPunct('@')) {
|
|
43
|
+
lexer.expectPunct('@');
|
|
44
|
+
const attr = lexer.expectAnyIdent();
|
|
45
|
+
if (attr !== 'id') {
|
|
46
|
+
throw new VosParseError(`unsupported table attribute \`@@${attr}\` (MVP supports \`@@id\` only)`);
|
|
47
|
+
}
|
|
48
|
+
lexer.expectPunct(':');
|
|
49
|
+
const ty = parseScalar(lexer);
|
|
50
|
+
fields.push({ name: 'id', ty, isId: true });
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const fieldName = lexer.expectAnyIdent();
|
|
54
|
+
lexer.expectPunct(':');
|
|
55
|
+
const ty = parseScalar(lexer);
|
|
56
|
+
fields.push({ name: fieldName, ty, isId: false });
|
|
57
|
+
}
|
|
58
|
+
lexer.skipWsAndComments();
|
|
59
|
+
lexer.eatPunct(',');
|
|
60
|
+
}
|
|
61
|
+
if (!fields.some((field) => field.isId)) {
|
|
62
|
+
throw new VosParseError(`table \`${name}\` requires \`@@id: <type>\``);
|
|
63
|
+
}
|
|
64
|
+
return { name, fields };
|
|
65
|
+
}
|
|
66
|
+
function parseScalar(lexer) {
|
|
67
|
+
const name = lexer.expectAnyIdent();
|
|
68
|
+
switch (name) {
|
|
69
|
+
case 'uuid':
|
|
70
|
+
return 'uuid';
|
|
71
|
+
case 'utf8':
|
|
72
|
+
return 'utf8';
|
|
73
|
+
case 'bool':
|
|
74
|
+
return 'bool';
|
|
75
|
+
case 'i64':
|
|
76
|
+
return 'i64';
|
|
77
|
+
case 'f64':
|
|
78
|
+
return 'f64';
|
|
79
|
+
default:
|
|
80
|
+
throw new VosParseError(`unsupported scalar type \`${name}\` (MVP: uuid, utf8, bool, i64, f64)`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class Lexer {
|
|
84
|
+
src;
|
|
85
|
+
i = 0;
|
|
86
|
+
constructor(src) {
|
|
87
|
+
this.src = src;
|
|
88
|
+
}
|
|
89
|
+
eof() {
|
|
90
|
+
return this.i >= this.src.length;
|
|
91
|
+
}
|
|
92
|
+
peek() {
|
|
93
|
+
return this.src[this.i];
|
|
94
|
+
}
|
|
95
|
+
bump() {
|
|
96
|
+
const ch = this.peek();
|
|
97
|
+
if (ch === undefined) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
this.i += 1;
|
|
101
|
+
return ch;
|
|
102
|
+
}
|
|
103
|
+
skipWsAndComments() {
|
|
104
|
+
while (true) {
|
|
105
|
+
while (this.peek() !== undefined && /\s/.test(this.peek())) {
|
|
106
|
+
this.bump();
|
|
107
|
+
}
|
|
108
|
+
if (this.src.startsWith('//', this.i)) {
|
|
109
|
+
while (this.peek() !== undefined && this.peek() !== '\n') {
|
|
110
|
+
this.bump();
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
eatPunct(expected) {
|
|
118
|
+
this.skipWsAndComments();
|
|
119
|
+
if (this.peek() === expected) {
|
|
120
|
+
this.bump();
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
expectPunct(expected) {
|
|
126
|
+
if (!this.eatPunct(expected)) {
|
|
127
|
+
throw new VosParseError(`expected \`${expected}\` at byte ${this.i}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
expectIdent(expected) {
|
|
131
|
+
const got = this.expectAnyIdent();
|
|
132
|
+
if (got !== expected) {
|
|
133
|
+
throw new VosParseError(`expected \`${expected}\`, found \`${got}\``);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
expectAnyIdent() {
|
|
137
|
+
this.skipWsAndComments();
|
|
138
|
+
const start = this.i;
|
|
139
|
+
const first = this.peek();
|
|
140
|
+
if (first === undefined || !/[A-Za-z_]/.test(first)) {
|
|
141
|
+
throw new VosParseError(`expected identifier at byte ${this.i}`);
|
|
142
|
+
}
|
|
143
|
+
this.bump();
|
|
144
|
+
while (this.peek() !== undefined && /[A-Za-z0-9_]/.test(this.peek())) {
|
|
145
|
+
this.bump();
|
|
146
|
+
}
|
|
147
|
+
return this.src.slice(start, this.i);
|
|
148
|
+
}
|
|
149
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export { Database, type OpenOptions } from
|
|
2
|
-
export {
|
|
1
|
+
export { Database, type OpenOptions } from './database.js';
|
|
2
|
+
export { defineMicro, type DefinedMicro, type MicroHandle, type TsMicroDefinitionInput, MicroSessionRegistry } from './micro.js';
|
|
3
|
+
export { bool, i64, nullType, text, type InferUdfArgs, type InferUdfReturn, type MicroEffect, type MicroInvocationMode, type UdfScalarKind, type UdfTypeDescriptor, } from './udf-types.js';
|
|
4
|
+
export { nativeBindingName, platformKey, resolveYydbBinary, resolveYydbCli } from './resolve-bin.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export { Database } from
|
|
2
|
-
export {
|
|
1
|
+
export { Database } from './database.js';
|
|
2
|
+
export { defineMicro, MicroSessionRegistry } from './micro.js';
|
|
3
|
+
export { bool, i64, nullType, text, } from './udf-types.js';
|
|
4
|
+
export { nativeBindingName, platformKey, resolveYydbBinary, resolveYydbCli } from './resolve-bin.js';
|
package/dist/micro.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { InferUdfArgs, InferUdfReturn, MicroEffect, MicroInvocationMode, UdfTypeDescriptor } from './udf-types.js';
|
|
2
|
+
/** Input accepted by [`Database.defineMicro`](./database.ts). */
|
|
3
|
+
export interface TsMicroDefinitionInput<TArgs extends readonly UdfTypeDescriptor[], TReturn extends UdfTypeDescriptor> {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly version: number;
|
|
6
|
+
readonly args: TArgs;
|
|
7
|
+
readonly returns: TReturn;
|
|
8
|
+
readonly effect: MicroEffect;
|
|
9
|
+
readonly deterministic: boolean;
|
|
10
|
+
readonly mode?: MicroInvocationMode;
|
|
11
|
+
readonly fn: (...args: InferUdfArgs<TArgs>) => InferUdfReturn<TReturn>;
|
|
12
|
+
}
|
|
13
|
+
/** Opaque definition produced by `defineMicro` before session registration. */
|
|
14
|
+
export interface DefinedMicro<TArgs extends readonly UdfTypeDescriptor[] = readonly UdfTypeDescriptor[], TReturn extends UdfTypeDescriptor = UdfTypeDescriptor> {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly version: number;
|
|
17
|
+
readonly args: TArgs;
|
|
18
|
+
readonly returns: TReturn;
|
|
19
|
+
readonly effect: MicroEffect;
|
|
20
|
+
readonly deterministic: boolean;
|
|
21
|
+
readonly mode: MicroInvocationMode;
|
|
22
|
+
readonly placement: 'host';
|
|
23
|
+
readonly fn: (...args: InferUdfArgs<TArgs>) => InferUdfReturn<TReturn>;
|
|
24
|
+
}
|
|
25
|
+
/** Handle returned after `registerMicro` installs a micro into the session. */
|
|
26
|
+
export interface MicroHandle {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly version: number;
|
|
29
|
+
readonly functionId: string;
|
|
30
|
+
readonly hostId: number;
|
|
31
|
+
/** Monotonic host implementation version for opaque handle checks. */
|
|
32
|
+
readonly implementationVersion: number;
|
|
33
|
+
}
|
|
34
|
+
type AnyDefinedMicro = DefinedMicro<readonly UdfTypeDescriptor[], UdfTypeDescriptor>;
|
|
35
|
+
/**
|
|
36
|
+
* Process-local registry for JS functions behind opaque host handles.
|
|
37
|
+
* Rust keeps `host_id + function_id + version`, not the closure.
|
|
38
|
+
*/
|
|
39
|
+
export declare class MicroSessionRegistry {
|
|
40
|
+
readonly hostId: number;
|
|
41
|
+
private nextFunctionId;
|
|
42
|
+
private readonly byKey;
|
|
43
|
+
private readonly byFunctionId;
|
|
44
|
+
constructor(hostId?: number);
|
|
45
|
+
/** Installs a defined micro into the current session registry. */
|
|
46
|
+
register<TArgs extends readonly UdfTypeDescriptor[], TReturn extends UdfTypeDescriptor>(definition: DefinedMicro<TArgs, TReturn>): MicroHandle;
|
|
47
|
+
/** Returns a registered micro by opaque host handle. */
|
|
48
|
+
resolve(handle: MicroHandle): AnyDefinedMicro | undefined;
|
|
49
|
+
/** Invokes a registered micro from a wire `MicroHostInvoke` payload. */
|
|
50
|
+
invokeFromWire(payload: {
|
|
51
|
+
hostId: number;
|
|
52
|
+
handleVersion: number;
|
|
53
|
+
functionId: string;
|
|
54
|
+
args: readonly unknown[];
|
|
55
|
+
}): string | number | boolean | null;
|
|
56
|
+
/** Invokes a registered scalar micro (host adapter entry point). */
|
|
57
|
+
invokeScalar(handle: MicroHandle, args: unknown[]): unknown;
|
|
58
|
+
/** Lists logical names currently registered in this session. */
|
|
59
|
+
list(): string[];
|
|
60
|
+
}
|
|
61
|
+
/** Builds a session-local micro definition without registering it. */
|
|
62
|
+
export declare function defineMicro<TArgs extends readonly UdfTypeDescriptor[], TReturn extends UdfTypeDescriptor>(input: TsMicroDefinitionInput<TArgs, TReturn>): DefinedMicro<TArgs, TReturn>;
|
|
63
|
+
export {};
|
package/dist/micro.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
let nextHostId = 1;
|
|
2
|
+
/**
|
|
3
|
+
* Process-local registry for JS functions behind opaque host handles.
|
|
4
|
+
* Rust keeps `host_id + function_id + version`, not the closure.
|
|
5
|
+
*/
|
|
6
|
+
export class MicroSessionRegistry {
|
|
7
|
+
hostId;
|
|
8
|
+
nextFunctionId = 1;
|
|
9
|
+
byKey = new Map();
|
|
10
|
+
byFunctionId = new Map();
|
|
11
|
+
constructor(hostId = nextHostId++) {
|
|
12
|
+
this.hostId = hostId;
|
|
13
|
+
}
|
|
14
|
+
/** Installs a defined micro into the current session registry. */
|
|
15
|
+
register(definition) {
|
|
16
|
+
const key = logicalKey(definition.name, definition.version);
|
|
17
|
+
if (this.byKey.has(key)) {
|
|
18
|
+
throw new Error(`micro already registered: ${definition.name}@${definition.version}`);
|
|
19
|
+
}
|
|
20
|
+
const functionId = String(this.nextFunctionId++);
|
|
21
|
+
const handle = {
|
|
22
|
+
name: definition.name,
|
|
23
|
+
version: definition.version,
|
|
24
|
+
functionId,
|
|
25
|
+
hostId: this.hostId,
|
|
26
|
+
implementationVersion: 1,
|
|
27
|
+
};
|
|
28
|
+
const entry = { handle, definition: definition };
|
|
29
|
+
this.byKey.set(key, entry);
|
|
30
|
+
this.byFunctionId.set(functionId, entry);
|
|
31
|
+
return handle;
|
|
32
|
+
}
|
|
33
|
+
/** Returns a registered micro by opaque host handle. */
|
|
34
|
+
resolve(handle) {
|
|
35
|
+
if (handle.hostId !== this.hostId) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return this.byFunctionId.get(handle.functionId)?.definition;
|
|
39
|
+
}
|
|
40
|
+
/** Invokes a registered micro from a wire `MicroHostInvoke` payload. */
|
|
41
|
+
invokeFromWire(payload) {
|
|
42
|
+
const handle = {
|
|
43
|
+
hostId: payload.hostId,
|
|
44
|
+
functionId: payload.functionId,
|
|
45
|
+
implementationVersion: payload.handleVersion,
|
|
46
|
+
name: '',
|
|
47
|
+
version: 0,
|
|
48
|
+
};
|
|
49
|
+
return this.invokeScalar(handle, [...payload.args]);
|
|
50
|
+
}
|
|
51
|
+
/** Invokes a registered scalar micro (host adapter entry point). */
|
|
52
|
+
invokeScalar(handle, args) {
|
|
53
|
+
const definition = this.resolve(handle);
|
|
54
|
+
if (!definition) {
|
|
55
|
+
throw new Error(`micro not found: ${handle.functionId}`);
|
|
56
|
+
}
|
|
57
|
+
if (definition.mode !== 'scalar') {
|
|
58
|
+
throw new Error(`micro ${definition.name} is not scalar mode`);
|
|
59
|
+
}
|
|
60
|
+
if (args.length !== definition.args.length) {
|
|
61
|
+
throw new Error(`micro ${definition.name} arity mismatch: expected ${definition.args.length}, got ${args.length}`);
|
|
62
|
+
}
|
|
63
|
+
validateArgTypes(definition, args);
|
|
64
|
+
const result = definition.fn(...args);
|
|
65
|
+
validateReturnType(definition.returns, result);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
/** Lists logical names currently registered in this session. */
|
|
69
|
+
list() {
|
|
70
|
+
return [...this.byKey.values()].map((entry) => entry.definition.name).sort();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Builds a session-local micro definition without registering it. */
|
|
74
|
+
export function defineMicro(input) {
|
|
75
|
+
if (!input.name.trim()) {
|
|
76
|
+
throw new Error('micro name must not be empty');
|
|
77
|
+
}
|
|
78
|
+
if (!Number.isInteger(input.version) || input.version <= 0) {
|
|
79
|
+
throw new Error('micro version must be a positive integer');
|
|
80
|
+
}
|
|
81
|
+
if (input.effect !== 'pure') {
|
|
82
|
+
throw new Error('TS micro effect must be pure in Phase 1');
|
|
83
|
+
}
|
|
84
|
+
if (!input.deterministic) {
|
|
85
|
+
throw new Error('TS micro must be deterministic in Phase 1');
|
|
86
|
+
}
|
|
87
|
+
if (typeof input.fn !== 'function') {
|
|
88
|
+
throw new Error('micro fn must be a function');
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
name: input.name,
|
|
92
|
+
version: input.version,
|
|
93
|
+
args: input.args,
|
|
94
|
+
returns: input.returns,
|
|
95
|
+
effect: input.effect,
|
|
96
|
+
deterministic: input.deterministic,
|
|
97
|
+
mode: input.mode ?? 'scalar',
|
|
98
|
+
placement: 'host',
|
|
99
|
+
fn: input.fn,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function logicalKey(name, version) {
|
|
103
|
+
return `${name}@${version}`;
|
|
104
|
+
}
|
|
105
|
+
function validateArgTypes(definition, args) {
|
|
106
|
+
for (let index = 0; index < definition.args.length; index += 1) {
|
|
107
|
+
const kind = definition.args[index]?.kind;
|
|
108
|
+
const value = args[index];
|
|
109
|
+
switch (kind) {
|
|
110
|
+
case 'i64':
|
|
111
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
112
|
+
throw new Error(`micro ${definition.name} arg ${index} expects i64`);
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
case 'bool':
|
|
116
|
+
if (typeof value !== 'boolean') {
|
|
117
|
+
throw new Error(`micro ${definition.name} arg ${index} expects bool`);
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
case 'text':
|
|
121
|
+
if (typeof value !== 'string') {
|
|
122
|
+
throw new Error(`micro ${definition.name} arg ${index} expects text`);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case 'null':
|
|
126
|
+
if (value !== null) {
|
|
127
|
+
throw new Error(`micro ${definition.name} arg ${index} expects null`);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
throw new Error(`unsupported micro arg kind: ${kind}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function validateReturnType(descriptor, value) {
|
|
136
|
+
switch (descriptor.kind) {
|
|
137
|
+
case 'i64':
|
|
138
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
139
|
+
throw new Error('micro return expects i64');
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
case 'bool':
|
|
143
|
+
if (typeof value !== 'boolean') {
|
|
144
|
+
throw new Error('micro return expects bool');
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
case 'text':
|
|
148
|
+
if (typeof value !== 'string') {
|
|
149
|
+
throw new Error('micro return expects text');
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
case 'null':
|
|
153
|
+
if (value !== null) {
|
|
154
|
+
throw new Error('micro return expects null');
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
default:
|
|
158
|
+
throw new Error(`unsupported micro return kind: ${descriptor.kind}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface YydbNative {
|
|
2
|
+
version(): string;
|
|
3
|
+
serve(dbPath: string, bind: string, insecureBind?: boolean): void;
|
|
4
|
+
initDb(dbPath: string, schemaVersion: number, schemaDocument?: string | null): void;
|
|
5
|
+
infoText(dbPath: string): string;
|
|
6
|
+
readSchemaFile(schemaPath: string): string;
|
|
7
|
+
}
|
|
8
|
+
/** Load the platform native binding used by the `yydb` CLI. */
|
|
9
|
+
export declare function loadNative(): YydbNative;
|
package/dist/native.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { nativeBindingName, platformKey } from './resolve-bin.js';
|
|
6
|
+
function existsFile(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
fs.accessSync(filePath, fs.constants.F_OK);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function loadFromPath(bindingPath) {
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
return require(bindingPath);
|
|
18
|
+
}
|
|
19
|
+
function monorepoBindingPath(key) {
|
|
20
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const repoRoot = path.resolve(here, '../../..');
|
|
22
|
+
const profile = process.env.YYDB_NATIVE_PROFILE === 'debug' ? 'debug' : 'release';
|
|
23
|
+
const base = path.join(repoRoot, 'target', profile);
|
|
24
|
+
const candidates = process.platform === 'win32'
|
|
25
|
+
? [path.join(base, 'yydb_napi.dll'), path.join(base, 'yydb_napi.node')]
|
|
26
|
+
: [path.join(base, 'libyydb_napi.so'), path.join(base, 'libyydb_napi.dylib'), path.join(base, 'yydb_napi.node')];
|
|
27
|
+
for (const candidate of candidates) {
|
|
28
|
+
if (existsFile(candidate)) {
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const staged = path.join(repoRoot, 'projects', 'packages', `yydb-${key}`, nativeBindingName(key));
|
|
33
|
+
return existsFile(staged) ? staged : null;
|
|
34
|
+
}
|
|
35
|
+
/** Load the platform native binding used by the `yydb` CLI. */
|
|
36
|
+
export function loadNative() {
|
|
37
|
+
const fromEnv = process.env.YYDB_NATIVE?.trim();
|
|
38
|
+
if (fromEnv) {
|
|
39
|
+
if (!existsFile(fromEnv)) {
|
|
40
|
+
throw new Error(`YYDB_NATIVE points to missing binding: ${fromEnv}`);
|
|
41
|
+
}
|
|
42
|
+
return loadFromPath(path.resolve(fromEnv));
|
|
43
|
+
}
|
|
44
|
+
const key = platformKey();
|
|
45
|
+
if (key) {
|
|
46
|
+
const pkg = `@yydb/yydb-${key}`;
|
|
47
|
+
const require = createRequire(import.meta.url);
|
|
48
|
+
try {
|
|
49
|
+
const pkgJson = require.resolve(`${pkg}/package.json`);
|
|
50
|
+
const candidate = path.join(path.dirname(pkgJson), nativeBindingName(key));
|
|
51
|
+
if (existsFile(candidate)) {
|
|
52
|
+
return loadFromPath(candidate);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// optionalDependency not installed for this platform
|
|
57
|
+
}
|
|
58
|
+
const local = monorepoBindingPath(key);
|
|
59
|
+
if (local) {
|
|
60
|
+
return loadFromPath(local);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw new Error('YYDB native binding not found. Install the matching optionalDependency ' +
|
|
64
|
+
`(@yydb/yydb-${key ?? '<platform>'}), set YYDB_NATIVE, or build ` +
|
|
65
|
+
'`cargo build -p yydb-napi --release` in this repo.');
|
|
66
|
+
}
|
package/dist/resolve-bin.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
export type PlatformKey =
|
|
2
|
+
export type PlatformKey = 'win32-x64' | 'linux-x64' | 'darwin-x64' | 'darwin-arm64';
|
|
3
3
|
/** Map Node platform/arch to optionalDependency package suffix. */
|
|
4
4
|
export declare function platformKey(platform?: NodeJS.Platform, arch?: NodeJS.Architecture): PlatformKey | null;
|
|
5
|
+
/** Native binding filename inside each `@yydb/yydb-<platform>` package. */
|
|
6
|
+
export declare function nativeBindingName(key: PlatformKey): string;
|
|
5
7
|
/**
|
|
6
|
-
* Resolve the `yydb`
|
|
7
|
-
*
|
|
8
|
-
|
|
8
|
+
* Resolve the TypeScript `yydb` CLI entry script.
|
|
9
|
+
* Order: `YYDB_CLI` → this package `dist/cli.js`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveYydbCli(): string;
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Use {@link resolveYydbCli}. Kept for callers that still import this name.
|
|
9
14
|
*/
|
|
10
15
|
export declare function resolveYydbBinary(): string;
|
package/dist/resolve-bin.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
const require = createRequire(import.meta.url);
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
6
4
|
/** Map Node platform/arch to optionalDependency package suffix. */
|
|
7
5
|
export function platformKey(platform = process.platform, arch = process.arch) {
|
|
8
|
-
if (platform ===
|
|
9
|
-
return
|
|
10
|
-
if (platform ===
|
|
11
|
-
return
|
|
12
|
-
if (platform ===
|
|
13
|
-
return
|
|
14
|
-
if (platform ===
|
|
15
|
-
return
|
|
6
|
+
if (platform === 'win32' && arch === 'x64')
|
|
7
|
+
return 'win32-x64';
|
|
8
|
+
if (platform === 'linux' && arch === 'x64')
|
|
9
|
+
return 'linux-x64';
|
|
10
|
+
if (platform === 'darwin' && arch === 'x64')
|
|
11
|
+
return 'darwin-x64';
|
|
12
|
+
if (platform === 'darwin' && arch === 'arm64')
|
|
13
|
+
return 'darwin-arm64';
|
|
16
14
|
return null;
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
/** Native binding filename inside each `@yydb/yydb-<platform>` package. */
|
|
17
|
+
export function nativeBindingName(key) {
|
|
18
|
+
return `yydb.${key}.node`;
|
|
20
19
|
}
|
|
21
|
-
function
|
|
20
|
+
function existsFile(filePath) {
|
|
22
21
|
try {
|
|
23
22
|
fs.accessSync(filePath, fs.constants.F_OK);
|
|
24
23
|
return true;
|
|
@@ -28,42 +27,27 @@ function existsExecutable(filePath) {
|
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
/**
|
|
31
|
-
* Resolve the `yydb`
|
|
32
|
-
*
|
|
33
|
-
* cargo `target/{release,debug}` (monorepo).
|
|
30
|
+
* Resolve the TypeScript `yydb` CLI entry script.
|
|
31
|
+
* Order: `YYDB_CLI` → this package `dist/cli.js`.
|
|
34
32
|
*/
|
|
35
|
-
export function
|
|
36
|
-
const fromEnv = process.env.
|
|
33
|
+
export function resolveYydbCli() {
|
|
34
|
+
const fromEnv = process.env.YYDB_CLI?.trim();
|
|
37
35
|
if (fromEnv) {
|
|
38
|
-
if (!
|
|
39
|
-
throw new Error(`
|
|
36
|
+
if (!existsFile(fromEnv)) {
|
|
37
|
+
throw new Error(`YYDB_CLI points to missing script: ${fromEnv}`);
|
|
40
38
|
}
|
|
41
39
|
return path.resolve(fromEnv);
|
|
42
40
|
}
|
|
43
|
-
const key = platformKey();
|
|
44
|
-
if (key) {
|
|
45
|
-
const pkg = `@yydb/yydb-${key}`;
|
|
46
|
-
const name = binaryName();
|
|
47
|
-
try {
|
|
48
|
-
const pkgJson = require.resolve(`${pkg}/package.json`);
|
|
49
|
-
const candidate = path.join(path.dirname(pkgJson), name);
|
|
50
|
-
if (existsExecutable(candidate)) {
|
|
51
|
-
return candidate;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
// optionalDependency not installed for this platform
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
41
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (existsExecutable(candidate)) {
|
|
63
|
-
return candidate;
|
|
64
|
-
}
|
|
42
|
+
const candidate = path.join(here, 'cli.js');
|
|
43
|
+
if (!existsFile(candidate)) {
|
|
44
|
+
throw new Error('YYDB CLI script not found. Build `@yydb/yydb` (`pnpm --filter @yydb/yydb build`) or set YYDB_CLI.');
|
|
65
45
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated Use {@link resolveYydbCli}. Kept for callers that still import this name.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveYydbBinary() {
|
|
52
|
+
return resolveYydbCli();
|
|
69
53
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Phase-1 scalar UDF wire kinds aligned with `yydb-udf`. */
|
|
2
|
+
export type UdfScalarKind = 'i64' | 'bool' | 'text' | 'null';
|
|
3
|
+
/** Runtime type descriptor used in `defineMicro` contracts. */
|
|
4
|
+
export interface UdfTypeDescriptor<T = unknown> {
|
|
5
|
+
readonly kind: UdfScalarKind;
|
|
6
|
+
/** Phantom type for author-side inference only. */
|
|
7
|
+
readonly _type?: T;
|
|
8
|
+
}
|
|
9
|
+
/** `i64` argument or return descriptor. */
|
|
10
|
+
export declare function i64(): UdfTypeDescriptor<number>;
|
|
11
|
+
/** `bool` argument or return descriptor. */
|
|
12
|
+
export declare function bool(): UdfTypeDescriptor<boolean>;
|
|
13
|
+
/** `text` argument or return descriptor. */
|
|
14
|
+
export declare function text(): UdfTypeDescriptor<string>;
|
|
15
|
+
/** `null` return descriptor. */
|
|
16
|
+
export declare function nullType(): UdfTypeDescriptor<null>;
|
|
17
|
+
/** Maps descriptor tuple types to positional argument types. */
|
|
18
|
+
export type InferUdfArgs<T extends readonly UdfTypeDescriptor[]> = {
|
|
19
|
+
[K in keyof T]: T[K] extends UdfTypeDescriptor<infer V> ? V : never;
|
|
20
|
+
};
|
|
21
|
+
/** Maps a return descriptor to its value type. */
|
|
22
|
+
export type InferUdfReturn<T extends UdfTypeDescriptor> = T extends UdfTypeDescriptor<infer V> ? V : never;
|
|
23
|
+
/** Session micro effect surface (Phase 1: pure only). */
|
|
24
|
+
export type MicroEffect = 'pure';
|
|
25
|
+
/** Invocation mode stored with the definition for future batch host calls. */
|
|
26
|
+
export type MicroInvocationMode = 'scalar' | 'batch';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** `i64` argument or return descriptor. */
|
|
2
|
+
export function i64() {
|
|
3
|
+
return { kind: 'i64' };
|
|
4
|
+
}
|
|
5
|
+
/** `bool` argument or return descriptor. */
|
|
6
|
+
export function bool() {
|
|
7
|
+
return { kind: 'bool' };
|
|
8
|
+
}
|
|
9
|
+
/** `text` argument or return descriptor. */
|
|
10
|
+
export function text() {
|
|
11
|
+
return { kind: 'text' };
|
|
12
|
+
}
|
|
13
|
+
/** `null` return descriptor. */
|
|
14
|
+
export function nullType() {
|
|
15
|
+
return { kind: 'null' };
|
|
16
|
+
}
|