@divizend/scratch-core 1.0.0
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/basic/demo.ts +11 -0
- package/basic/index.ts +490 -0
- package/core/Auth.ts +63 -0
- package/core/Currency.ts +16 -0
- package/core/Env.ts +186 -0
- package/core/Fragment.ts +43 -0
- package/core/FragmentServingMode.ts +37 -0
- package/core/JsonSchemaValidator.ts +173 -0
- package/core/ProjectRoot.ts +76 -0
- package/core/Scratch.ts +44 -0
- package/core/URI.ts +203 -0
- package/core/Universe.ts +406 -0
- package/core/index.ts +27 -0
- package/gsuite/core/GSuite.ts +237 -0
- package/gsuite/core/GSuiteAdmin.ts +81 -0
- package/gsuite/core/GSuiteOrgConfig.ts +47 -0
- package/gsuite/core/GSuiteUser.ts +115 -0
- package/gsuite/core/index.ts +21 -0
- package/gsuite/documents/Document.ts +173 -0
- package/gsuite/documents/Documents.ts +52 -0
- package/gsuite/documents/index.ts +19 -0
- package/gsuite/drive/Drive.ts +118 -0
- package/gsuite/drive/DriveFile.ts +147 -0
- package/gsuite/drive/index.ts +19 -0
- package/gsuite/gmail/Gmail.ts +430 -0
- package/gsuite/gmail/GmailLabel.ts +55 -0
- package/gsuite/gmail/GmailMessage.ts +428 -0
- package/gsuite/gmail/GmailMessagePart.ts +298 -0
- package/gsuite/gmail/GmailThread.ts +97 -0
- package/gsuite/gmail/index.ts +5 -0
- package/gsuite/gmail/utils.ts +184 -0
- package/gsuite/index.ts +28 -0
- package/gsuite/spreadsheets/CellValue.ts +71 -0
- package/gsuite/spreadsheets/Sheet.ts +128 -0
- package/gsuite/spreadsheets/SheetValues.ts +12 -0
- package/gsuite/spreadsheets/Spreadsheet.ts +76 -0
- package/gsuite/spreadsheets/Spreadsheets.ts +52 -0
- package/gsuite/spreadsheets/index.ts +25 -0
- package/gsuite/spreadsheets/utils.ts +52 -0
- package/gsuite/utils.ts +104 -0
- package/http-server/HttpServer.ts +110 -0
- package/http-server/NativeHttpServer.ts +1084 -0
- package/http-server/index.ts +3 -0
- package/http-server/middlewares/01-cors.ts +33 -0
- package/http-server/middlewares/02-static.ts +67 -0
- package/http-server/middlewares/03-request-logger.ts +159 -0
- package/http-server/middlewares/04-body-parser.ts +54 -0
- package/http-server/middlewares/05-no-cache.ts +23 -0
- package/http-server/middlewares/06-response-handler.ts +39 -0
- package/http-server/middlewares/handler-wrapper.ts +250 -0
- package/http-server/middlewares/index.ts +37 -0
- package/http-server/middlewares/types.ts +27 -0
- package/index.ts +24 -0
- package/package.json +37 -0
- package/queue/EmailQueue.ts +228 -0
- package/queue/RateLimiter.ts +54 -0
- package/queue/index.ts +2 -0
- package/resend/Resend.ts +190 -0
- package/resend/index.ts +11 -0
- package/s2/S2.ts +335 -0
- package/s2/index.ts +11 -0
package/basic/demo.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the default BASIC demo code
|
|
3
|
+
*/
|
|
4
|
+
export const DEFAULT_BASIC_DEMO = `# count from [start] to [end] in [stream]
|
|
5
|
+
# {"start":{"type":"number"},"end":{"type":"number"}}
|
|
6
|
+
x = $.inputs.start
|
|
7
|
+
while x <= $.inputs.end
|
|
8
|
+
call appendToStream $.inputs.stream {"count": x}
|
|
9
|
+
x = x + 1
|
|
10
|
+
end
|
|
11
|
+
return x`;
|
package/basic/index.ts
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BASIC Compiler Module
|
|
3
|
+
* Compiles BASIC code to TypeScript endpoint definitions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface SchemaDefinition {
|
|
7
|
+
[key: string]: {
|
|
8
|
+
type: "string" | "number" | "boolean" | "array" | "object" | "json";
|
|
9
|
+
default?: any;
|
|
10
|
+
description?: string;
|
|
11
|
+
schema?: any;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parses BASIC values to TypeScript expressions
|
|
18
|
+
* Passes through expressions 1:1 without parsing
|
|
19
|
+
*/
|
|
20
|
+
class ValueParser {
|
|
21
|
+
parse(value: string): string {
|
|
22
|
+
// Pass through the value as-is, character by character
|
|
23
|
+
return value.trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parses BASIC conditions to TypeScript conditions
|
|
29
|
+
* Passes through conditions 1:1 without parsing
|
|
30
|
+
*/
|
|
31
|
+
class ConditionParser {
|
|
32
|
+
parse(condition: string): string {
|
|
33
|
+
// Pass through the condition as-is, character by character
|
|
34
|
+
return condition.trim();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parses call arguments from BASIC call statements
|
|
40
|
+
*/
|
|
41
|
+
class CallArgsParser {
|
|
42
|
+
parse(callExpr: string): string[] {
|
|
43
|
+
const args: string[] = [];
|
|
44
|
+
let current = "";
|
|
45
|
+
let inString = false;
|
|
46
|
+
let stringChar = "";
|
|
47
|
+
let depth = 0;
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < callExpr.length; i++) {
|
|
50
|
+
const char = callExpr[i];
|
|
51
|
+
|
|
52
|
+
if (!inString) {
|
|
53
|
+
if (char === '"' || char === "'") {
|
|
54
|
+
inString = true;
|
|
55
|
+
stringChar = char;
|
|
56
|
+
current += char;
|
|
57
|
+
} else if (char === " " && depth === 0) {
|
|
58
|
+
if (current.trim()) {
|
|
59
|
+
args.push(current.trim());
|
|
60
|
+
current = "";
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (char === "{" || char === "[") depth++;
|
|
64
|
+
if (char === "}" || char === "]") depth--;
|
|
65
|
+
current += char;
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
current += char;
|
|
69
|
+
if (char === stringChar && callExpr[i - 1] !== "\\") {
|
|
70
|
+
inString = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (current.trim()) {
|
|
76
|
+
args.push(current.trim());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return args;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generates schema from text and optional schema definition
|
|
85
|
+
*/
|
|
86
|
+
class SchemaGenerator {
|
|
87
|
+
/**
|
|
88
|
+
* Extracts parameter names from text (e.g., "do something [param1] with [param2]")
|
|
89
|
+
*/
|
|
90
|
+
private extractParameterNames(text: string): string[] {
|
|
91
|
+
const matches = text.match(/\[([^\]]+)\]/g);
|
|
92
|
+
if (!matches) return [];
|
|
93
|
+
return matches.map((match) => match.slice(1, -1));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Generates default schema from parameter names
|
|
98
|
+
*/
|
|
99
|
+
private generateDefaultSchema(parameterNames: string[]): SchemaDefinition {
|
|
100
|
+
const schema: SchemaDefinition = {};
|
|
101
|
+
for (const paramName of parameterNames) {
|
|
102
|
+
schema[paramName] = {
|
|
103
|
+
type: "string",
|
|
104
|
+
default: `[${paramName}]`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return schema;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Parses schema from comment line (JSON format)
|
|
112
|
+
*/
|
|
113
|
+
private parseSchemaComment(commentLine: string): SchemaDefinition | null {
|
|
114
|
+
try {
|
|
115
|
+
// Remove # and trim
|
|
116
|
+
const jsonStr = commentLine.replace(/^#\s*/, "").trim();
|
|
117
|
+
if (!jsonStr) return null;
|
|
118
|
+
return JSON.parse(jsonStr);
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Merges default schema with optional schema
|
|
126
|
+
*/
|
|
127
|
+
private mergeSchemas(
|
|
128
|
+
defaultSchema: SchemaDefinition,
|
|
129
|
+
optionalSchema: SchemaDefinition | null
|
|
130
|
+
): SchemaDefinition {
|
|
131
|
+
if (!optionalSchema) return defaultSchema;
|
|
132
|
+
|
|
133
|
+
const merged = { ...defaultSchema };
|
|
134
|
+
for (const [key, value] of Object.entries(optionalSchema)) {
|
|
135
|
+
merged[key] = { ...defaultSchema[key], ...value };
|
|
136
|
+
}
|
|
137
|
+
return merged;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Generates schema from text and optional schema comment
|
|
142
|
+
*/
|
|
143
|
+
generate(text: string, schemaComment?: string): SchemaDefinition {
|
|
144
|
+
const parameterNames = this.extractParameterNames(text);
|
|
145
|
+
const defaultSchema = this.generateDefaultSchema(parameterNames);
|
|
146
|
+
const optionalSchema = schemaComment
|
|
147
|
+
? this.parseSchemaComment(schemaComment)
|
|
148
|
+
: null;
|
|
149
|
+
return this.mergeSchemas(defaultSchema, optionalSchema);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Parses BASIC statements and converts them to TypeScript
|
|
155
|
+
*/
|
|
156
|
+
class BasicParser {
|
|
157
|
+
private valueParser: ValueParser;
|
|
158
|
+
private conditionParser: ConditionParser;
|
|
159
|
+
private callArgsParser: CallArgsParser;
|
|
160
|
+
private statements: string[] = [];
|
|
161
|
+
private indent: number = 4;
|
|
162
|
+
private hasReturn: boolean = false;
|
|
163
|
+
private variables: Set<string> = new Set();
|
|
164
|
+
|
|
165
|
+
constructor() {
|
|
166
|
+
this.valueParser = new ValueParser();
|
|
167
|
+
this.conditionParser = new ConditionParser();
|
|
168
|
+
this.callArgsParser = new CallArgsParser();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
parse(lines: string[]): string {
|
|
172
|
+
this.statements = [];
|
|
173
|
+
this.indent = 4;
|
|
174
|
+
this.hasReturn = false;
|
|
175
|
+
this.variables.clear();
|
|
176
|
+
|
|
177
|
+
for (const line of lines) {
|
|
178
|
+
this.parseLine(line.trim());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Add default return if none provided
|
|
182
|
+
if (!this.hasReturn) {
|
|
183
|
+
this.statements.push(
|
|
184
|
+
`${" ".repeat(this.indent)}return { success: true };`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const handlerBody =
|
|
189
|
+
this.statements.length > 0
|
|
190
|
+
? this.statements.join("\n")
|
|
191
|
+
: " return { success: true };";
|
|
192
|
+
|
|
193
|
+
// Replace $ with context (but not inside strings)
|
|
194
|
+
return this.replaceDollarSignWithContext(handlerBody);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Replaces $ with context in the generated code, but preserves $ inside strings
|
|
199
|
+
*/
|
|
200
|
+
private replaceDollarSignWithContext(code: string): string {
|
|
201
|
+
let result = "";
|
|
202
|
+
let inString = false;
|
|
203
|
+
let stringChar = "";
|
|
204
|
+
let i = 0;
|
|
205
|
+
|
|
206
|
+
while (i < code.length) {
|
|
207
|
+
const char = code[i];
|
|
208
|
+
const prevChar = i > 0 ? code[i - 1] : "";
|
|
209
|
+
|
|
210
|
+
if (!inString) {
|
|
211
|
+
if (char === '"' || char === "'") {
|
|
212
|
+
inString = true;
|
|
213
|
+
stringChar = char;
|
|
214
|
+
result += char;
|
|
215
|
+
i++;
|
|
216
|
+
} else if (char === "$") {
|
|
217
|
+
// Check what follows $
|
|
218
|
+
const nextChar = i + 1 < code.length ? code[i + 1] : "";
|
|
219
|
+
|
|
220
|
+
if (nextChar === ".") {
|
|
221
|
+
// $.property -> context.property
|
|
222
|
+
result += "context";
|
|
223
|
+
i++;
|
|
224
|
+
} else if (/[a-zA-Z_$]/.test(nextChar)) {
|
|
225
|
+
// $property -> context.property
|
|
226
|
+
result += "context.";
|
|
227
|
+
i++;
|
|
228
|
+
} else {
|
|
229
|
+
// $ alone or $ followed by non-identifier -> context
|
|
230
|
+
result += "context";
|
|
231
|
+
i++;
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
result += char;
|
|
235
|
+
i++;
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
result += char;
|
|
239
|
+
if (char === stringChar && prevChar !== "\\") {
|
|
240
|
+
inString = false;
|
|
241
|
+
}
|
|
242
|
+
i++;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private parseLine(trimmed: string): void {
|
|
250
|
+
// Variable assignment: x = value
|
|
251
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*/.test(trimmed)) {
|
|
252
|
+
this.parseAssignment(trimmed);
|
|
253
|
+
}
|
|
254
|
+
// While loop: while condition
|
|
255
|
+
else if (trimmed.startsWith("while ")) {
|
|
256
|
+
this.parseWhile(trimmed);
|
|
257
|
+
}
|
|
258
|
+
// End (closes while/if)
|
|
259
|
+
else if (trimmed === "end") {
|
|
260
|
+
this.parseEnd();
|
|
261
|
+
}
|
|
262
|
+
// If statement: if condition
|
|
263
|
+
else if (trimmed.startsWith("if ")) {
|
|
264
|
+
this.parseIf(trimmed);
|
|
265
|
+
}
|
|
266
|
+
// Else
|
|
267
|
+
else if (trimmed === "else") {
|
|
268
|
+
this.parseElse();
|
|
269
|
+
}
|
|
270
|
+
// Return statement: return value
|
|
271
|
+
else if (trimmed.startsWith("return ")) {
|
|
272
|
+
this.parseReturn(trimmed);
|
|
273
|
+
}
|
|
274
|
+
// Endpoint call: call endpointName arg1 arg2 ...
|
|
275
|
+
else if (trimmed.startsWith("call ")) {
|
|
276
|
+
this.parseCall(trimmed);
|
|
277
|
+
}
|
|
278
|
+
// Unknown
|
|
279
|
+
else {
|
|
280
|
+
throw new Error(`Unknown statement: ${trimmed}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private parseAssignment(trimmed: string): void {
|
|
285
|
+
const match = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
|
|
286
|
+
if (match) {
|
|
287
|
+
const varName = match[1];
|
|
288
|
+
const value = match[2];
|
|
289
|
+
this.variables.add(varName);
|
|
290
|
+
const tsValue = this.valueParser.parse(value);
|
|
291
|
+
const isFirstUse = !this.statements.some((s) =>
|
|
292
|
+
s.includes(`${varName} =`)
|
|
293
|
+
);
|
|
294
|
+
const decl = isFirstUse ? "let " : "";
|
|
295
|
+
this.statements.push(
|
|
296
|
+
`${" ".repeat(this.indent)}${decl}${varName} = ${tsValue};`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private parseWhile(trimmed: string): void {
|
|
302
|
+
const condition = trimmed.substring(6).trim();
|
|
303
|
+
const tsCondition = this.conditionParser.parse(condition);
|
|
304
|
+
this.statements.push(`${" ".repeat(this.indent)}while (${tsCondition}) {`);
|
|
305
|
+
this.indent += 2;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private parseEnd(): void {
|
|
309
|
+
this.indent = Math.max(4, this.indent - 2);
|
|
310
|
+
this.statements.push(`${" ".repeat(this.indent)}}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private parseIf(trimmed: string): void {
|
|
314
|
+
const condition = trimmed.substring(3).trim();
|
|
315
|
+
const tsCondition = this.conditionParser.parse(condition);
|
|
316
|
+
this.statements.push(`${" ".repeat(this.indent)}if (${tsCondition}) {`);
|
|
317
|
+
this.indent += 2;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private parseElse(): void {
|
|
321
|
+
this.indent = Math.max(4, this.indent - 2);
|
|
322
|
+
this.statements.push(`${" ".repeat(this.indent)}} else {`);
|
|
323
|
+
this.indent += 2;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private parseReturn(trimmed: string): void {
|
|
327
|
+
const value = trimmed.substring(7).trim();
|
|
328
|
+
const tsValue = this.valueParser.parse(value);
|
|
329
|
+
this.statements.push(`${" ".repeat(this.indent)}return ${tsValue};`);
|
|
330
|
+
this.hasReturn = true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private parseCall(trimmed: string): void {
|
|
334
|
+
const callExpr = trimmed.substring(5).trim();
|
|
335
|
+
const parts = this.callArgsParser.parse(callExpr);
|
|
336
|
+
if (parts.length === 0) {
|
|
337
|
+
throw new Error(`Invalid call statement: ${trimmed}`);
|
|
338
|
+
}
|
|
339
|
+
const endpointName = parts[0];
|
|
340
|
+
const args = parts.slice(1);
|
|
341
|
+
|
|
342
|
+
// Parse arguments
|
|
343
|
+
const parsedArgs = args.map((arg) => this.valueParser.parse(arg));
|
|
344
|
+
const argsStr = parsedArgs.join(", ");
|
|
345
|
+
|
|
346
|
+
// Use Universe.call() method for clean endpoint calling
|
|
347
|
+
this.statements.push(
|
|
348
|
+
`${" ".repeat(
|
|
349
|
+
this.indent
|
|
350
|
+
)}context.result = await context.universe!.call(context, "${endpointName}"${argsStr ? `, ${argsStr}` : ""});`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Generates TypeScript code from parsed BASIC
|
|
357
|
+
*/
|
|
358
|
+
class TypeScriptGenerator {
|
|
359
|
+
private schemaGenerator: SchemaGenerator;
|
|
360
|
+
|
|
361
|
+
constructor() {
|
|
362
|
+
this.schemaGenerator = new SchemaGenerator();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
generate(
|
|
366
|
+
opcode: string,
|
|
367
|
+
text: string,
|
|
368
|
+
schemaComment: string | undefined,
|
|
369
|
+
handlerBody: string,
|
|
370
|
+
basicCode: string
|
|
371
|
+
): string {
|
|
372
|
+
const schema = this.schemaGenerator.generate(text, schemaComment);
|
|
373
|
+
|
|
374
|
+
// Format schema as TypeScript object with proper indentation
|
|
375
|
+
const schemaLines = JSON.stringify(schema, null, 2)
|
|
376
|
+
.split("\n")
|
|
377
|
+
.map((line, idx) => {
|
|
378
|
+
if (idx === 0) return " " + line;
|
|
379
|
+
return " " + line;
|
|
380
|
+
})
|
|
381
|
+
.join("\n");
|
|
382
|
+
|
|
383
|
+
// Format BASIC code as a comment block
|
|
384
|
+
const basicCodeComment = basicCode
|
|
385
|
+
.split("\n")
|
|
386
|
+
.map((line) => ` * ${line}`)
|
|
387
|
+
.join("\n");
|
|
388
|
+
|
|
389
|
+
return `/**
|
|
390
|
+
${basicCodeComment}
|
|
391
|
+
*/
|
|
392
|
+
import type { ScratchEndpointDefinition } from "../index";
|
|
393
|
+
|
|
394
|
+
export const ${opcode}: ScratchEndpointDefinition = {
|
|
395
|
+
block: async () => ({
|
|
396
|
+
opcode: "${opcode}",
|
|
397
|
+
blockType: "command",
|
|
398
|
+
text: ${JSON.stringify(text)},
|
|
399
|
+
schema: ${schemaLines},
|
|
400
|
+
}),
|
|
401
|
+
handler: async (context) => {
|
|
402
|
+
${handlerBody}
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
`;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Main BASIC Compiler class
|
|
411
|
+
*/
|
|
412
|
+
export class BasicCompiler {
|
|
413
|
+
private parser: BasicParser;
|
|
414
|
+
private generator: TypeScriptGenerator;
|
|
415
|
+
|
|
416
|
+
constructor() {
|
|
417
|
+
this.parser = new BasicParser();
|
|
418
|
+
this.generator = new TypeScriptGenerator();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Compiles BASIC code to TypeScript endpoint definition
|
|
423
|
+
* @param basicCode - BASIC source code (first line is text, optional second line is schema comment)
|
|
424
|
+
* @param opcode - The opcode for the generated endpoint
|
|
425
|
+
* @returns TypeScript source code for the endpoint
|
|
426
|
+
*/
|
|
427
|
+
compile(basicCode: string, opcode: string): string {
|
|
428
|
+
const lines = basicCode.split("\n").map((line) => line.trim());
|
|
429
|
+
|
|
430
|
+
// Extract text from first line (mandatory)
|
|
431
|
+
if (lines.length === 0) {
|
|
432
|
+
throw new Error("BASIC code must have at least a text line");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
let textLine = lines[0];
|
|
436
|
+
let schemaComment: string | undefined;
|
|
437
|
+
let codeStartIndex = 1;
|
|
438
|
+
|
|
439
|
+
// First line must be the text (can be a comment starting with #)
|
|
440
|
+
if (textLine.startsWith("#")) {
|
|
441
|
+
textLine = textLine.substring(1).trim();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (!textLine) {
|
|
445
|
+
throw new Error("First line must contain the endpoint text");
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Second line (optional) can be a schema comment
|
|
449
|
+
if (lines.length > 1 && lines[1].startsWith("#")) {
|
|
450
|
+
schemaComment = lines[1];
|
|
451
|
+
codeStartIndex = 2;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Parse the actual BASIC code (skip comments, but keep non-comment lines)
|
|
455
|
+
const codeLines = lines
|
|
456
|
+
.slice(codeStartIndex)
|
|
457
|
+
.map((line) => {
|
|
458
|
+
// Remove comments from code lines
|
|
459
|
+
const commentIndex = line.indexOf("#");
|
|
460
|
+
if (commentIndex >= 0) {
|
|
461
|
+
return line.substring(0, commentIndex).trim();
|
|
462
|
+
}
|
|
463
|
+
return line.trim();
|
|
464
|
+
})
|
|
465
|
+
.filter((line) => line.length > 0);
|
|
466
|
+
|
|
467
|
+
// Parse BASIC code to TypeScript
|
|
468
|
+
const handlerBody = this.parser.parse(codeLines);
|
|
469
|
+
|
|
470
|
+
// Generate TypeScript
|
|
471
|
+
return this.generator.generate(
|
|
472
|
+
opcode,
|
|
473
|
+
textLine,
|
|
474
|
+
schemaComment,
|
|
475
|
+
handlerBody,
|
|
476
|
+
basicCode
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Convenience function for backward compatibility
|
|
483
|
+
*/
|
|
484
|
+
export function compileBasicToTypeScript(
|
|
485
|
+
basicCode: string,
|
|
486
|
+
opcode: string
|
|
487
|
+
): string {
|
|
488
|
+
const compiler = new BasicCompiler();
|
|
489
|
+
return compiler.compile(basicCode, opcode);
|
|
490
|
+
}
|
package/core/Auth.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth - Authentication module for Universe
|
|
3
|
+
*
|
|
4
|
+
* Handles JWT token validation and signing
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { jwtVerify, SignJWT } from "jose";
|
|
8
|
+
import { env } from "./Env";
|
|
9
|
+
|
|
10
|
+
export class Auth {
|
|
11
|
+
private jwtSecretKey: Uint8Array | null;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
const jwtSecret = env("WEB_UI_JWT_SECRET", {
|
|
15
|
+
required: false,
|
|
16
|
+
defaultValue: "",
|
|
17
|
+
});
|
|
18
|
+
this.jwtSecretKey = jwtSecret ? new TextEncoder().encode(jwtSecret) : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validate JWT token and extract payload
|
|
23
|
+
*/
|
|
24
|
+
async validateJwtToken(token: string): Promise<any | null> {
|
|
25
|
+
if (!this.jwtSecretKey) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const { payload } = await jwtVerify(token, this.jwtSecretKey);
|
|
31
|
+
return payload;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sign/create a JWT token
|
|
39
|
+
*/
|
|
40
|
+
async signJwtToken(
|
|
41
|
+
payload: { email: string },
|
|
42
|
+
expirationTime: string = "30d"
|
|
43
|
+
): Promise<string> {
|
|
44
|
+
if (!this.jwtSecretKey) {
|
|
45
|
+
throw new Error("JWT secret not configured");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const jwt = await new SignJWT(payload)
|
|
49
|
+
.setProtectedHeader({ alg: "HS256" })
|
|
50
|
+
.setIssuedAt()
|
|
51
|
+
.setExpirationTime(expirationTime)
|
|
52
|
+
.sign(this.jwtSecretKey);
|
|
53
|
+
|
|
54
|
+
return jwt;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if JWT authentication is configured
|
|
59
|
+
*/
|
|
60
|
+
isConfigured(): boolean {
|
|
61
|
+
return this.jwtSecretKey !== null;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/core/Currency.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Currency Utility Functions
|
|
3
|
+
*
|
|
4
|
+
* Provides utility functions for currency handling, conversion, and formatting
|
|
5
|
+
* in the AI Executive system. This module handles financial data processing
|
|
6
|
+
* for business workflows and invoice generation.
|
|
7
|
+
*
|
|
8
|
+
* @module Currency
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
* @author Divizend GmbH
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export class Currency {
|
|
14
|
+
// Currency utility methods will be implemented here
|
|
15
|
+
// as the system expands to handle financial operations
|
|
16
|
+
}
|