tina4-nodejs 3.13.103 → 3.13.105

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.
Files changed (33) hide show
  1. package/CLAUDE.md +16 -2
  2. package/package.json +1 -1
  3. package/packages/cli/dist/bin.js +747 -57
  4. package/packages/core/dist/index.js +750 -57
  5. package/packages/core/src/authGate.ts +6 -0
  6. package/packages/core/src/index.ts +2 -0
  7. package/packages/core/src/queue.ts +75 -18
  8. package/packages/core/src/queueBackends/liteBackend.ts +17 -1
  9. package/packages/core/src/queueBackends/mongoBackend.ts +80 -14
  10. package/packages/core/src/server.ts +5 -0
  11. package/packages/core/src/sso.ts +285 -0
  12. package/packages/orm/dist/index.js +755 -62
  13. package/packages/orm/src/adapters/postgres.ts +2 -0
  14. package/packages/orm/src/baseModel.ts +63 -10
  15. package/packages/orm/src/index.ts +2 -0
  16. package/packages/orm/src/migration.ts +14 -5
  17. package/packages/orm/src/point.ts +105 -0
  18. package/packages/orm/src/queryBuilder.ts +66 -5
  19. package/packages/orm/src/sqlTranslator.ts +63 -0
  20. package/packages/orm/src/types.ts +5 -1
  21. package/packages/swagger/dist/index.js +11 -1
  22. package/packages/swagger/src/generator.ts +11 -1
  23. package/types/core/src/index.d.ts +2 -0
  24. package/types/core/src/queue.d.ts +41 -4
  25. package/types/core/src/queueBackends/liteBackend.d.ts +7 -1
  26. package/types/core/src/queueBackends/mongoBackend.d.ts +7 -2
  27. package/types/core/src/sso.d.ts +55 -0
  28. package/types/orm/src/baseModel.d.ts +13 -5
  29. package/types/orm/src/index.d.ts +2 -0
  30. package/types/orm/src/point.d.ts +24 -0
  31. package/types/orm/src/queryBuilder.d.ts +10 -1
  32. package/types/orm/src/sqlTranslator.d.ts +13 -0
  33. package/types/orm/src/types.d.ts +5 -1
