admindb 2.2.1 → 2.3.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.
Files changed (87) hide show
  1. package/README.md +368 -364
  2. package/dist/app.js +6 -9
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +18 -30
  5. package/dist/auth/crypto.js +11 -21
  6. package/dist/auth/middleware.js +9 -16
  7. package/dist/auth/routes.js +9 -15
  8. package/dist/cli/args.d.ts +1 -1
  9. package/dist/cli/args.js +15 -26
  10. package/dist/cli/config.js +6 -16
  11. package/dist/cli/runner.js +4 -7
  12. package/dist/data/chain.d.ts +1 -1
  13. package/dist/data/chain.js +40 -617
  14. package/dist/data/datasets.d.ts +1 -1
  15. package/dist/data/datasets.js +43 -1
  16. package/dist/data/detector.js +45 -21
  17. package/dist/data/engine.d.ts +1 -1
  18. package/dist/data/engine.js +483 -142
  19. package/dist/data/index.d.ts +1 -1
  20. package/dist/data/index.js +8 -0
  21. package/dist/data/prng.d.ts +1 -0
  22. package/dist/data/prng.js +73 -0
  23. package/dist/data/registry.d.ts +1 -0
  24. package/dist/data/registry.js +805 -0
  25. package/dist/data/schema-graph.d.ts +1 -0
  26. package/dist/data/schema-graph.js +210 -0
  27. package/dist/data/strategies.d.ts +1 -1
  28. package/dist/data/strategies.js +44 -283
  29. package/dist/data/templates.d.ts +1 -0
  30. package/dist/data/templates.js +134 -0
  31. package/dist/data/types.d.ts +1 -1
  32. package/dist/data/validator.d.ts +1 -0
  33. package/dist/data/validator.js +160 -0
  34. package/dist/db/database.js +9 -14
  35. package/dist/db/export.js +32 -55
  36. package/dist/db/filters.d.ts +1 -1
  37. package/dist/db/filters.js +15 -38
  38. package/dist/db/introspection.js +4 -7
  39. package/dist/db/manager.js +27 -51
  40. package/dist/db/migrations.js +7 -14
  41. package/dist/db/postgres.js +30 -78
  42. package/dist/index.d.ts +1 -1
  43. package/dist/public/css/app.css +1 -1
  44. package/dist/public/js/browse.js +1 -1
  45. package/dist/public/js/inspector.js +1 -1
  46. package/dist/public/js/query.js +1 -1
  47. package/dist/public/js/seed.js +1 -1
  48. package/dist/routes/api/erd.js +3 -8
  49. package/dist/routes/api/helpers.d.ts +1 -1
  50. package/dist/routes/api/helpers.js +75 -25
  51. package/dist/routes/api/import-export.js +1 -2
  52. package/dist/routes/api/query.js +2 -6
  53. package/dist/routes/api/rows.js +4 -8
  54. package/dist/routes/api/seed.js +35 -21
  55. package/dist/routes/api/tables.js +15 -30
  56. package/dist/routes/databases.js +8 -26
  57. package/dist/routes/pages.js +67 -18
  58. package/dist/serverless.js +15 -40
  59. package/dist/sql/generator.d.ts +1 -1
  60. package/dist/sql/generator.js +35 -109
  61. package/dist/types/api.d.ts +1 -1
  62. package/dist/utils/common.d.ts +1 -1
  63. package/dist/utils/common.js +45 -85
  64. package/dist/utils/csv.d.ts +1 -1
  65. package/dist/utils/csv.js +11 -17
  66. package/dist/utils/datatype.js +46 -60
  67. package/dist/utils/icons.js +8 -8
  68. package/dist/views/layouts/main.hbs +1 -87
  69. package/dist/views/pages/databases.hbs +1 -233
  70. package/dist/views/pages/designer.hbs +1 -89
  71. package/dist/views/pages/erd.hbs +1 -1241
  72. package/dist/views/pages/error.hbs +1 -10
  73. package/dist/views/pages/form.hbs +1 -59
  74. package/dist/views/pages/home.hbs +1 -220
  75. package/dist/views/pages/info.hbs +1 -107
  76. package/dist/views/pages/login.hbs +1 -98
  77. package/dist/views/pages/query.hbs +1 -129
  78. package/dist/views/pages/schema.hbs +1 -298
  79. package/dist/views/pages/seed-select.hbs +251 -0
  80. package/dist/views/pages/seed.hbs +256 -414
  81. package/dist/views/pages/table.hbs +1 -327
  82. package/dist/views/partials/navbar.hbs +1 -83
  83. package/dist/views/partials/sidebar.hbs +1 -129
  84. package/docs/API.md +149 -149
  85. package/docs/EXAMPLES.md +579 -579
  86. package/docs/SECURITY.md +265 -265
  87. package/package.json +79 -79
