@yydb/yydb 0.0.3 → 0.0.4
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 +27 -42
- package/dist/cli/cli.js +0 -2
- package/package.json +6 -10
- package/dist/cli/generate-cmd.d.ts +0 -3
- package/dist/cli/generate-cmd.js +0 -25
- package/dist/generator/emit-typescript.d.ts +0 -3
- package/dist/generator/emit-typescript.js +0 -26
- package/dist/generator/fixtures/setting.generated.d.ts +0 -13
- package/dist/generator/fixtures/setting.generated.js +0 -3
- package/dist/generator/index.d.ts +0 -6
- package/dist/generator/index.js +0 -10
- package/dist/generator/ir.d.ts +0 -20
- package/dist/generator/ir.js +0 -12
- package/dist/generator/parse-vos.d.ts +0 -6
- package/dist/generator/parse-vos.js +0 -149
package/README.md
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
# `@yydb/yydb`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
wire protocol. For a **lightweight client only** (browser, renderer, or an app that already runs `yydb serve`), use
|
|
7
|
-
[`@yydb/yydb-client`](../yydb-client).
|
|
3
|
+
Opens a `.yydb` file in Node.js with one dependency: a private loopback `yydb serve` process starts automatically and
|
|
4
|
+
the package talks to it over the YY wire protocol. For a lightweight client only (browser, renderer, or an app that
|
|
5
|
+
already runs `yydb serve`), use [`@yydb/yydb-client`](https://www.npmjs.com/package/@yydb/yydb-client).
|
|
8
6
|
|
|
9
7
|
[](https://www.npmjs.com/package/@yydb/yydb)
|
|
10
8
|
[](https://www.npmjs.com/package/@yydb/yydb)
|
|
11
|
-
[](https://github.com/yy-database/yydb/actions/workflows/check-rust.yml)
|
|
12
|
-
[](https://github.com/yy-database/yydb/actions/workflows/check-typescript.yml)
|
|
13
9
|
|
|
14
|
-
##
|
|
10
|
+
## Exports
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
| Import | Role |
|
|
13
|
+
|------------------|------------------------------------------|
|
|
14
|
+
| `@yydb/yydb` | `Database` API and default entry |
|
|
15
|
+
| `@yydb/yydb/cli` | CLI (`version`, `init`, `info`, `serve`) |
|
|
16
|
+
|
|
17
|
+
Node.js 18+. The matching platform `.node` binding installs via optional dependencies.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
is
|
|
22
|
-
started on a private loopback endpoint when `Database.open()` runs.
|
|
19
|
+
Schema truth lives in the `.yydb` file via `ensureSchema` and VOS documents.
|
|
23
20
|
|
|
24
21
|
## Example
|
|
25
22
|
|
|
@@ -38,19 +35,16 @@ await db.ensureSchema(
|
|
|
38
35
|
);
|
|
39
36
|
|
|
40
37
|
await db.put("theme", "dark");
|
|
41
|
-
|
|
42
|
-
console.log(value && new TextDecoder().decode(value));
|
|
43
|
-
|
|
38
|
+
console.log(await db.get("theme"));
|
|
44
39
|
await db.close();
|
|
45
40
|
```
|
|
46
41
|
|
|
47
|
-
The
|
|
48
|
-
the file does not depend on a separate schema registry.
|
|
42
|
+
The file is created when missing. Schema version and VOS document persist with the data.
|
|
49
43
|
|
|
50
44
|
## API
|
|
51
45
|
|
|
52
46
|
```ts
|
|
53
|
-
const db = await Database.open(path, options
|
|
47
|
+
const db = await Database.open(path, options?);
|
|
54
48
|
|
|
55
49
|
await db.ensureSchema(version, vosDocument);
|
|
56
50
|
await db.getSchema();
|
|
@@ -61,30 +55,21 @@ await db.serverVersion();
|
|
|
61
55
|
await db.close();
|
|
62
56
|
```
|
|
63
57
|
|
|
64
|
-
`OpenOptions
|
|
65
|
-
arguments
|
|
66
|
-
|
|
67
|
-
## Use a different package when
|
|
68
|
-
|
|
69
|
-
| Need | Package |
|
|
70
|
-
|----------------------------------------|-------------------------------------------|
|
|
71
|
-
| Node all-in-one (CLI + engine + serve) | **`@yydb/yydb`** (this package) |
|
|
72
|
-
| Lightweight wire client only | [`@yydb/yydb-client`](../yydb-client) |
|
|
73
|
-
| In-process Rust embed | [`yydb`](https://crates.io/crates/yydb) |
|
|
74
|
-
| Rust wire server crate (used by N-API) | [`yydb-server`](../../crates/yydb-server) |
|
|
75
|
-
| Rust remote client | [`yydb-client`](../../crates/yydb-client) |
|
|
58
|
+
`OpenOptions`: `cli` (explicit CLI script path), `port` (preferred loopback port), `serveArgs` (extra `yydb serve`
|
|
59
|
+
arguments). `binary` is a deprecated alias for `cli`.
|
|
76
60
|
|
|
77
|
-
|
|
78
|
-
`@yydb/yydb-client` against the main process endpoint.
|
|
61
|
+
## Related packages
|
|
79
62
|
|
|
80
|
-
|
|
63
|
+
| Need | Package |
|
|
64
|
+
|--------------------------------|------------------------------------------------------------------------|
|
|
65
|
+
| Node all-in-one (this package) | **`@yydb/yydb`** |
|
|
66
|
+
| Wire client only | [`@yydb/yydb-client`](https://www.npmjs.com/package/@yydb/yydb-client) |
|
|
67
|
+
| In-process Rust embed | [`yydb`](https://crates.io/crates/yydb) |
|
|
68
|
+
| Agent skills (wire protocol) | [`@yydb/yydb-skills`](https://www.npmjs.com/package/@yydb/yydb-skills) |
|
|
81
69
|
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
Electron: main process can use `@yydb/yydb`; renderer code can use `@yydb/yydb-client` against the main-process
|
|
71
|
+
endpoint.
|
|
84
72
|
|
|
85
|
-
|
|
73
|
+
The managed engine listens on `127.0.0.1` with no authentication — keep the endpoint private.
|
|
86
74
|
|
|
87
|
-
|
|
88
|
-
- [User guides](https://github.com/yy-database/yydb.rs/tree/dev/projects/packages/homepage/documentation/zh-hans)
|
|
89
|
-
- [API documentation](https://docs.rs/yydb)
|
|
90
|
-
- [License](https://github.com/yy-database/yydb.rs/blob/dev/License.md)
|
|
75
|
+
[YYDB overview](https://github.com/yy-database/yydb.rs) · [User guides](https://github.com/yy-database/yydb.rs/tree/dev/projects/packages/homepage/documentation/zh-hans)
|
package/dist/cli/cli.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { dirname, join } from 'node:path';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { createCli } from '@vmz/commander';
|
|
4
|
-
import { registerGenerateCommand } from './generate-cmd.js';
|
|
5
4
|
import { registerInfoCommand } from './info-cmd.js';
|
|
6
5
|
import { registerInitCommand } from './init-cmd.js';
|
|
7
6
|
import { registerServeCommand } from './serve-cmd.js';
|
|
@@ -13,7 +12,6 @@ function buildYydbCli() {
|
|
|
13
12
|
registerInitCommand(cli);
|
|
14
13
|
registerInfoCommand(cli);
|
|
15
14
|
registerServeCommand(cli);
|
|
16
|
-
registerGenerateCommand(cli);
|
|
17
15
|
return cli;
|
|
18
16
|
}
|
|
19
17
|
export async function runCli(argv) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yydb/yydb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "YYDB all-in-one for Node: embedded engine, CLI, and local serve",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -17,10 +17,6 @@
|
|
|
17
17
|
"./cli": {
|
|
18
18
|
"types": "./dist/cli.d.ts",
|
|
19
19
|
"import": "./dist/cli.js"
|
|
20
|
-
},
|
|
21
|
-
"./generator": {
|
|
22
|
-
"types": "./dist/generator/index.d.ts",
|
|
23
|
-
"import": "./dist/generator/index.js"
|
|
24
20
|
}
|
|
25
21
|
},
|
|
26
22
|
"files": [
|
|
@@ -52,7 +48,7 @@
|
|
|
52
48
|
},
|
|
53
49
|
"dependencies": {
|
|
54
50
|
"@vmz/commander": "^0.2.0",
|
|
55
|
-
"@yydb/yydb-client": "0.0.
|
|
51
|
+
"@yydb/yydb-client": "0.0.4"
|
|
56
52
|
},
|
|
57
53
|
"devDependencies": {
|
|
58
54
|
"@types/node": "^20.10.0",
|
|
@@ -62,9 +58,9 @@
|
|
|
62
58
|
"node": ">=18"
|
|
63
59
|
},
|
|
64
60
|
"optionalDependencies": {
|
|
65
|
-
"@yydb/yydb-win32-x64": "0.0.
|
|
66
|
-
"@yydb/yydb-linux-x64": "0.0.
|
|
67
|
-
"@yydb/yydb-darwin-x64": "0.0.
|
|
68
|
-
"@yydb/yydb-darwin-arm64": "0.0.
|
|
61
|
+
"@yydb/yydb-win32-x64": "0.0.4",
|
|
62
|
+
"@yydb/yydb-linux-x64": "0.0.4",
|
|
63
|
+
"@yydb/yydb-darwin-x64": "0.0.4",
|
|
64
|
+
"@yydb/yydb-darwin-arm64": "0.0.4"
|
|
69
65
|
}
|
|
70
66
|
}
|
package/dist/cli/generate-cmd.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { generateTypeScript } from '../generator/index.js';
|
|
4
|
-
import { requiredPositional, str } from './options.js';
|
|
5
|
-
export function registerGenerateCommand(cli) {
|
|
6
|
-
cli.command('generate', 'cli.cmd.generate')
|
|
7
|
-
.option('-o, --output <path>', 'cli.opt.output')
|
|
8
|
-
.option('--schema-version <n>', 'cli.opt.schema-version')
|
|
9
|
-
.action((options) => cmdGenerate(options));
|
|
10
|
-
}
|
|
11
|
-
export function cmdGenerate(options) {
|
|
12
|
-
const input = requiredPositional(options, 'generate');
|
|
13
|
-
const output = str(options, 'output');
|
|
14
|
-
if (!output) {
|
|
15
|
-
throw new Error('generate requires --output <path>');
|
|
16
|
-
}
|
|
17
|
-
const schemaVersion = Number.parseInt(str(options, 'schema-version') ?? '1', 10);
|
|
18
|
-
const source = fs.readFileSync(input, 'utf8');
|
|
19
|
-
const ts = generateTypeScript(source, schemaVersion);
|
|
20
|
-
const outPath = path.resolve(output);
|
|
21
|
-
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
22
|
-
fs.writeFileSync(outPath, ts);
|
|
23
|
-
console.log(`wrote ${outPath}`);
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { scalarToTs } from './ir.js';
|
|
2
|
-
const BANNER = '// @generated by `yydb generate`, do not edit!';
|
|
3
|
-
/** Generate `schema.generated.ts` source from IR. */
|
|
4
|
-
export function emitTypeScript(ir) {
|
|
5
|
-
const lines = [BANNER];
|
|
6
|
-
lines.push(`export const APP_SCHEMA_VERSION = ${ir.schemaVersion};`);
|
|
7
|
-
lines.push(`export const APP_SCHEMA = ${JSON.stringify(ir.vosSource)};`);
|
|
8
|
-
for (const table of ir.tables) {
|
|
9
|
-
lines.push(emitInterfaceRow(table));
|
|
10
|
-
}
|
|
11
|
-
lines.push(emitInterfaceAppDb(ir));
|
|
12
|
-
lines.push(`export type AppDbTableName = keyof AppDb["tables"];`);
|
|
13
|
-
return `${lines.join('\n')}\n`;
|
|
14
|
-
}
|
|
15
|
-
function emitInterfaceRow(table) {
|
|
16
|
-
const lines = [`export interface ${table.name}Row {`];
|
|
17
|
-
for (const field of table.fields) {
|
|
18
|
-
lines.push(` ${field.name}: ${scalarToTs(field.ty)};`);
|
|
19
|
-
}
|
|
20
|
-
lines.push('}');
|
|
21
|
-
return lines.join('\n');
|
|
22
|
-
}
|
|
23
|
-
function emitInterfaceAppDb(ir) {
|
|
24
|
-
const tableLines = ir.tables.map((table) => ` ${table.name}: ${table.name}Row;`);
|
|
25
|
-
return ['export interface AppDb {', ' tables: {', ...tableLines, ' };', '}'].join('\n');
|
|
26
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare const APP_SCHEMA_VERSION = 1;
|
|
2
|
-
export declare const APP_SCHEMA = "table Setting {\n @@id: uuid,\n key: utf8,\n value: utf8,\n}\n";
|
|
3
|
-
export interface SettingRow {
|
|
4
|
-
id: string;
|
|
5
|
-
key: string;
|
|
6
|
-
value: string;
|
|
7
|
-
}
|
|
8
|
-
export interface AppDb {
|
|
9
|
-
tables: {
|
|
10
|
-
Setting: SettingRow;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
export type AppDbTableName = keyof AppDb["tables"];
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export type { SchemaField, SchemaTable, ScalarType, TsSchemaIr } from './ir.js';
|
|
2
|
-
export { emitTypeScript } from './emit-typescript.js';
|
|
3
|
-
export { parseVosSubset, VosParseError } from './parse-vos.js';
|
|
4
|
-
/** Generate TypeScript from a VOS document. */
|
|
5
|
-
export declare function generateTypeScript(vosSource: string, schemaVersion: number): string;
|
|
6
|
-
export { generateTypeScript as generateTypescript };
|
package/dist/generator/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { emitTypeScript } from './emit-typescript.js';
|
|
2
|
-
import { parseVosSubset } from './parse-vos.js';
|
|
3
|
-
export { emitTypeScript } from './emit-typescript.js';
|
|
4
|
-
export { parseVosSubset, VosParseError } from './parse-vos.js';
|
|
5
|
-
/** Generate TypeScript from a VOS document. */
|
|
6
|
-
export function generateTypeScript(vosSource, schemaVersion) {
|
|
7
|
-
const ir = parseVosSubset(vosSource, schemaVersion);
|
|
8
|
-
return emitTypeScript(ir);
|
|
9
|
-
}
|
|
10
|
-
export { generateTypeScript as generateTypescript };
|
package/dist/generator/ir.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** Scalar types supported by the MVP emitter (expand with `vos-parser`). */
|
|
2
|
-
export type ScalarType = 'uuid' | 'utf8' | 'bool' | 'i64' | 'f64';
|
|
3
|
-
export interface SchemaField {
|
|
4
|
-
readonly name: string;
|
|
5
|
-
readonly ty: ScalarType;
|
|
6
|
-
/** True when this field is declared via `@@id`. */
|
|
7
|
-
readonly isId: boolean;
|
|
8
|
-
}
|
|
9
|
-
export interface SchemaTable {
|
|
10
|
-
readonly name: string;
|
|
11
|
-
readonly fields: readonly SchemaField[];
|
|
12
|
-
}
|
|
13
|
-
/** One schema document ready for TypeScript emission. */
|
|
14
|
-
export interface TsSchemaIr {
|
|
15
|
-
/** Original VOS source (database truth embedded into generated module). */
|
|
16
|
-
readonly vosSource: string;
|
|
17
|
-
readonly schemaVersion: number;
|
|
18
|
-
readonly tables: readonly SchemaTable[];
|
|
19
|
-
}
|
|
20
|
-
export declare function scalarToTs(ty: ScalarType): string;
|
package/dist/generator/ir.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { TsSchemaIr } from './ir.js';
|
|
2
|
-
export declare class VosParseError extends Error {
|
|
3
|
-
constructor(message: string);
|
|
4
|
-
}
|
|
5
|
-
/** Interim VOS subset parser for `yydb generate` (MVP). */
|
|
6
|
-
export declare function parseVosSubset(source: string, schemaVersion: number): TsSchemaIr;
|
|
@@ -1,149 +0,0 @@
|
|
|
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
|
-
}
|