@@ -74,7 +74,13 @@ export interface QueueBackendInterface {
74
74
  close(): void;
75
75
  complete?(queue: string, id: string): void;
76
76
  fail?(queue: string, id: string, error: string, maxRetries: number, retryBackoff: number): void;
77
- retry?(queue: string, id: string, delaySeconds?: number): void;
77
+ /**
78
+ * Explicit manual re-queue: returns true if the id was found and revived,
79
+ * false otherwise (parity with Python's backend.retry_job()). A backend may
80
+ * legacy-return void; callers coerce a void return to true so nothing that
81
+ * used to be reported as success silently flips to failure.
82
+ */
83
+ retry?(queue: string, id: string, delaySeconds?: number): boolean | void;
78
84
  deadLetters?(queue: string, maxRetries?: number): QueueJob[];
79
85
  failed?(queue: string, maxRetries?: number): QueueJob[];
80
86
  retryFailed?(queue: string, maxRetries?: number): number;
@@ -137,7 +143,19 @@ export declare class Queue {
137
143
  */
138
144
  process(handler: (job: QueueJob | QueueJob[]) => Promise<void> | void, options?: ProcessOptions): void;
139
145
  /**
140
- * Count jobs filtered by status. Defaults to "pending".
146
+ * Count jobs by status. Defaults to "pending".
147
+ *
148
+ * ``"pending"`` counts jobs waiting to be popped -- INCLUDES retryable-
149
+ * but-attempted ones, because they live in the pending queue under the
150
+ * auto-retry lifecycle (see failed()).
151
+ * ``"reserved"`` counts jobs a consumer has popped but not yet
152
+ * completed/failed (in-flight against the visibility timeout).
153
+ * ``"completed"`` counts jobs the consumer has finished successfully.
154
+ * ``"failed"``, ``"dead"``, ``"dead_letter"`` are ALIASES that all count
155
+ * the dead-letter store -- jobs whose attempts >= maxRetries and that
156
+ * have given up. Use deadLetters() to list them. Retryable-but-attempted
157
+ * jobs are NOT counted by size("failed"); use failed() to list them or
158
+ * size("pending") to include them in a total.
141
159
  */
142
160
  size(status?: string): number;
143
161
  /**
@@ -172,7 +190,11 @@ export declare class Queue {
172
190
  /**
173
191
  * Get jobs that failed at least once but are still being retried
174
192
  * (0 < attempts < maxRetries). These live in the pending queue under the
175
- * auto-retry lifecycle; dead-lettered jobs are returned by deadLetters().
193
+ * auto-retry lifecycle (fail() re-queues them with an incremented attempts
194
+ * count and a retryBackoff delay) so pop() picks them up again. They are
195
+ * NOT counted by size("failed") -- that alias counts the dead-letter store,
196
+ * matching deadLetters(). To include retryable-failed jobs in a total, use
197
+ * size("pending"). Terminal failures are returned by deadLetters().
176
198
  */
177
199
  failed(): QueueJob[];
178
200
  /**
@@ -184,7 +206,22 @@ export declare class Queue {
184
206
  */
185
207
  retry(jobId?: string, delaySeconds?: number): boolean;
186
208
  /**
187
- * Get dead letter jobs failed jobs that exceeded max retries.
209
+ * Get jobs that exceeded max_retries -- terminal failures.
210
+ *
211
+ * Same set counted by size("failed") / size("dead") / size("dead_letter")
212
+ * (three aliases for the dead-letter store). To LIST retryable-but-
213
+ * attempted jobs (attempts > 0 AND attempts < maxRetries) that are still
214
+ * being auto-retried, use failed() -- those live in the pending queue and
215
+ * are NOT dead letters.
216
+ *
217
+ * Returns Job objects with the failure reason on ``.error`` (not raw dicts)
218
+ * so callers can iterate uniformly with the rest of the queue API and, in
219
+ * particular, call ``.retry()`` on each to manually revive it:
220
+ *
221
+ * for (const job of queue.deadLetters()) {
222
+ * Log.warn(`revived ${job.id}: ${job.error}`);
223
+ * job.retry();
224
+ * }
188
225
  */
189
226
  deadLetters(maxRetries?: number): QueueJob[];
190
227
  /**
@@ -122,7 +122,13 @@ export declare class LiteBackend {
122
122
  * Explicit re-queue requested by the caller (job.retry()).
123
123
  *
124
124
  * Always re-enqueues regardless of the retry limit — manual override,
125
- * distinct from the automatic failJob() path.
125
+ * distinct from the automatic failJob() path. Cleans up BOTH the
126
+ * reservation record AND any dead-letter file for this id, so a caller
127
+ * that iterates deadLetters() and calls .retry() on each doesn't leave
128
+ * the failed/ directory carrying duplicates (PY-12-05, 3.13.105).
129
+ * Aligns with retry(queue, jobId) which had always unlinked the
130
+ * dead-letter file -- two spellings of the same intent that previously
131
+ * diverged.
126
132
  */
127
133
  retryJob(queue: string, job: QueueJob, delaySeconds?: number): void;
128
134
  }
@@ -87,8 +87,13 @@ export declare class MongoBackend implements QueueBackend {
87
87
  * retries remain, else dead-letter. Mirrors the file/lite backend.
88
88
  */
89
89
  fail(queue: string, id: string, error: string, maxRetries: number, retryBackoff?: number): void;
90
- /** Explicit manual re-queue (always re-enqueues regardless of the retry limit). */
91
- retry(queue: string, id: string, delaySeconds?: number): void;
90
+ /**
91
+ * Revive a specific dead-letter job by id. Returns true if the DL was found
92
+ * and revived, false otherwise (parity with LiteBackend.retry(queue, id)
93
+ * and Python's mongo_backend.retry_job()). Pre-3.13.105 this returned void
94
+ * and Queue.retry(id) reported success for every call, even for unknown ids.
95
+ */
96
+ retry(queue: string, id: string, delaySeconds?: number): boolean;
92
97
  /** Jobs that exceeded max retries (the `<queue>.dead_letter` collection topic). */
93
98
  deadLetters(queue: string, maxRetries?: number): QueueJob[];
94
99
  /** Jobs that failed but are still eligible for retry (status=failed, attempts < max). */
@@ -0,0 +1,55 @@
1
+ import type { Router } from "./router.js";
2
+ export declare class SsoError extends Error {
3
+ }
4
+ type Json = Record<string, any>;
5
+ export interface SsoOptions {
6
+ issuer?: string;
7
+ clientId?: string;
8
+ clientSecret?: string;
9
+ redirectUri?: string;
10
+ scopes?: string[];
11
+ verify?: "introspection" | "jwks";
12
+ postLogoutRedirectUri?: string;
13
+ claimMap?: Record<string, string>;
14
+ timeout?: number;
15
+ }
16
+ export declare class Sso {
17
+ static readonly PENDING_KEY = "_tina4_sso_pending";
18
+ static readonly SESSION_KEY = "_tina4_sso";
19
+ readonly issuer: string;
20
+ readonly clientId: string;
21
+ readonly clientSecret?: string;
22
+ readonly redirectUri: string;
23
+ readonly scopes: string[];
24
+ readonly verify: "introspection" | "jwks";
25
+ readonly postLogoutRedirectUri?: string;
26
+ readonly claimMap: Record<string, string>;
27
+ readonly timeout: number;
28
+ private metadata;
29
+ private static mountedRouters;
30
+ constructor(options?: SsoOptions);
31
+ static fromIssuer(options?: SsoOptions): Promise<Sso>;
32
+ static configured(): boolean;
33
+ private jsonEnv;
34
+ private static secureUrl;
35
+ private validateConfig;
36
+ private requestJson;
37
+ discover(force?: boolean): Promise<Json>;
38
+ static safeReturn(value?: string): string;
39
+ private session;
40
+ login(requestOrSession: any, returnTo?: string): Promise<string>;
41
+ private static equal;
42
+ private static jwtPayload;
43
+ private introspect;
44
+ private claim;
45
+ private normalize;
46
+ callback(requestOrSession: any, query?: Json): Promise<{
47
+ identity: Json;
48
+ return_to: string;
49
+ }>;
50
+ identity(requestOrSession: any): Json | null;
51
+ refresh(requestOrSession: any): Promise<Json>;
52
+ logout(requestOrSession: any, returnTo?: string): Promise<string>;
53
+ static mountConfigured(router: Router): Promise<boolean>;
54
+ }
55
+ export { Sso as SSO };
@@ -271,6 +271,8 @@ export declare class BaseModel {
271
271
  * @param case_ Key casing: 'camel' (default, keys as-is) or 'snake' (convert via fieldMapping).
272
272
  */
273
273
  toDict(include?: string[], case_?: "camel" | "snake"): Record<string, unknown>;
274
+ toFeature(geometryField?: string, include?: string[]): Record<string, unknown>;
275
+ static featureCollection(models: BaseModel[], geometryField?: string, include?: string[]): Record<string, unknown>;
274
276
  /**
275
277
  * Convert to an associative object (alias for toDict).
276
278
  */
@@ -302,6 +304,7 @@ export declare class BaseModel {
302
304
  * Uses the adapter's createTable method if available, otherwise builds SQL directly.
303
305
  */
304
306
  static createTable(): Promise<boolean>;
307
+ private static createSpatialIndexes;
305
308
  /**
306
309
  * Find a record by primary key or throw an error if not found.
307
310
  */
@@ -336,11 +339,16 @@ export declare class BaseModel {
336
339
  /**
337
340
  * Invalidate every cached query that touches this model's table.
338
341
  *
339
- * Tag-scoped, NOT a wholesale flush: a cached JOIN on another model that reads
340
- * this table is busted too (it carries this table's tag), while a query that
341
- * never touches this table is left intact. Called after every ORM write
342
- * (save/delete/forceDelete/restore) so a read-after-write never serves a
343
- * stale/deleted row (CACHE-DEC-01).
342
+ * Tag-scoped in the ORM layer (a cached JOIN on another model that reads
343
+ * this table is busted too because it carries this table's tag; a query
344
+ * that never touches this table is left intact), then cascaded to the
345
+ * DB layer on this model's bound connection so an out-of-band write /
346
+ * deliberate refresh / race-with-another-process cannot leave stale rows
347
+ * in db.fetch()'s persistent cache. Called after every ORM write
348
+ * (save/delete/forceDelete/restore) so a read-after-write never serves
349
+ * a stale/deleted row (CACHE-DEC-01). PY-06-22 (3.13.105) added the
350
+ * DB-layer cascade -- previously the two cache layers disagreed under
351
+ * TINA4_AUTO_CACHING=true + TINA4_DB_CACHE=true.
344
352
  */
345
353
  static clearCache(): void;
346
354
  /**
@@ -19,6 +19,8 @@ export type { ValidationError } from "./validation.js";
19
19
  export { BaseModel, snakeToCamel, camelToSnake } from "./baseModel.js";
20
20
  export { QueryBuilder } from "./queryBuilder.js";
21
21
  export { SQLTranslator, QueryCache } from "./sqlTranslator.js";
22
+ export { Point, SpatialNotSupportedError, DEFAULT_SRID } from "./point.js";
23
+ export type { GeoJsonPoint } from "./point.js";
22
24
  export { DEFAULT_DATABASE_CONNECT_TIMEOUT_SECONDS, CONNECT_TIMEOUT_TOLERANCE_MS, connectTimeoutMillis, driverConnectTimeoutMillis, connectTarget, withConnectTimeout, } from "./connectTimeout.js";
23
25
  export { CachedDatabaseAdapter } from "./cachedDatabase.js";
24
26
  export type { CachedAdapterOptions } from "./cachedDatabase.js";
@@ -0,0 +1,24 @@
1
+ export declare const DEFAULT_SRID = 4326;
2
+ export declare class SpatialNotSupportedError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export type GeoJsonPoint = {
6
+ type: "Point";
7
+ coordinates: [number, number];
8
+ };
9
+ /** Immutable SRID-aware longitude/latitude point (ADR-0057). */
10
+ export declare class Point {
11
+ readonly lon: number;
12
+ readonly lat: number;
13
+ readonly srid: number;
14
+ constructor(lon: unknown, lat: unknown, srid?: unknown);
15
+ get wkt(): string;
16
+ get ewkt(): string;
17
+ get geojson(): GeoJsonPoint;
18
+ toJSON(): GeoJsonPoint;
19
+ toArray(): [number, number];
20
+ static parse(value: unknown, srid?: number): Point;
21
+ static geometryBinding(value: unknown, srid?: number): [string, "ewkt" | "geojson"];
22
+ private static fromGeoJson;
23
+ private static fromWkb;
24
+ }
@@ -22,6 +22,7 @@ export declare class QueryBuilder {
22
22
  private table;
23
23
  private db;
24
24
  private columns;
25
+ private selectParams;
25
26
  private wheres;
26
27
  private params;
27
28
  private joinClauses;
@@ -29,6 +30,8 @@ export declare class QueryBuilder {
29
30
  private havings;
30
31
  private havingParams;
31
32
  private orderByCols;
33
+ private orderByParams;
34
+ private primaryKey;
32
35
  private limitVal;
33
36
  private offsetVal;
34
37
  /**
@@ -42,7 +45,7 @@ export declare class QueryBuilder {
42
45
  * @param db - Optional database adapter.
43
46
  * @returns A new QueryBuilder instance.
44
47
  */
45
- static fromTable(tableName: string, db?: DatabaseAdapter): QueryBuilder;
48
+ static fromTable(tableName: string, db?: DatabaseAdapter, primaryKey?: string): QueryBuilder;
46
49
  /**
47
50
  * Set the columns to select.
48
51
  *
@@ -104,6 +107,11 @@ export declare class QueryBuilder {
104
107
  * @returns this for chaining.
105
108
  */
106
109
  orderBy(expression: string): QueryBuilder;
110
+ withinDistance(column: string, pointValue: unknown, radiusMetres: number, srid?: number): QueryBuilder;
111
+ intersects(column: string, geometry: unknown, srid?: number): QueryBuilder;
112
+ bbox(column: string, minLon: unknown, minLat: unknown, maxLon: unknown, maxLat: unknown, srid?: number): QueryBuilder;
113
+ selectDistance(column: string, pointValue: unknown, alias?: string, srid?: number): QueryBuilder;
114
+ orderByDistance(column: string, pointValue: unknown, direction?: "ASC" | "DESC", srid?: number): QueryBuilder;
107
115
  /**
108
116
  * Set LIMIT and optional OFFSET.
109
117
  *
@@ -190,4 +198,5 @@ export declare class QueryBuilder {
190
198
  * Ensure a database adapter is available.
191
199
  */
192
200
  private ensureDb;
201
+ private engine;
193
202
  }
@@ -17,6 +17,19 @@
17
17
  * Also includes a query cache with TTL support.
18
18
  */
19
19
  export declare class SQLTranslator {
20
+ private static readonly SPATIAL_ENGINES;
21
+ private static readonly SPATIAL_IDENTIFIER;
22
+ static requireSpatial(engine: string, feature: string): string;
23
+ static spatialIdentifier(name: string, what?: string): string;
24
+ static pointColumnType(engine: string, srid?: number): string;
25
+ static spatialIndex(engine: string, table: string, column: string): string;
26
+ static pointLiteral(engine: string, srid?: number): string;
27
+ static withinDistance(engine: string, column: string, srid?: number): string;
28
+ static distance(engine: string, column: string, srid?: number): string;
29
+ static distanceAs(engine: string, column: string, alias: string, srid?: number): string;
30
+ static geometryLiteral(engine: string, form: "ewkt" | "geojson", srid?: number): string;
31
+ static intersects(engine: string, column: string, form?: "ewkt" | "geojson", srid?: number): string;
32
+ static bbox(engine: string, column: string, srid?: number): string;
20
33
  /**
21
34
  * Convert LIMIT/OFFSET to Firebird ROWS...TO syntax.
22
35
  *
@@ -1,4 +1,4 @@
1
- export type FieldType = "string" | "integer" | "number" | "numeric" | "decimal" | "boolean" | "datetime" | "text" | "json" | "foreignKey";
1
+ export type FieldType = "string" | "integer" | "number" | "numeric" | "decimal" | "boolean" | "datetime" | "text" | "json" | "foreignKey" | "point";
2
2
  export interface FieldDefinition {
3
3
  type: FieldType;
4
4
  primaryKey?: boolean;
@@ -23,6 +23,10 @@ export interface FieldDefinition {
23
23
  references?: string;
24
24
  /** For type "foreignKey": override the has-many property name on the referenced model */
25
25
  relatedName?: string;
26
+ /** For type "point": spatial reference id (default WGS 84 / 4326). */
27
+ srid?: number;
28
+ /** For type "point": create the provider's spatial index (default true). */
29
+ spatialIndex?: boolean;
26
30
  }
27
31
  export interface RelationshipDefinition {
28
32
  model: string;