tina4-nodejs 3.10.5 → 3.10.6

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/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
- # CLAUDE.md — AI Developer Guide for tina4-nodejs (v3.10.5)
1
+ # CLAUDE.md — AI Developer Guide for tina4-nodejs (v3.10.6)
2
2
 
3
3
  > This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
4
4
 
5
5
  ## What This Project Is
6
6
 
7
- Tina4 for Node.js/TypeScript v3.10.5 — a convention-over-configuration structural paradigm. **Not a framework.** The developer writes TypeScript; Tina4 is invisible infrastructure.
7
+ Tina4 for Node.js/TypeScript v3.10.6 — a convention-over-configuration structural paradigm. **Not a framework.** The developer writes TypeScript; Tina4 is invisible infrastructure.
8
8
 
9
9
  The philosophy: zero ceremony, batteries included, file system as source of truth.
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tina4-nodejs",
3
- "version": "3.10.5",
3
+ "version": "3.10.6",
4
4
  "type": "module",
5
5
  "description": "This is not a framework. Tina4 for Node.js/TypeScript — zero deps, 38 built-in features.",
6
6
  "keywords": ["tina4", "framework", "web", "api", "orm", "graphql", "websocket", "typescript"],
@@ -161,6 +161,23 @@ export abstract class WSDLService {
161
161
 
162
162
  protected namespace: string = "http://tina4.com/wsdl";
163
163
 
164
+ /**
165
+ * Lifecycle hook: called before operation invocation.
166
+ * Override to validate, log, or modify the incoming request.
167
+ */
168
+ protected onRequest(_request: unknown): void {
169
+ // no-op — override in subclass
170
+ }
171
+
172
+ /**
173
+ * Lifecycle hook: called after operation returns.
174
+ * Override to transform, audit, or enrich the result.
175
+ * Must return the (possibly modified) result.
176
+ */
177
+ protected onResult(result: Record<string, unknown>): Record<string, unknown> {
178
+ return result;
179
+ }
180
+
164
181
  /** Discovered operations (populated on first use). */
165
182
  private _operations: Map<string, WSDLOperation> | null = null;
166
183
 
@@ -376,10 +393,15 @@ export abstract class WSDLService {
376
393
  }
377
394
  }
378
395
 
396
+ // Lifecycle hook: before invocation
397
+ this.onRequest(soapXml);
398
+
379
399
  // Invoke the method
380
400
  try {
381
- const result = await (method as (...args: unknown[]) => Promise<unknown>).call(this, ...params);
382
- return this.soapResponse(opName, result as Record<string, unknown>);
401
+ const rawResult = await (method as (...args: unknown[]) => Promise<unknown>).call(this, ...params);
402
+ // Lifecycle hook: after invocation — allow result transformation
403
+ const result = this.onResult(rawResult as Record<string, unknown>);
404
+ return this.soapResponse(opName, result);
383
405
  } catch (err) {
384
406
  const errMsg = err instanceof Error ? err.message : String(err);
385
407
  return this.soapFault("Server", errMsg);
@@ -341,19 +341,39 @@ function evalExpr(expr: string, context: Record<string, unknown>): unknown {
341
341
  }
342
342
  }
343
343
 
344
- // Function call: name("arg1", "arg2")
345
- const fnMatch = expr.match(/^(\w+)\s*\(([\s\S]*)?\)$/);
344
+ // Function call: name("arg1", "arg2") — supports dotted names like user.t("key")
345
+ const fnMatch = expr.match(/^([\w.]+)\s*\(([\s\S]*)?\)$/);
346
346
  if (fnMatch) {
347
347
  const fnName = fnMatch[1];
348
348
  const rawArgs = fnMatch[2] || "";
349
- const fn = context[fnName] ?? resolveVar(fnName, context);
350
- if (typeof fn === "function") {
351
- if (rawArgs.trim()) {
352
- const parts = splitArgs(rawArgs);
353
- const evalArgs = parts.map(a => evalExpr(a.trim(), context));
354
- return fn(...evalArgs);
349
+
350
+ // Dotted function name: resolve object, then call method
351
+ if (fnName.includes(".")) {
352
+ const lastDot = fnName.lastIndexOf(".");
353
+ const objPath = fnName.slice(0, lastDot);
354
+ const methodName = fnName.slice(lastDot + 1);
355
+ const obj = resolveVar(objPath, context);
356
+ if (obj && typeof obj === "object" && methodName in (obj as Record<string, unknown>)) {
357
+ const method = (obj as Record<string, unknown>)[methodName];
358
+ if (typeof method === "function") {
359
+ if (rawArgs.trim()) {
360
+ const parts = splitArgs(rawArgs);
361
+ const evalArgs = parts.map(a => evalExpr(a.trim(), context));
362
+ return method.apply(obj, evalArgs);
363
+ }
364
+ return method.call(obj);
365
+ }
366
+ }
367
+ } else {
368
+ const fn = context[fnName] ?? resolveVar(fnName, context);
369
+ if (typeof fn === "function") {
370
+ if (rawArgs.trim()) {
371
+ const parts = splitArgs(rawArgs);
372
+ const evalArgs = parts.map(a => evalExpr(a.trim(), context));
373
+ return fn(...evalArgs);
374
+ }
375
+ return fn();
355
376
  }
356
- return fn();
357
377
  }
358
378
  }
359
379