@@ -1 +1 @@
1
- import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory,};export interface ApiSuccessResponse<T=unknown>{success:true;data:T;}export interface ApiErrorResponse<T=unknown>{success:false;error:string;data?:T;}export type ApiResponse<T=unknown>=ApiSuccessResponse<T>|ApiErrorResponse<T>;export type ListTablesResponseData=TableListItem[];export type ListTablesResponse=ApiResponse<ListTablesResponseData>;export type TableInfoResponseData=TableInfoData;export type TableInfoResponse=ApiResponse<TableInfoResponseData>;export type FkOptionsResponseData=Record<string,{value:unknown;label:string;}[]>;export type FkOptionsResponse=ApiResponse<FkOptionsResponseData>;export interface GetRowsResponseData{rows:Record<string,unknown>[];count:number;page:number;limit:number;filters:RowFilters;}export type GetRowsResponse=ApiResponse<GetRowsResponseData>;export type GetRowResponseData=Record<string,unknown>;export type GetRowResponse=ApiResponse<GetRowResponseData>;export interface RowReferenceGroup{table:string; from :string;to:string;value:unknown;count:number;columns:string[];rows:Record<string,unknown>[];}export interface GetRowReferencesResponseData{references:RowReferenceGroup[];}export type GetRowReferencesResponse=ApiResponse<GetRowReferencesResponseData>;export interface InsertRowRequestBody{values:Record<string,unknown>;}export interface InsertRowResponseData{message:string;rowId?:number|bigint;sql:string;}export type InsertRowResponse=ApiResponse<InsertRowResponseData>;export interface GenerateInsertRequestBody{values:Record<string,unknown>;}export interface GenerateInsertResponseData{sql:string;}export type GenerateInsertResponse=ApiResponse<GenerateInsertResponseData>;export interface ImportCsvRequestBody{csv:string;}export interface ImportCsvResponseData{message:string;inserted?:number;skipped?:number;}export type ImportCsvResponse=ApiResponse<ImportCsvResponseData>;export interface UpdateRowRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface UpdateRowResponseData{message:string;sql:string;}export type UpdateRowResponse=ApiResponse<UpdateRowResponseData>;export interface GenerateUpdateRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface GenerateUpdateResponseData{sql:string;}export type GenerateUpdateResponse=ApiResponse<GenerateUpdateResponseData>;export interface DeleteRowResponseData{message:string;}export type DeleteRowResponse=ApiResponse<DeleteRowResponseData>;export interface BlobMetaResponseData{isNull:boolean;mime?:string;ext?:string;isImage?:boolean;isText?:boolean;isPdf?:boolean;isAudio?:boolean;isVideo?:boolean;size:number;sizeFormatted:string;hexDump?:{lines:{offset:string;hex:string;ascii:string;}[];totalBytes:number;truncated:boolean;};textPreview?:string|null;}export type BlobMetaResponse=ApiResponse<BlobMetaResponseData>;export interface UpdateBlobRequestBody{data:string;format?:'base64'|'hex'|'text';}export interface UpdateBlobResponseData{message:string;size:number;}export type UpdateBlobResponse=ApiResponse<UpdateBlobResponseData>;export interface TableDdlResponseData{table:string;ddl:string;}export type TableDdlResponse=ApiResponse<TableDdlResponseData>;export interface BulkImpactRequestBody{ids:(string|number)[];}export interface BulkImpactReference{table:string; from :string;to:string;count:number;}export interface BulkImpactResponseData{selected:number;references:BulkImpactReference[];total:number;}export type BulkImpactResponse=ApiResponse<BulkImpactResponseData>;export interface BulkDeleteRequestBody{ids:(string|number)[];confirmImpact?:boolean;}export interface BulkDeleteResponseData{message:string;deleted:number;}export type BulkDeleteResponse=ApiResponse<BulkDeleteResponseData>;export interface BulkExportRequestBody{ids:(string|number)[];format?:'csv'|'json';}export interface BulkExportResponseData{format:string;filename:string;content:string;rowCount:number;}export type BulkExportResponse=ApiResponse<BulkExportResponseData>;export interface BulkUpdateItem{id:string|number;values?:Record<string,unknown>;nulls?:string[];}export interface BulkUpdateRequestBody{updates:BulkUpdateItem[];}export interface BulkUpdateResponseData{message:string;updated:number;}export type BulkUpdateResponse=ApiResponse<BulkUpdateResponseData>;export interface SeedConfigResponseData{table:string;columns:ColumnGeneratorConfig[];maxRows:number;}export type SeedConfigResponse=ApiResponse<SeedConfigResponseData>;export interface SeedGenerateRequestBody{count?:number;plan?:Record<string,ColumnPlan>;}export interface SeedGenerateResponseData{table:string;count:number;sql:string;previewRows?:Record<string,unknown>[];warnings:string[];}export type SeedGenerateResponse=ApiResponse<SeedGenerateResponseData>;export interface SeedInsertRequestBody{count?:number;plan?:Record<string,ColumnPlan>;truncate?:boolean;}export interface SeedInsertResponseData{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs?:number;}export type SeedInsertResponse=ApiResponse<SeedInsertResponseData>;export interface CreateTableRequestBody{name:string;columns:ColumnDef[];}export interface CreateTableResponseData{message:string;sql:string;}export type CreateTableResponse=ApiResponse<CreateTableResponseData>;export interface GenerateCreateTableRequestBody{name:string;columns:ColumnDef[];}export interface GenerateCreateTableResponseData{sql:string;}export type GenerateCreateTableResponse=ApiResponse<GenerateCreateTableResponseData>;export type GetSchemaResponseData=SchemaInfo;export type GetSchemaResponse=ApiResponse<GetSchemaResponseData>;export interface RenameTableRequestBody{name:string;}export interface RenameTableResponseData{message:string;sql:string;table:string;}export type RenameTableResponse=ApiResponse<RenameTableResponseData>;export interface AddColumnRequestBody{column:ColumnDef;}export interface AddColumnResponseData{message:string;sql:string;}export type AddColumnResponse=ApiResponse<AddColumnResponseData>;export interface RenameColumnRequestBody{name:string;}export interface RenameColumnResponseData{message:string;sql:string;}export type RenameColumnResponse=ApiResponse<RenameColumnResponseData>;export interface DropColumnResponseData{message:string;sql:string;}export type DropColumnResponse=ApiResponse<DropColumnResponseData>;export interface DropTableResponseData{message:string;sql:string;}export type DropTableResponse=ApiResponse<DropTableResponseData>;export interface CreateIndexRequestBody{name?:string;columns:string[];unique?:boolean;}export interface CreateIndexResponseData{message:string;sql:string;}export type CreateIndexResponse=ApiResponse<CreateIndexResponseData>;export interface DropIndexResponseData{message:string;sql:string;}export type DropIndexResponse=ApiResponse<DropIndexResponseData>;export interface ExecuteQueryRequestBody{sql:string;}export interface QueryCountResult{kind:'count';count:number;message:string;}export interface QuerySelectResult{kind:'select';columns:string[];rows:Record<string,unknown>[];}export interface QueryWriteResult{kind:'write';changes:number|null;message:string;}export type ExecuteQueryResponseData=QueryCountResult|QuerySelectResult|QueryWriteResult;export type ExecuteQueryResponse=ApiResponse<ExecuteQueryResponseData>;export interface ExportQueryRequestBody{sql:string;format?:'csv'|'json';}export type ListSavedQueriesResponseData=SavedQuery[];export type ListSavedQueriesResponse=ApiResponse<ListSavedQueriesResponseData>;export interface SaveQueryRequestBody{name:string;sql:string;}export interface SaveQueryResponseData{message:string;}export type SaveQueryResponse=ApiResponse<SaveQueryResponseData>;export interface DeleteSavedQueryResponseData{message:string;}export type DeleteSavedQueryResponse=ApiResponse<DeleteSavedQueryResponseData>;export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export type ListDatabasesResponseData=DatabaseRow[];export type ListDatabasesResponse=ApiResponse<ListDatabasesResponseData>;export interface CreateDatabaseRequestBody{name:string;}export interface CreateDatabaseResponseData{id:string;message:string;}export type CreateDatabaseResponse=ApiResponse<CreateDatabaseResponseData>;export interface OpenDatabaseRequestBody{path:string;}export interface OpenDatabaseResponseData{id:string;message:string;}export type OpenDatabaseResponse=ApiResponse<OpenDatabaseResponseData>;export interface DeleteDatabaseResponseData{message:string;}export type DeleteDatabaseResponse=ApiResponse<DeleteDatabaseResponseData>;export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export interface ListFsResponseData{path:string;name:string;parent:string|null;entries:FsEntry[];}export type ListFsResponse=ApiResponse<ListFsResponseData>;
1
+ import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory};export interface ApiSuccessResponse<T=unknown>{success:true;data:T;}export interface ApiErrorResponse<T=unknown>{success:false;error:string;data?:T;}export type ApiResponse<T=unknown>=ApiSuccessResponse<T>|ApiErrorResponse<T>;export type ListTablesResponseData=TableListItem[];export type ListTablesResponse=ApiResponse<ListTablesResponseData>;export type TableInfoResponseData=TableInfoData;export type TableInfoResponse=ApiResponse<TableInfoResponseData>;export type FkOptionsResponseData=Record<string,{value:unknown;label:string;}[]>;export type FkOptionsResponse=ApiResponse<FkOptionsResponseData>;export interface GetRowsResponseData{rows:Record<string,unknown>[];count:number;page:number;limit:number;filters:RowFilters;}export type GetRowsResponse=ApiResponse<GetRowsResponseData>;export type GetRowResponseData=Record<string,unknown>;export type GetRowResponse=ApiResponse<GetRowResponseData>;export interface RowReferenceGroup{table:string; from :string;to:string;value:unknown;count:number;columns:string[];rows:Record<string,unknown>[];}export interface GetRowReferencesResponseData{references:RowReferenceGroup[];}export type GetRowReferencesResponse=ApiResponse<GetRowReferencesResponseData>;export interface InsertRowRequestBody{values:Record<string,unknown>;}export interface InsertRowResponseData{message:string;rowId?:number|bigint;sql:string;}export type InsertRowResponse=ApiResponse<InsertRowResponseData>;export interface GenerateInsertRequestBody{values:Record<string,unknown>;}export interface GenerateInsertResponseData{sql:string;}export type GenerateInsertResponse=ApiResponse<GenerateInsertResponseData>;export interface ImportCsvRequestBody{csv:string;}export interface ImportCsvResponseData{message:string;inserted?:number;skipped?:number;}export type ImportCsvResponse=ApiResponse<ImportCsvResponseData>;export interface UpdateRowRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface UpdateRowResponseData{message:string;sql:string;}export type UpdateRowResponse=ApiResponse<UpdateRowResponseData>;export interface GenerateUpdateRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface GenerateUpdateResponseData{sql:string;}export type GenerateUpdateResponse=ApiResponse<GenerateUpdateResponseData>;export interface DeleteRowResponseData{message:string;}export type DeleteRowResponse=ApiResponse<DeleteRowResponseData>;export interface BlobMetaResponseData{isNull:boolean;mime?:string;ext?:string;isImage?:boolean;isText?:boolean;isPdf?:boolean;isAudio?:boolean;isVideo?:boolean;size:number;sizeFormatted:string;hexDump?:{lines:{offset:string;hex:string;ascii:string;}[];totalBytes:number;truncated:boolean;};textPreview?:string|null;}export type BlobMetaResponse=ApiResponse<BlobMetaResponseData>;export interface UpdateBlobRequestBody{data:string;format?:'base64'|'hex'|'text';}export interface UpdateBlobResponseData{message:string;size:number;}export type UpdateBlobResponse=ApiResponse<UpdateBlobResponseData>;export interface TableDdlResponseData{table:string;ddl:string;}export type TableDdlResponse=ApiResponse<TableDdlResponseData>;export interface BulkImpactRequestBody{ids:(string|number)[];}export interface BulkImpactReference{table:string; from :string;to:string;count:number;}export interface BulkImpactResponseData{selected:number;references:BulkImpactReference[];total:number;}export type BulkImpactResponse=ApiResponse<BulkImpactResponseData>;export interface BulkDeleteRequestBody{ids:(string|number)[];confirmImpact?:boolean;}export interface BulkDeleteResponseData{message:string;deleted:number;}export type BulkDeleteResponse=ApiResponse<BulkDeleteResponseData>;export interface BulkExportRequestBody{ids:(string|number)[];format?:'csv'|'json';}export interface BulkExportResponseData{format:string;filename:string;content:string;rowCount:number;}export type BulkExportResponse=ApiResponse<BulkExportResponseData>;export interface BulkUpdateItem{id:string|number;values?:Record<string,unknown>;nulls?:string[];}export interface BulkUpdateRequestBody{updates:BulkUpdateItem[];}export interface BulkUpdateResponseData{message:string;updated:number;}export type BulkUpdateResponse=ApiResponse<BulkUpdateResponseData>;export interface SeedConfigResponseData{table:string;columns:ColumnGeneratorConfig[];maxRows:number;}export type SeedConfigResponse=ApiResponse<SeedConfigResponseData>;export interface SeedGenerateRequestBody{count?:number;plan?:Record<string,ColumnPlan>;}export interface SeedGenerateResponseData{table:string;count:number;sql:string;previewRows?:Record<string,unknown>[];warnings:string[];}export type SeedGenerateResponse=ApiResponse<SeedGenerateResponseData>;export interface SeedInsertRequestBody{count?:number;plan?:Record<string,ColumnPlan>;truncate?:boolean;}export interface SeedInsertResponseData{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs?:number;}export type SeedInsertResponse=ApiResponse<SeedInsertResponseData>;export interface CreateTableRequestBody{name:string;columns:ColumnDef[];}export interface CreateTableResponseData{message:string;sql:string;}export type CreateTableResponse=ApiResponse<CreateTableResponseData>;export interface GenerateCreateTableRequestBody{name:string;columns:ColumnDef[];}export interface GenerateCreateTableResponseData{sql:string;}export type GenerateCreateTableResponse=ApiResponse<GenerateCreateTableResponseData>;export type GetSchemaResponseData=SchemaInfo;export type GetSchemaResponse=ApiResponse<GetSchemaResponseData>;export interface RenameTableRequestBody{name:string;}export interface RenameTableResponseData{message:string;sql:string;table:string;}export type RenameTableResponse=ApiResponse<RenameTableResponseData>;export interface AddColumnRequestBody{column:ColumnDef;}export interface AddColumnResponseData{message:string;sql:string;}export type AddColumnResponse=ApiResponse<AddColumnResponseData>;export interface RenameColumnRequestBody{name:string;}export interface RenameColumnResponseData{message:string;sql:string;}export type RenameColumnResponse=ApiResponse<RenameColumnResponseData>;export interface DropColumnResponseData{message:string;sql:string;}export type DropColumnResponse=ApiResponse<DropColumnResponseData>;export interface DropTableResponseData{message:string;sql:string;}export type DropTableResponse=ApiResponse<DropTableResponseData>;export interface CreateIndexRequestBody{name?:string;columns:string[];unique?:boolean;}export interface CreateIndexResponseData{message:string;sql:string;}export type CreateIndexResponse=ApiResponse<CreateIndexResponseData>;export interface DropIndexResponseData{message:string;sql:string;}export type DropIndexResponse=ApiResponse<DropIndexResponseData>;export interface ExecuteQueryRequestBody{sql:string;}export interface QueryCountResult{kind:'count';count:number;message:string;}export interface QuerySelectResult{kind:'select';columns:string[];rows:Record<string,unknown>[];}export interface QueryWriteResult{kind:'write';changes:number|null;message:string;}export type ExecuteQueryResponseData=QueryCountResult|QuerySelectResult|QueryWriteResult;export type ExecuteQueryResponse=ApiResponse<ExecuteQueryResponseData>;export interface ExportQueryRequestBody{sql:string;format?:'csv'|'json';}export type ListSavedQueriesResponseData=SavedQuery[];export type ListSavedQueriesResponse=ApiResponse<ListSavedQueriesResponseData>;export interface SaveQueryRequestBody{name:string;sql:string;}export interface SaveQueryResponseData{message:string;}export type SaveQueryResponse=ApiResponse<SaveQueryResponseData>;export interface DeleteSavedQueryResponseData{message:string;}export type DeleteSavedQueryResponse=ApiResponse<DeleteSavedQueryResponseData>;export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export type ListDatabasesResponseData=DatabaseRow[];export type ListDatabasesResponse=ApiResponse<ListDatabasesResponseData>;export interface CreateDatabaseRequestBody{name:string;}export interface CreateDatabaseResponseData{id:string;message:string;}export type CreateDatabaseResponse=ApiResponse<CreateDatabaseResponseData>;export interface OpenDatabaseRequestBody{path:string;}export interface OpenDatabaseResponseData{id:string;message:string;}export type OpenDatabaseResponse=ApiResponse<OpenDatabaseResponseData>;export interface DeleteDatabaseResponseData{message:string;}export type DeleteDatabaseResponse=ApiResponse<DeleteDatabaseResponseData>;export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export interface ListFsResponseData{path:string;name:string;parent:string|null;entries:FsEntry[];}export type ListFsResponse=ApiResponse<ListFsResponseData>;
@@ -1 +1 @@
1
- export declare function errorMessage(err:unknown):string;export declare function isPostgresConnectionString(target:unknown):boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare function isInternalTable(name:string):boolean;export declare function encodePk(values:unknown[]):string;export declare function decodePk(encoded:string):string[];export declare function normalizeCell(value:unknown):unknown;export declare function normalizeRow(row:Record<string,unknown>):Record<string,unknown>;export declare function coerceFormValue(raw:unknown,columnType:string,dialect?:'sqlite'|'postgres'):unknown;export declare function formatBytes(bytes:number):string;export declare function truncate(str:string,maxLength:number):string;
1
+ export declare const errorMessage:(err:unknown)=>string;export declare const isPostgresConnectionString:(target:unknown)=>boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare const isInternalTable:(name:string)=>boolean;export declare const encodePk:(values:unknown[])=>string;export declare const decodePk:(encoded:string)=>string[];export declare function normalizeCell(value:unknown):unknown;export declare function normalizeRow(row:Record<string,unknown>):Record<string,unknown>;export declare function coerceFormValue(raw:unknown,columnType:string,dialect?:'sqlite'|'postgres'):unknown;export declare function formatBytes(bytes:number):string;export declare const truncate:(str:string,maxLength:number)=>string;
@@ -3,31 +3,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.errorMessage = errorMessage;
7
- exports.isPostgresConnectionString = isPostgresConnectionString;
6
+ exports.truncate = exports.decodePk = exports.encodePk = exports.isInternalTable = exports.isPostgresConnectionString = exports.errorMessage = void 0;
8
7
  exports.sanitizeConnectionString = sanitizeConnectionString;
9
8
  exports.isPathWithinRoot = isPathWithinRoot;
10
9
  exports.parseFilters = parseFilters;
11
10
  exports.filtersToQS = filtersToQS;
12
- exports.isInternalTable = isInternalTable;
13
- exports.encodePk = encodePk;
14
- exports.decodePk = decodePk;
15
11
  exports.normalizeCell = normalizeCell;
16
12
  exports.normalizeRow = normalizeRow;
17
13
  exports.coerceFormValue = coerceFormValue;
18
14
  exports.formatBytes = formatBytes;
19
- exports.truncate = truncate;
20
15
  const node_fs_1 = require("node:fs");
21
16
  const node_path_1 = __importDefault(require("node:path"));
22
- function errorMessage(err) {
23
- if (err instanceof Error)
24
- return err.message;
25
- return String(err);
26
- }
27
- function isPostgresConnectionString(target) {
28
- return (typeof target === 'string' &&
29
- (target.startsWith('postgres://') || target.startsWith('postgresql://')));
30
- }
17
+ const errorMessage = (err) => (err instanceof Error ? err.message : String(err));
18
+ exports.errorMessage = errorMessage;
19
+ const isPostgresConnectionString = (target) => typeof target === 'string' && /^(postgres|postgresql):\/\//.test(target);
20
+ exports.isPostgresConnectionString = isPostgresConnectionString;
31
21
  function sanitizeConnectionString(conn) {
32
22
  if (!conn || typeof conn !== 'string')
33
23
  return '';
@@ -42,25 +32,19 @@ function sanitizeConnectionString(conn) {
42
32
  return conn.replace(/:([^:@]+)@/, ':****@');
43
33
  }
44
34
  function isPathWithinRoot(root, p) {
45
- if (!root || !p || typeof root !== 'string' || typeof p !== 'string')
35
+ if (!root || !p || typeof root !== 'string' || typeof p !== 'string' || p.includes('\0') || root.includes('\0')) {
46
36
  return false;
47
- if (p.includes('\0') || root.includes('\0'))
48
- return false;
49
- const absRoot = node_path_1.default.resolve(root);
50
- const absPath = node_path_1.default.resolve(p);
51
- let realRoot = absRoot;
52
- let realPath = absPath;
53
- try {
54
- if ((0, node_fs_1.existsSync)(absRoot))
55
- realRoot = (0, node_fs_1.realpathSync)(absRoot);
56
- }
57
- catch { }
58
- try {
59
- if ((0, node_fs_1.existsSync)(absPath))
60
- realPath = (0, node_fs_1.realpathSync)(absPath);
61
37
  }
62
- catch { }
63
- const rel = node_path_1.default.relative(realRoot, realPath);
38
+ const resolveReal = (target) => {
39
+ const abs = node_path_1.default.resolve(target);
40
+ try {
41
+ if ((0, node_fs_1.existsSync)(abs))
42
+ return (0, node_fs_1.realpathSync)(abs);
43
+ }
44
+ catch { }
45
+ return abs;
46
+ };
47
+ const rel = node_path_1.default.relative(resolveReal(root), resolveReal(p));
64
48
  return rel === '' || (!rel.startsWith('..') && !node_path_1.default.isAbsolute(rel));
65
49
  }
66
50
  function parseFilters(raw) {
@@ -104,52 +88,41 @@ function filtersToQS(filters) {
104
88
  if (Array.isArray(v)) {
105
89
  if (v.length)
106
90
  out[k] = v;
107
- continue;
108
91
  }
109
- if (typeof v === 'object') {
92
+ else if (typeof v === 'object') {
110
93
  if (Object.keys(v).length)
111
94
  out[k] = v;
112
- continue;
113
95
  }
114
- if (String(v).trim() !== '')
96
+ else if (String(v).trim() !== '') {
115
97
  out[k] = String(v).trim();
116
- }
117
- const keys = Object.keys(out);
118
- if (!keys.length)
119
- return '';
120
- return 'f=' + encodeURIComponent(JSON.stringify(out));
121
- }
122
- function isInternalTable(name) {
123
- return name.startsWith('_');
124
- }
125
- function encodePk(values) {
126
- return values.map((v) => encodeURIComponent(v == null ? '' : String(v))).join(',');
127
- }
128
- function decodePk(encoded) {
129
- return String(encoded)
130
- .split(',')
131
- .map((s) => {
132
- try {
133
- return decodeURIComponent(s);
134
- }
135
- catch {
136
- return s;
137
98
  }
138
- });
99
+ }
100
+ return Object.keys(out).length ? `f=${encodeURIComponent(JSON.stringify(out))}` : '';
139
101
  }
102
+ const isInternalTable = (name) => name.startsWith('_');
103
+ exports.isInternalTable = isInternalTable;
104
+ const encodePk = (values) => values.map((v) => encodeURIComponent(v == null ? '' : String(v))).join(',');
105
+ exports.encodePk = encodePk;
106
+ const decodePk = (encoded) => String(encoded)
107
+ .split(',')
108
+ .map((s) => {
109
+ try {
110
+ return decodeURIComponent(s);
111
+ }
112
+ catch {
113
+ return s;
114
+ }
115
+ });
116
+ exports.decodePk = decodePk;
140
117
  function normalizeCell(value) {
141
118
  if (value instanceof Uint8Array || Buffer.isBuffer(value)) {
142
119
  return `0x${Buffer.from(value).toString('hex')}`;
143
120
  }
144
- if (value instanceof Date) {
121
+ if (value instanceof Date)
145
122
  return value.toISOString();
146
- }
147
- if (typeof value === 'object' && value !== null) {
123
+ if (typeof value === 'object' && value !== null)
148
124
  return JSON.stringify(value);
149
- }
150
- if (value === undefined)
151
- return null;
152
- return value;
125
+ return value ?? null;
153
126
  }
154
127
  function normalizeRow(row) {
155
128
  const out = {};
@@ -158,9 +131,9 @@ function normalizeRow(row) {
158
131
  return out;
159
132
  }
160
133
  function coerceFormValue(raw, columnType, dialect = 'sqlite') {
161
- const type = (columnType || '').toUpperCase();
162
134
  if (raw === undefined || raw === null)
163
135
  return null;
136
+ const type = (columnType || '').toUpperCase();
164
137
  if (type === 'BOOLEAN' || type === 'BOOL') {
165
138
  const s = String(raw).trim().toLowerCase();
166
139
  if (s === '')
@@ -185,30 +158,20 @@ function coerceFormValue(raw, columnType, dialect = 'sqlite') {
185
158
  }
186
159
  if (type === 'BLOB' || type === 'BYTEA') {
187
160
  const s = String(raw);
188
- if (/^0x[0-9a-f]*$/i.test(s))
189
- return Buffer.from(s.slice(2), 'hex');
190
- if (/^\\x[0-9a-f]*$/i.test(s))
191
- return Buffer.from(s.slice(2), 'hex');
192
- return Buffer.from(s, 'utf8');
161
+ return /^(0x|\\x)[0-9a-f]*$/i.test(s) ? Buffer.from(s.slice(2), 'hex') : Buffer.from(s, 'utf8');
193
162
  }
194
163
  if (type === 'JSON' || type === 'JSONB') {
195
164
  const s = String(raw).trim();
196
- if (s === '')
197
- return null;
198
- return s;
165
+ return s === '' ? null : s;
199
166
  }
200
167
  if (type.endsWith('[]') || type.startsWith('_') || type.includes('ARRAY')) {
201
168
  if (Array.isArray(raw))
202
169
  return raw;
203
170
  const s = String(raw).trim();
204
- if (s === '')
205
- return null;
206
- return s;
171
+ return s === '' ? null : s;
207
172
  }
208
173
  const s = String(raw);
209
- if (s === '')
210
- return null;
211
- return s;
174
+ return s === '' ? null : s;
212
175
  }
213
176
  function formatBytes(bytes) {
214
177
  if (bytes === 0)
@@ -218,8 +181,5 @@ function formatBytes(bytes) {
218
181
  const i = Math.floor(Math.log(bytes) / Math.log(k));
219
182
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
220
183
  }
221
- function truncate(str, maxLength) {
222
- if (!str || str.length <= maxLength)
223
- return str;
224
- return `${str.slice(0, maxLength)}…`;
225
- }
184
+ const truncate = (str, maxLength) => !str || str.length <= maxLength ? str : `${str.slice(0, maxLength)}…`;
185
+ exports.truncate = truncate;
@@ -1 +1 @@
1
- export declare function csvEscape(value:unknown):string;export declare function toCsv(rows:Record<string,unknown>[],columns:string[]):string;export declare function toJson(rows:Record<string,unknown>[]):string;export declare function parseCsv(text:string):string[][];
1
+ export declare function csvEscape(value:unknown):string;export declare const toCsv:(rows:Record<string,unknown>[],columns:string[])=>string;export declare const toJson:(rows:Record<string,unknown>[])=>string;export declare function parseCsv(text:string):string[][];
package/dist/utils/csv.js CHANGED
@@ -1,23 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toJson = exports.toCsv = void 0;
3
4
  exports.csvEscape = csvEscape;
4
- exports.toCsv = toCsv;
5
- exports.toJson = toJson;
6
5
  exports.parseCsv = parseCsv;
7
6
  function csvEscape(value) {
8
- const s = value === null || value === undefined ? '' : String(value);
9
- if (/[",\r\n]/.test(s))
10
- return `"${s.replace(/"/g, '""')}"`;
11
- return s;
12
- }
13
- function toCsv(rows, columns) {
14
- const header = columns.map(csvEscape).join(',');
15
- const body = rows.map((row) => columns.map((c) => csvEscape(row[c])).join(','));
16
- return [header, ...body].join('\r\n');
17
- }
18
- function toJson(rows) {
19
- return JSON.stringify(rows, null, 2);
7
+ const s = value == null ? '' : String(value);
8
+ return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
20
9
  }
10
+ const toCsv = (rows, columns) => [
11
+ columns.map(csvEscape).join(','),
12
+ ...rows.map((row) => columns.map((c) => csvEscape(row[c])).join(',')),
13
+ ].join('\r\n');
14
+ exports.toCsv = toCsv;
15
+ const toJson = (rows) => JSON.stringify(rows, null, 2);
16
+ exports.toJson = toJson;
21
17
  function parseCsv(text) {
22
18
  const rows = [];
23
19
  let row = [];
@@ -54,9 +50,7 @@ function parseCsv(text) {
54
50
  rows.push(row);
55
51
  row = [];
56
52
  }
57
- else if (ch === '\r') {
58
- }
59
- else {
53
+ else if (ch !== '\r') {
60
54
  field += ch;
61
55
  }
62
56
  }
@@ -6,60 +6,66 @@ exports.analyzeBlob = analyzeBlob;
6
6
  exports.isJsonString = isJsonString;
7
7
  exports.formatJsonSafely = formatJsonSafely;
8
8
  const common_js_1 = require("./common.js");
9
+ const mimeResult = (mime, ext, flags = {}) => ({
10
+ mime,
11
+ ext,
12
+ isImage: false,
13
+ isText: false,
14
+ isPdf: false,
15
+ isAudio: false,
16
+ isVideo: false,
17
+ ...flags,
18
+ });
9
19
  function sniffMimeType(buf) {
10
20
  const len = buf.length;
11
- if (len === 0) {
12
- return { mime: 'application/octet-stream', ext: 'bin', isImage: false, isText: false, isPdf: false, isAudio: false, isVideo: false };
13
- }
21
+ if (len === 0)
22
+ return mimeResult('application/octet-stream', 'bin');
14
23
  if (len >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47 && buf[4] === 0x0d && buf[5] === 0x0a && buf[6] === 0x1a && buf[7] === 0x0a) {
15
- return { mime: 'image/png', ext: 'png', isImage: true, isText: false, isPdf: false, isAudio: false, isVideo: false };
24
+ return mimeResult('image/png', 'png', { isImage: true });
16
25
  }
17
26
  if (len >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {
18
- return { mime: 'image/jpeg', ext: 'jpg', isImage: true, isText: false, isPdf: false, isAudio: false, isVideo: false };
27
+ return mimeResult('image/jpeg', 'jpg', { isImage: true });
19
28
  }
20
29
  if (len >= 6 && buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38 && (buf[4] === 0x37 || buf[4] === 0x39) && buf[5] === 0x61) {
21
- return { mime: 'image/gif', ext: 'gif', isImage: true, isText: false, isPdf: false, isAudio: false, isVideo: false };
30
+ return mimeResult('image/gif', 'gif', { isImage: true });
22
31
  }
23
32
  if (len >= 12 && buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) {
24
- return { mime: 'image/webp', ext: 'webp', isImage: true, isText: false, isPdf: false, isAudio: false, isVideo: false };
33
+ return mimeResult('image/webp', 'webp', { isImage: true });
25
34
  }
26
35
  if (len >= 2 && buf[0] === 0x42 && buf[1] === 0x4d) {
27
- return { mime: 'image/bmp', ext: 'bmp', isImage: true, isText: false, isPdf: false, isAudio: false, isVideo: false };
36
+ return mimeResult('image/bmp', 'bmp', { isImage: true });
28
37
  }
29
38
  if (len >= 4 && buf[0] === 0x00 && buf[1] === 0x00 && buf[2] === 0x01 && buf[3] === 0x00) {
30
- return { mime: 'image/x-icon', ext: 'ico', isImage: true, isText: false, isPdf: false, isAudio: false, isVideo: false };
39
+ return mimeResult('image/x-icon', 'ico', { isImage: true });
31
40
  }
32
41
  if (len >= 4 && buf[0] === 0x25 && buf[1] === 0x50 && buf[2] === 0x44 && buf[3] === 0x46) {
33
- return { mime: 'application/pdf', ext: 'pdf', isImage: false, isText: false, isPdf: true, isAudio: false, isVideo: false };
34
- }
35
- if (len >= 3 && buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) {
36
- return { mime: 'audio/mpeg', ext: 'mp3', isImage: false, isText: false, isPdf: false, isAudio: true, isVideo: false };
42
+ return mimeResult('application/pdf', 'pdf', { isPdf: true });
37
43
  }
38
- if (len >= 2 && buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0) {
39
- return { mime: 'audio/mpeg', ext: 'mp3', isImage: false, isText: false, isPdf: false, isAudio: true, isVideo: false };
44
+ if ((len >= 3 && buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) || (len >= 2 && buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0)) {
45
+ return mimeResult('audio/mpeg', 'mp3', { isAudio: true });
40
46
  }
41
47
  if (len >= 12 && buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x57 && buf[9] === 0x41 && buf[10] === 0x56 && buf[11] === 0x45) {
42
- return { mime: 'audio/wav', ext: 'wav', isImage: false, isText: false, isPdf: false, isAudio: true, isVideo: false };
48
+ return mimeResult('audio/wav', 'wav', { isAudio: true });
43
49
  }
44
50
  if (len >= 4 && buf[0] === 0x4f && buf[1] === 0x67 && buf[2] === 0x67 && buf[3] === 0x53) {
45
- return { mime: 'audio/ogg', ext: 'ogg', isImage: false, isText: false, isPdf: false, isAudio: true, isVideo: false };
51
+ return mimeResult('audio/ogg', 'ogg', { isAudio: true });
46
52
  }
47
53
  if (len >= 8 && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) {
48
- return { mime: 'video/mp4', ext: 'mp4', isImage: false, isText: false, isPdf: false, isAudio: false, isVideo: true };
54
+ return mimeResult('video/mp4', 'mp4', { isVideo: true });
49
55
  }
50
56
  if (len >= 4 && buf[0] === 0x1a && buf[1] === 0x45 && buf[2] === 0xdf && buf[3] === 0xa3) {
51
- return { mime: 'video/webm', ext: 'webm', isImage: false, isText: false, isPdf: false, isAudio: false, isVideo: true };
57
+ return mimeResult('video/webm', 'webm', { isVideo: true });
52
58
  }
53
59
  const sampleLen = Math.min(len, 1024);
54
60
  const sample = Buffer.from(buf.subarray(0, sampleLen)).toString('utf8');
55
61
  const trimmed = sample.trim();
56
62
  if (trimmed.startsWith('<svg') || (trimmed.startsWith('<?xml') && trimmed.includes('<svg'))) {
57
- return { mime: 'image/svg+xml', ext: 'svg', isImage: true, isText: true, isPdf: false, isAudio: false, isVideo: false };
63
+ return mimeResult('image/svg+xml', 'svg', { isImage: true, isText: true });
58
64
  }
59
65
  if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
60
66
  try {
61
67
  JSON.parse(sample);
62
- return { mime: 'application/json', ext: 'json', isImage: false, isText: true, isPdf: false, isAudio: false, isVideo: false };
68
+ return mimeResult('application/json', 'json', { isText: true });
63
69
  }
64
70
  catch {
65
71
  }
@@ -72,15 +78,16 @@ function sniffMimeType(buf) {
72
78
  }
73
79
  }
74
80
  if (nonPrintable === 0) {
75
- return { mime: 'text/plain; charset=utf-8', ext: 'txt', isImage: false, isText: true, isPdf: false, isAudio: false, isVideo: false };
81
+ return mimeResult('text/plain; charset=utf-8', 'txt', { isText: true });
76
82
  }
77
- return { mime: 'application/octet-stream', ext: 'bin', isImage: false, isText: false, isPdf: false, isAudio: false, isVideo: false };
83
+ return mimeResult('application/octet-stream', 'bin');
78
84
  }
79
85
  function generateHexDump(buf, maxBytes = 4096, bytesPerLine = 16) {
80
86
  const totalBytes = buf.length;
81
87
  const limit = Math.min(totalBytes, maxBytes);
82
88
  const truncated = totalBytes > maxBytes;
83
89
  const lines = [];
90
+ const mid = Math.floor(bytesPerLine / 2);
84
91
  for (let i = 0; i < limit; i += bytesPerLine) {
85
92
  const offset = i.toString(16).padStart(8, '0');
86
93
  const chunk = buf.subarray(i, Math.min(i + bytesPerLine, limit));
@@ -90,18 +97,16 @@ function generateHexDump(buf, maxBytes = 4096, bytesPerLine = 16) {
90
97
  if (j < chunk.length) {
91
98
  const byte = chunk[j];
92
99
  hexParts.push(byte.toString(16).padStart(2, '0'));
93
- asciiPart += (byte >= 32 && byte <= 126) ? String.fromCharCode(byte) : '.';
100
+ asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.';
94
101
  }
95
102
  else {
96
103
  hexParts.push(' ');
97
104
  asciiPart += ' ';
98
105
  }
99
106
  }
100
- const mid = Math.floor(bytesPerLine / 2);
101
- const hexFormatted = hexParts.slice(0, mid).join(' ') + ' ' + hexParts.slice(mid).join(' ');
102
107
  lines.push({
103
108
  offset,
104
- hex: hexFormatted,
109
+ hex: `${hexParts.slice(0, mid).join(' ')} ${hexParts.slice(mid).join(' ')}`,
105
110
  ascii: asciiPart,
106
111
  });
107
112
  }
@@ -110,12 +115,7 @@ function generateHexDump(buf, maxBytes = 4096, bytesPerLine = 16) {
110
115
  function analyzeBlob(data) {
111
116
  let buf;
112
117
  if (typeof data === 'string') {
113
- if (data.startsWith('0x') || data.startsWith('0X')) {
114
- buf = Buffer.from(data.slice(2), 'hex');
115
- }
116
- else {
117
- buf = Buffer.from(data, 'utf8');
118
- }
118
+ buf = /^0x/i.test(data) ? Buffer.from(data.slice(2), 'hex') : Buffer.from(data, 'utf8');
119
119
  }
120
120
  else {
121
121
  buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
@@ -144,38 +144,24 @@ function isJsonString(value) {
144
144
  if (typeof value !== 'string')
145
145
  return false;
146
146
  const trimmed = value.trim();
147
- if (!trimmed)
147
+ if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('[')))
148
+ return false;
149
+ try {
150
+ JSON.parse(trimmed);
151
+ return true;
152
+ }
153
+ catch {
148
154
  return false;
149
- if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
150
- try {
151
- JSON.parse(trimmed);
152
- return true;
153
- }
154
- catch {
155
- return false;
156
- }
157
155
  }
158
- return false;
159
156
  }
160
157
  function formatJsonSafely(value, indent = 2) {
161
- if (value === null || value === undefined)
158
+ if (value == null)
162
159
  return null;
163
- if (typeof value === 'object') {
164
- try {
165
- return JSON.stringify(value, null, indent);
166
- }
167
- catch {
168
- return null;
169
- }
160
+ try {
161
+ const obj = typeof value === 'string' ? JSON.parse(value) : value;
162
+ return typeof obj === 'object' && obj !== null ? JSON.stringify(obj, null, indent) : null;
170
163
  }
171
- if (typeof value === 'string') {
172
- try {
173
- const parsed = JSON.parse(value);
174
- return JSON.stringify(parsed, null, indent);
175
- }
176
- catch {
177
- return null;
178
- }
164
+ catch {
165
+ return null;
179
166
  }
180
- return null;
181
167
  }
@@ -45,6 +45,9 @@ exports.ICONS = {
45
45
  spinner: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>',
46
46
  terminal: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>',
47
47
  format: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h10M4 18h14"/></svg>',
48
+ columns: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 4.5v15m6-15v15m-10.875 0h15.75c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125H4.125C3.504 4.5 3 5.004 3 5.625v12.75c0 .621.504 1.125 1.125 1.125z" /></svg>',
49
+ pin: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg>',
50
+ grip: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="9" cy="7" r="1.2" fill="currentColor"/><circle cx="15" cy="7" r="1.2" fill="currentColor"/><circle cx="9" cy="12" r="1.2" fill="currentColor"/><circle cx="15" cy="12" r="1.2" fill="currentColor"/><circle cx="9" cy="17" r="1.2" fill="currentColor"/><circle cx="15" cy="17" r="1.2" fill="currentColor"/></svg>',
48
51
  check: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>',
49
52
  x: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>',
50
53
  warning: '<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>',
@@ -70,12 +73,9 @@ exports.ICONS = {
70
73
  };
71
74
  function renderIcon(name, customClass) {
72
75
  const svg = exports.ICONS[name];
73
- if (!svg)
74
- return '';
75
- if (!customClass)
76
- return svg;
77
- if (svg.includes('class="')) {
78
- return svg.replace(/class="[^"]*"/, `class="${customClass}"`);
79
- }
80
- return svg.replace('<svg', `<svg class="${customClass}"`);
76
+ if (!svg || !customClass)
77
+ return svg || '';
78
+ return svg.includes('class="')
79
+ ? svg.replace(/class="[^"]*"/, `class="${customClass}"`)
80
+ : svg.replace('<svg', `<svg class="${customClass}"`);
81
81
  }