ts-prorm-orm 1.1.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/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for the ORM
|
|
4
|
+
* Includes string converters, pluralization, object utilities, UUID generation, and SQL formatting
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.camelToSnake = camelToSnake;
|
|
22
|
+
exports.camelize = camelize;
|
|
23
|
+
exports.snakeToCamel = snakeToCamel;
|
|
24
|
+
exports.snakeCase = snakeCase;
|
|
25
|
+
exports.camelToKebab = camelToKebab;
|
|
26
|
+
exports.kebabToCamel = kebabToCamel;
|
|
27
|
+
exports.pluralize = pluralize;
|
|
28
|
+
exports.singularize = singularize;
|
|
29
|
+
exports.cloneDeep = cloneDeep;
|
|
30
|
+
exports.merge = merge;
|
|
31
|
+
exports.pick = pick;
|
|
32
|
+
exports.omit = omit;
|
|
33
|
+
exports.generateUUID = generateUUID;
|
|
34
|
+
exports.generateShortUUID = generateShortUUID;
|
|
35
|
+
exports.isValidUUID = isValidUUID;
|
|
36
|
+
exports.formatValues = formatValues;
|
|
37
|
+
exports.formatValue = formatValue;
|
|
38
|
+
exports.escapeString = escapeString;
|
|
39
|
+
exports.escapeId = escapeId;
|
|
40
|
+
exports.formatWhere = formatWhere;
|
|
41
|
+
exports.isEmpty = isEmpty;
|
|
42
|
+
exports.coalesce = coalesce;
|
|
43
|
+
exports.escapeLike = escapeLike;
|
|
44
|
+
exports.addTicks = addTicks;
|
|
45
|
+
exports.removeTicks = removeTicks;
|
|
46
|
+
exports.toDefaultValue = toDefaultValue;
|
|
47
|
+
exports.formatDefaultValue = formatDefaultValue;
|
|
48
|
+
exports.spliceStr = spliceStr;
|
|
49
|
+
exports.formatDateString = formatDateString;
|
|
50
|
+
exports.randomString = randomString;
|
|
51
|
+
exports.randomAlphaNumeric = randomAlphaNumeric;
|
|
52
|
+
exports.randomNumeric = randomNumeric;
|
|
53
|
+
exports.randomHex = randomHex;
|
|
54
|
+
exports.timestamp = timestamp;
|
|
55
|
+
exports.nowDate = nowDate;
|
|
56
|
+
exports.defaultValue = defaultValue;
|
|
57
|
+
exports.defaultIfEmpty = defaultIfEmpty;
|
|
58
|
+
exports.retry = retry;
|
|
59
|
+
exports.retryWithBackoff = retryWithBackoff;
|
|
60
|
+
exports.unique = unique;
|
|
61
|
+
exports.flatten = flatten;
|
|
62
|
+
exports.groupBy = groupBy;
|
|
63
|
+
exports.sortBy = sortBy;
|
|
64
|
+
exports.sum = sum;
|
|
65
|
+
exports.underscore = underscore;
|
|
66
|
+
exports.includes = includes;
|
|
67
|
+
exports.startsWith = startsWith;
|
|
68
|
+
exports.endsWith = endsWith;
|
|
69
|
+
exports.repeat = repeat;
|
|
70
|
+
exports.reverse = reverse;
|
|
71
|
+
exports.clone = clone;
|
|
72
|
+
exports.get = get;
|
|
73
|
+
exports.set = set;
|
|
74
|
+
exports.has = has;
|
|
75
|
+
exports.isString = isString;
|
|
76
|
+
exports.isNumber = isNumber;
|
|
77
|
+
exports.isBoolean = isBoolean;
|
|
78
|
+
exports.isDate = isDate;
|
|
79
|
+
exports.isArray = isArray;
|
|
80
|
+
exports.isObject = isObject;
|
|
81
|
+
exports.formatCurrency = formatCurrency;
|
|
82
|
+
exports.format = format;
|
|
83
|
+
const string_1 = require("./string");
|
|
84
|
+
// ==================== String Case Converters ====================
|
|
85
|
+
/**
|
|
86
|
+
* Convert camelCase to snake_case
|
|
87
|
+
*/
|
|
88
|
+
function camelToSnake(str) {
|
|
89
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Convert snake_case to camelCase
|
|
93
|
+
* Alias for snakeToCamel
|
|
94
|
+
*/
|
|
95
|
+
function camelize(str) {
|
|
96
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Convert snake_case to camelCase
|
|
100
|
+
*/
|
|
101
|
+
function snakeToCamel(str) {
|
|
102
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Convert camelCase to snake_case
|
|
106
|
+
* Alias for camelToSnake
|
|
107
|
+
*/
|
|
108
|
+
function snakeCase(str) {
|
|
109
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Convert camelCase to kebab-case
|
|
113
|
+
*/
|
|
114
|
+
function camelToKebab(str) {
|
|
115
|
+
return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Convert kebab-case to camelCase
|
|
119
|
+
*/
|
|
120
|
+
function kebabToCamel(str) {
|
|
121
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
122
|
+
}
|
|
123
|
+
// ==================== Pluralization ====================
|
|
124
|
+
// Common irregular plurals
|
|
125
|
+
const irregularPlurals = {
|
|
126
|
+
person: 'people',
|
|
127
|
+
man: 'men',
|
|
128
|
+
woman: 'women',
|
|
129
|
+
child: 'children',
|
|
130
|
+
tooth: 'teeth',
|
|
131
|
+
foot: 'feet',
|
|
132
|
+
mouse: 'mice',
|
|
133
|
+
goose: 'geese',
|
|
134
|
+
ox: 'oxen',
|
|
135
|
+
quiz: 'quizzes',
|
|
136
|
+
matrix: 'matrices',
|
|
137
|
+
vertex: 'vertices',
|
|
138
|
+
index: 'indices',
|
|
139
|
+
appendix: 'appendices',
|
|
140
|
+
bacterium: 'bacteria',
|
|
141
|
+
criterion: 'criteria',
|
|
142
|
+
phenomenon: 'phenomena',
|
|
143
|
+
analysis: 'analyses',
|
|
144
|
+
diagnosis: 'diagnoses',
|
|
145
|
+
hypothesis: 'hypotheses',
|
|
146
|
+
synthesis: 'syntheses',
|
|
147
|
+
crisis: 'crises',
|
|
148
|
+
basis: 'bases',
|
|
149
|
+
};
|
|
150
|
+
// Words that are the same in singular and plural
|
|
151
|
+
const uncountableWords = new Set([
|
|
152
|
+
'equipment',
|
|
153
|
+
'information',
|
|
154
|
+
'rice',
|
|
155
|
+
'money',
|
|
156
|
+
'species',
|
|
157
|
+
'series',
|
|
158
|
+
'fish',
|
|
159
|
+
'sheep',
|
|
160
|
+
'deer',
|
|
161
|
+
'moose',
|
|
162
|
+
'aircraft',
|
|
163
|
+
'offspring',
|
|
164
|
+
'news',
|
|
165
|
+
'media',
|
|
166
|
+
'status',
|
|
167
|
+
]);
|
|
168
|
+
/**
|
|
169
|
+
* Pluralize a word
|
|
170
|
+
*/
|
|
171
|
+
function pluralize(word, count) {
|
|
172
|
+
// If count is 1, return singular form
|
|
173
|
+
if (count !== undefined && count === 1) {
|
|
174
|
+
return singularize(word);
|
|
175
|
+
}
|
|
176
|
+
// Handle uncountable words
|
|
177
|
+
const lowerWord = word.toLowerCase();
|
|
178
|
+
if (uncountableWords.has(lowerWord)) {
|
|
179
|
+
return word;
|
|
180
|
+
}
|
|
181
|
+
// Handle irregular plurals
|
|
182
|
+
if (irregularPlurals[lowerWord]) {
|
|
183
|
+
return irregularPlurals[lowerWord];
|
|
184
|
+
}
|
|
185
|
+
// Handle words ending in 'y' (consonant + y -> ies)
|
|
186
|
+
if (lowerWord.endsWith('y') && !isVowel(lowerWord[lowerWord.length - 2])) {
|
|
187
|
+
return word.slice(0, -1) + 'ies';
|
|
188
|
+
}
|
|
189
|
+
// Handle words ending in 'f' or 'fe' (fe -> ves)
|
|
190
|
+
if (lowerWord.endsWith('fe')) {
|
|
191
|
+
return word.slice(0, -2) + 'ves';
|
|
192
|
+
}
|
|
193
|
+
if (lowerWord.endsWith('f')) {
|
|
194
|
+
return word.slice(0, -1) + 'ves';
|
|
195
|
+
}
|
|
196
|
+
// Handle words ending in 'o' (sometimes -> es)
|
|
197
|
+
if (lowerWord.endsWith('o') && !isVowel(lowerWord[lowerWord.length - 2])) {
|
|
198
|
+
// Common exceptions
|
|
199
|
+
const exceptions = ['photo', 'piano', 'halo', 'logo', 'mango', 'cargo', 'hero', ' veto'];
|
|
200
|
+
if (exceptions.some((ex) => lowerWord === ex)) {
|
|
201
|
+
return word + 'es';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Handle words ending in 's', 'ss', 'sh', 'ch', 'x', 'z'
|
|
205
|
+
const esEndings = ['s', 'ss', 'sh', 'ch', 'x', 'z', 'o'];
|
|
206
|
+
if (esEndings.some((ending) => lowerWord.endsWith(ending))) {
|
|
207
|
+
return word + 'es';
|
|
208
|
+
}
|
|
209
|
+
// Default: add 's'
|
|
210
|
+
return word + 's';
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Singularize a word
|
|
214
|
+
*/
|
|
215
|
+
function singularize(word) {
|
|
216
|
+
const lowerWord = word.toLowerCase();
|
|
217
|
+
// Handle uncountable words
|
|
218
|
+
if (uncountableWords.has(lowerWord)) {
|
|
219
|
+
return word;
|
|
220
|
+
}
|
|
221
|
+
// Handle irregular plurals (reverse lookup)
|
|
222
|
+
for (const [singular, plural] of Object.entries(irregularPlurals)) {
|
|
223
|
+
if (plural === lowerWord) {
|
|
224
|
+
// Preserve original case
|
|
225
|
+
if (word[0] === word[0].toUpperCase()) {
|
|
226
|
+
return singular.charAt(0).toUpperCase() + singular.slice(1);
|
|
227
|
+
}
|
|
228
|
+
return singular;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Handle words ending in 'ies'
|
|
232
|
+
if (lowerWord.endsWith('ies') && lowerWord.length > 3) {
|
|
233
|
+
return word.slice(0, -3) + 'y';
|
|
234
|
+
}
|
|
235
|
+
// Handle words ending in 'ves'
|
|
236
|
+
if (lowerWord.endsWith('ves')) {
|
|
237
|
+
if (lowerWord.endsWith('lves')) {
|
|
238
|
+
return word.slice(0, -3) + 'fe';
|
|
239
|
+
}
|
|
240
|
+
if (lowerWord.endsWith('ives')) {
|
|
241
|
+
return word.slice(0, -3) + 'fe';
|
|
242
|
+
}
|
|
243
|
+
if (lowerWord.endsWith('oves')) {
|
|
244
|
+
return word.slice(0, -3) + 'fe';
|
|
245
|
+
}
|
|
246
|
+
return word.slice(0, -3) + 'f';
|
|
247
|
+
}
|
|
248
|
+
// Handle words ending in 'es'
|
|
249
|
+
if (lowerWord.endsWith('es') && lowerWord.length > 2) {
|
|
250
|
+
// Check for words ending in 'ses', 'shes', 'ches', 'xes', 'zes'
|
|
251
|
+
if (lowerWord.length > 3 && /[sz]es$/.test(lowerWord)) {
|
|
252
|
+
return word.slice(0, -2);
|
|
253
|
+
}
|
|
254
|
+
// Check for words like 'heroes', 'vetoes'
|
|
255
|
+
if (/[aeiou]oes$/.test(lowerWord)) {
|
|
256
|
+
return word.slice(0, -2);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Handle words ending in 's' (but not 'ss')
|
|
260
|
+
if (lowerWord.endsWith('s') && !lowerWord.endsWith('ss') && lowerWord.length > 1) {
|
|
261
|
+
return word.slice(0, -1);
|
|
262
|
+
}
|
|
263
|
+
return word;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Helper function to check if a character is a vowel
|
|
267
|
+
*/
|
|
268
|
+
function isVowel(char) {
|
|
269
|
+
return 'aeiou'.includes(char.toLowerCase());
|
|
270
|
+
}
|
|
271
|
+
// ==================== Object Utilities ====================
|
|
272
|
+
/**
|
|
273
|
+
* Deep clone an object
|
|
274
|
+
*/
|
|
275
|
+
function cloneDeep(obj) {
|
|
276
|
+
if (obj === null || typeof obj !== 'object') {
|
|
277
|
+
return obj;
|
|
278
|
+
}
|
|
279
|
+
if (obj instanceof Date) {
|
|
280
|
+
return new Date(obj.getTime());
|
|
281
|
+
}
|
|
282
|
+
if (obj instanceof Array) {
|
|
283
|
+
return obj.map((item) => cloneDeep(item));
|
|
284
|
+
}
|
|
285
|
+
if (obj instanceof Set) {
|
|
286
|
+
return new Set(Array.from(obj).map((item) => cloneDeep(item)));
|
|
287
|
+
}
|
|
288
|
+
if (obj instanceof Map) {
|
|
289
|
+
return new Map(Array.from(obj.entries()).map(([key, value]) => [cloneDeep(key), cloneDeep(value)]));
|
|
290
|
+
}
|
|
291
|
+
if (typeof obj === 'object') {
|
|
292
|
+
const cloned = {};
|
|
293
|
+
for (const key of Object.keys(obj)) {
|
|
294
|
+
cloned[key] = cloneDeep(obj[key]);
|
|
295
|
+
}
|
|
296
|
+
return cloned;
|
|
297
|
+
}
|
|
298
|
+
return obj;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Deep merge objects
|
|
302
|
+
*/
|
|
303
|
+
function merge(target, ...sources) {
|
|
304
|
+
if (!sources.length) {
|
|
305
|
+
return target;
|
|
306
|
+
}
|
|
307
|
+
const source = sources.shift();
|
|
308
|
+
if (isPlainObject(target) && isPlainObject(source)) {
|
|
309
|
+
const sourceObj = source;
|
|
310
|
+
const sourceKeys = Object.keys(sourceObj);
|
|
311
|
+
for (const key of sourceKeys) {
|
|
312
|
+
const sourceValue = sourceObj[key];
|
|
313
|
+
if (isPlainObject(sourceValue)) {
|
|
314
|
+
if (!target[key]) {
|
|
315
|
+
Object.assign(target, { [key]: {} });
|
|
316
|
+
}
|
|
317
|
+
merge(target[key], sourceValue);
|
|
318
|
+
}
|
|
319
|
+
else if (sourceValue !== undefined) {
|
|
320
|
+
Object.assign(target, { [key]: sourceValue });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return merge(target, ...sources);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Check if value is a plain object
|
|
328
|
+
*/
|
|
329
|
+
function isPlainObject(item) {
|
|
330
|
+
return item && typeof item === 'object' && !Array.isArray(item);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Pick properties from an object
|
|
334
|
+
*/
|
|
335
|
+
function pick(obj, keys) {
|
|
336
|
+
const result = {};
|
|
337
|
+
for (const key of keys) {
|
|
338
|
+
if (key in obj) {
|
|
339
|
+
result[key] = obj[key];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Omit properties from an object
|
|
346
|
+
*/
|
|
347
|
+
function omit(obj, keys) {
|
|
348
|
+
const result = { ...obj };
|
|
349
|
+
for (const key of keys) {
|
|
350
|
+
delete result[key];
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
// ==================== UUID Generation ====================
|
|
355
|
+
/**
|
|
356
|
+
* Generate a UUID v4
|
|
357
|
+
*/
|
|
358
|
+
function generateUUID() {
|
|
359
|
+
// Use crypto.randomUUID if available
|
|
360
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
361
|
+
return crypto.randomUUID();
|
|
362
|
+
}
|
|
363
|
+
// Fallback implementation
|
|
364
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
365
|
+
const r = (Math.random() * 16) | 0;
|
|
366
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
367
|
+
return v.toString(16);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Generate a short UUID (8 characters)
|
|
372
|
+
*/
|
|
373
|
+
function generateShortUUID() {
|
|
374
|
+
return generateUUID().replace(/-/g, '').substring(0, 8);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Validate UUID format
|
|
378
|
+
*/
|
|
379
|
+
function isValidUUID(uuid) {
|
|
380
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
381
|
+
return uuidRegex.test(uuid);
|
|
382
|
+
}
|
|
383
|
+
// ==================== SQL Formatting ====================
|
|
384
|
+
/**
|
|
385
|
+
* Format values for SQL query
|
|
386
|
+
*/
|
|
387
|
+
function formatValues(values) {
|
|
388
|
+
const formatted = {};
|
|
389
|
+
for (const [key, value] of Object.entries(values)) {
|
|
390
|
+
formatted[key] = formatValue(value);
|
|
391
|
+
}
|
|
392
|
+
return formatted;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Format a single value for SQL
|
|
396
|
+
*/
|
|
397
|
+
function formatValue(value) {
|
|
398
|
+
if (value === null || value === undefined) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
if (value instanceof Date) {
|
|
402
|
+
return value.toISOString();
|
|
403
|
+
}
|
|
404
|
+
if (Array.isArray(value)) {
|
|
405
|
+
return value.map((item) => formatValue(item));
|
|
406
|
+
}
|
|
407
|
+
if (typeof value === 'object') {
|
|
408
|
+
// Handle special objects
|
|
409
|
+
if (value instanceof Set) {
|
|
410
|
+
return Array.from(value).map((item) => formatValue(item));
|
|
411
|
+
}
|
|
412
|
+
if (value instanceof Map) {
|
|
413
|
+
const obj = {};
|
|
414
|
+
value.forEach((v, k) => {
|
|
415
|
+
obj[k] = formatValue(v);
|
|
416
|
+
});
|
|
417
|
+
return JSON.stringify(obj);
|
|
418
|
+
}
|
|
419
|
+
return JSON.stringify(value);
|
|
420
|
+
}
|
|
421
|
+
return value;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Escape a string for SQL
|
|
425
|
+
*/
|
|
426
|
+
function escapeString(value) {
|
|
427
|
+
if (typeof value !== 'string') {
|
|
428
|
+
return String(value);
|
|
429
|
+
}
|
|
430
|
+
// Escape single quotes by doubling them
|
|
431
|
+
return value.replace(/'/g, "''");
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Escape an identifier (table/column name)
|
|
435
|
+
*/
|
|
436
|
+
function escapeId(identifier) {
|
|
437
|
+
// Escape backticks and wrap in backticks
|
|
438
|
+
return `\`${identifier.replace(/`/g, '``')}\``;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Format a where condition for SQL
|
|
442
|
+
*/
|
|
443
|
+
function formatWhere(where) {
|
|
444
|
+
const conditions = [];
|
|
445
|
+
for (const [key, value] of Object.entries(where)) {
|
|
446
|
+
if (key.startsWith('$')) {
|
|
447
|
+
// Logical operators
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (typeof value === 'object' && value !== null) {
|
|
451
|
+
// Operator conditions
|
|
452
|
+
for (const [op, opValue] of Object.entries(value)) {
|
|
453
|
+
conditions.push(`${escapeId(key)} ${formatOperator(op)} ${formatValue(opValue)}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
// Simple equality
|
|
458
|
+
conditions.push(`${escapeId(key)} = ${formatValue(value)}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return conditions.join(' AND ');
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Format an operator
|
|
465
|
+
*/
|
|
466
|
+
function formatOperator(op) {
|
|
467
|
+
const operators = {
|
|
468
|
+
$eq: '=',
|
|
469
|
+
$ne: '!=',
|
|
470
|
+
$gt: '>',
|
|
471
|
+
$gte: '>=',
|
|
472
|
+
$lt: '<',
|
|
473
|
+
$lte: '<=',
|
|
474
|
+
$like: 'LIKE',
|
|
475
|
+
$notLike: 'NOT LIKE',
|
|
476
|
+
$in: 'IN',
|
|
477
|
+
$notIn: 'NOT IN',
|
|
478
|
+
$between: 'BETWEEN',
|
|
479
|
+
$notBetween: 'NOT BETWEEN',
|
|
480
|
+
$isNull: 'IS NULL',
|
|
481
|
+
$isNotNull: 'IS NOT NULL',
|
|
482
|
+
};
|
|
483
|
+
return operators[op] || op;
|
|
484
|
+
}
|
|
485
|
+
// ==================== Other Utilities ====================
|
|
486
|
+
/**
|
|
487
|
+
* Check if value is empty (null, undefined, empty string, empty array, empty object)
|
|
488
|
+
*/
|
|
489
|
+
function isEmpty(value) {
|
|
490
|
+
if (value === null || value === undefined) {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
if (typeof value === 'string') {
|
|
494
|
+
return value.trim() === '';
|
|
495
|
+
}
|
|
496
|
+
if (Array.isArray(value)) {
|
|
497
|
+
return value.length === 0;
|
|
498
|
+
}
|
|
499
|
+
if (typeof value === 'object') {
|
|
500
|
+
return Object.keys(value).length === 0;
|
|
501
|
+
}
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Get the first defined value from arguments
|
|
506
|
+
*/
|
|
507
|
+
function coalesce(...values) {
|
|
508
|
+
return values.find((value) => value !== null && value !== undefined);
|
|
509
|
+
}
|
|
510
|
+
// Note: `truncate` and `pad` are defined once in './string' and re-exported
|
|
511
|
+
// via `export * from './string'` below. Duplicate definitions used to live
|
|
512
|
+
// here with a different (and inconsistent) default `pad` direction, which
|
|
513
|
+
// silently shadowed the './string' implementation because local exports
|
|
514
|
+
// take precedence over `export *` re-exports for the same name.
|
|
515
|
+
/**
|
|
516
|
+
* Convert a value to a string suitable for use in SQL LIKE
|
|
517
|
+
*/
|
|
518
|
+
function escapeLike(value) {
|
|
519
|
+
return value.replace(/[%_]/g, (match) => `\\${match}`);
|
|
520
|
+
}
|
|
521
|
+
// ==================== Tick/Backtick Utilities ====================
|
|
522
|
+
/**
|
|
523
|
+
* Wrap a string in backticks (for SQL identifiers)
|
|
524
|
+
*/
|
|
525
|
+
function addTicks(str) {
|
|
526
|
+
return `\`${str}\``;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Remove backticks from a string
|
|
530
|
+
*/
|
|
531
|
+
function removeTicks(str) {
|
|
532
|
+
return str.replace(/^`|`$/g, '');
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Convert a value to its default JavaScript value
|
|
536
|
+
*/
|
|
537
|
+
function toDefaultValue(value) {
|
|
538
|
+
if (value === null) {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
if (value === undefined) {
|
|
542
|
+
return undefined;
|
|
543
|
+
}
|
|
544
|
+
const defaultValues = {
|
|
545
|
+
// String types
|
|
546
|
+
STRING: () => '',
|
|
547
|
+
TEXT: () => '',
|
|
548
|
+
CITEXT: () => '',
|
|
549
|
+
CHAR: () => '',
|
|
550
|
+
UUID: () => undefined,
|
|
551
|
+
// Number types
|
|
552
|
+
INTEGER: () => 0,
|
|
553
|
+
BIGINT: () => 0,
|
|
554
|
+
FLOAT: () => 0.0,
|
|
555
|
+
DOUBLE: () => 0.0,
|
|
556
|
+
DECIMAL: () => 0,
|
|
557
|
+
// Boolean
|
|
558
|
+
BOOLEAN: () => false,
|
|
559
|
+
// Date types
|
|
560
|
+
DATE: () => null,
|
|
561
|
+
DATEONLY: () => null,
|
|
562
|
+
NOW: () => null,
|
|
563
|
+
TIME: () => null,
|
|
564
|
+
// Other
|
|
565
|
+
JSON: () => null,
|
|
566
|
+
JSONB: () => null,
|
|
567
|
+
BLOB: () => null,
|
|
568
|
+
ENUM: () => null,
|
|
569
|
+
ARRAY: () => [],
|
|
570
|
+
GEOMETRY: () => null,
|
|
571
|
+
GEOGRAPHY: () => null,
|
|
572
|
+
};
|
|
573
|
+
// If value is a DataType instance or string type name
|
|
574
|
+
if (typeof value === 'string' && defaultValues[value]) {
|
|
575
|
+
return defaultValues[value]();
|
|
576
|
+
}
|
|
577
|
+
// If value is an object with a type property
|
|
578
|
+
if (typeof value === 'object' && value !== null) {
|
|
579
|
+
const typeName = value.type?.toUpperCase?.() || value.typeName?.toUpperCase?.();
|
|
580
|
+
if (typeName && defaultValues[typeName]) {
|
|
581
|
+
return defaultValues[typeName]();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return value;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Format a value for SQL based on dialect
|
|
588
|
+
*/
|
|
589
|
+
function formatDefaultValue(value, dialect) {
|
|
590
|
+
if (value === null || value === undefined) {
|
|
591
|
+
return 'NULL';
|
|
592
|
+
}
|
|
593
|
+
if (typeof value === 'boolean') {
|
|
594
|
+
// Handle PostgreSQL boolean format
|
|
595
|
+
if (dialect === 'postgres') {
|
|
596
|
+
return value ? 'TRUE' : 'FALSE';
|
|
597
|
+
}
|
|
598
|
+
return value ? '1' : '0';
|
|
599
|
+
}
|
|
600
|
+
if (typeof value === 'number') {
|
|
601
|
+
return String(value);
|
|
602
|
+
}
|
|
603
|
+
if (typeof value === 'string') {
|
|
604
|
+
// Escape single quotes by doubling them
|
|
605
|
+
const escaped = value.replace(/'/g, "''");
|
|
606
|
+
return `'${escaped}'`;
|
|
607
|
+
}
|
|
608
|
+
if (value instanceof Date) {
|
|
609
|
+
const dateStr = value.toISOString();
|
|
610
|
+
// Handle different dialect date formats
|
|
611
|
+
if (dialect === 'postgres') {
|
|
612
|
+
return `'${dateStr}'`;
|
|
613
|
+
}
|
|
614
|
+
if (dialect === 'mysql') {
|
|
615
|
+
return `'${dateStr.slice(0, 19).replace('T', ' ')}'`;
|
|
616
|
+
}
|
|
617
|
+
return `'${dateStr}'`;
|
|
618
|
+
}
|
|
619
|
+
if (Array.isArray(value)) {
|
|
620
|
+
if (dialect === 'postgres' || dialect === 'mysql') {
|
|
621
|
+
return `'${JSON.stringify(value)}'::jsonb`;
|
|
622
|
+
}
|
|
623
|
+
return `'${JSON.stringify(value)}'`;
|
|
624
|
+
}
|
|
625
|
+
if (typeof value === 'object') {
|
|
626
|
+
return `'${JSON.stringify(value)}'`;
|
|
627
|
+
}
|
|
628
|
+
return String(value);
|
|
629
|
+
}
|
|
630
|
+
// ==================== String Splicing ====================
|
|
631
|
+
/**
|
|
632
|
+
* Insert a string at a specific position, optionally replacing characters
|
|
633
|
+
* @param str The original string
|
|
634
|
+
* @param index Position to insert/replace at (0-indexed)
|
|
635
|
+
* @param count Number of characters to replace (0 for insert only)
|
|
636
|
+
* @param insert String to insert
|
|
637
|
+
*/
|
|
638
|
+
function spliceStr(str, index, count, insert) {
|
|
639
|
+
if (index < 0) {
|
|
640
|
+
index = str.length + index;
|
|
641
|
+
}
|
|
642
|
+
if (index < 0) {
|
|
643
|
+
index = 0;
|
|
644
|
+
}
|
|
645
|
+
if (index > str.length) {
|
|
646
|
+
index = str.length;
|
|
647
|
+
}
|
|
648
|
+
return str.slice(0, index) + insert + str.slice(index + count);
|
|
649
|
+
}
|
|
650
|
+
// ==================== Date Formatting ====================
|
|
651
|
+
/**
|
|
652
|
+
* Format a date to a string
|
|
653
|
+
* @param date - Date to format
|
|
654
|
+
* @param formatStr - Format string (YYYY-MM-DD, etc.)
|
|
655
|
+
*/
|
|
656
|
+
function formatDateString(date, formatStr = 'YYYY-MM-DD') {
|
|
657
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
658
|
+
if (isNaN(d.getTime())) {
|
|
659
|
+
throw new Error('Invalid date');
|
|
660
|
+
}
|
|
661
|
+
const year = d.getFullYear();
|
|
662
|
+
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
663
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
664
|
+
const hours = String(d.getHours()).padStart(2, '0');
|
|
665
|
+
const minutes = String(d.getMinutes()).padStart(2, '0');
|
|
666
|
+
const seconds = String(d.getSeconds()).padStart(2, '0');
|
|
667
|
+
return formatStr
|
|
668
|
+
.replace('YYYY', String(year))
|
|
669
|
+
.replace('YY', String(year).slice(-2))
|
|
670
|
+
.replace('MM', month)
|
|
671
|
+
.replace('DD', day)
|
|
672
|
+
.replace('HH', hours)
|
|
673
|
+
.replace('mm', minutes)
|
|
674
|
+
.replace('ss', seconds);
|
|
675
|
+
}
|
|
676
|
+
// ==================== Random String Generation ====================
|
|
677
|
+
/**
|
|
678
|
+
* Generate a random string of specified length
|
|
679
|
+
*/
|
|
680
|
+
function randomString(length = 16, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
|
|
681
|
+
let result = '';
|
|
682
|
+
const charsLength = chars.length;
|
|
683
|
+
for (let i = 0; i < length; i++) {
|
|
684
|
+
result += chars.charAt(Math.floor(Math.random() * charsLength));
|
|
685
|
+
}
|
|
686
|
+
return result;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Generate a random alphanumeric string
|
|
690
|
+
*/
|
|
691
|
+
function randomAlphaNumeric(length = 16) {
|
|
692
|
+
return randomString(length, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789');
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Generate a random numeric string
|
|
696
|
+
*/
|
|
697
|
+
function randomNumeric(length = 8) {
|
|
698
|
+
return randomString(length, '0123456789');
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Generate a random hex string
|
|
702
|
+
*/
|
|
703
|
+
function randomHex(length = 16) {
|
|
704
|
+
return randomString(length, '0123456789abcdef');
|
|
705
|
+
}
|
|
706
|
+
// ==================== Current Timestamp ====================
|
|
707
|
+
/**
|
|
708
|
+
* Get current timestamp in milliseconds
|
|
709
|
+
*
|
|
710
|
+
* Note: named `timestamp` (not `now`) to avoid colliding with the
|
|
711
|
+
* `now(): Date` export from './date', which is re-exported via
|
|
712
|
+
* `export * from './date'` below and is the canonical `utils.now()`.
|
|
713
|
+
*/
|
|
714
|
+
function timestamp() {
|
|
715
|
+
return Date.now();
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Get current timestamp as Date object
|
|
719
|
+
*/
|
|
720
|
+
function nowDate() {
|
|
721
|
+
return new Date();
|
|
722
|
+
}
|
|
723
|
+
// ==================== Default Value ====================
|
|
724
|
+
/**
|
|
725
|
+
* Return default value if value is null or undefined
|
|
726
|
+
*/
|
|
727
|
+
function defaultValue(value, defaultVal) {
|
|
728
|
+
return value !== null && value !== undefined ? value : defaultVal;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Return default value if value is empty
|
|
732
|
+
*/
|
|
733
|
+
function defaultIfEmpty(value, defaultVal) {
|
|
734
|
+
if (value === null || value === undefined)
|
|
735
|
+
return defaultVal;
|
|
736
|
+
if (typeof value === 'string' && value.trim() === '')
|
|
737
|
+
return defaultVal;
|
|
738
|
+
if (Array.isArray(value) && value.length === 0)
|
|
739
|
+
return defaultVal;
|
|
740
|
+
if (typeof value === 'object' && Object.keys(value).length === 0)
|
|
741
|
+
return defaultVal;
|
|
742
|
+
return value;
|
|
743
|
+
}
|
|
744
|
+
// Default error patterns to retry on
|
|
745
|
+
const DEFAULT_RETRY_PATTERNS = [
|
|
746
|
+
'Connection timeout',
|
|
747
|
+
'ETIMEDOUT',
|
|
748
|
+
'ECONNREFUSED',
|
|
749
|
+
'ECONNRESET',
|
|
750
|
+
'EHOSTUNREACH',
|
|
751
|
+
'ENOTFOUND',
|
|
752
|
+
'timeout',
|
|
753
|
+
'connecting',
|
|
754
|
+
'connection',
|
|
755
|
+
'database',
|
|
756
|
+
];
|
|
757
|
+
/**
|
|
758
|
+
* Sleep for the specified duration
|
|
759
|
+
*/
|
|
760
|
+
function sleep(ms) {
|
|
761
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Check if an error matches any of the retry patterns
|
|
765
|
+
*/
|
|
766
|
+
function shouldRetry(error, matchPatterns) {
|
|
767
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
768
|
+
const patterns = matchPatterns || DEFAULT_RETRY_PATTERNS;
|
|
769
|
+
return patterns.some((pattern) => errorMessage.toLowerCase().includes(pattern.toLowerCase()));
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Calculate the delay for the next retry using exponential backoff
|
|
773
|
+
*/
|
|
774
|
+
function calculateBackoffDelay(attempt, baseTimeout, backoffMultiplier, maxDelay) {
|
|
775
|
+
const delay = baseTimeout * Math.pow(backoffMultiplier, attempt);
|
|
776
|
+
return Math.min(delay, maxDelay);
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Retry a function with configurable options
|
|
780
|
+
*
|
|
781
|
+
* @param fn - The async function to retry
|
|
782
|
+
* @param options - Retry options
|
|
783
|
+
* @returns The result of the function
|
|
784
|
+
*/
|
|
785
|
+
async function retry(fn, options = {}) {
|
|
786
|
+
const { max = 3, timeout = 1000, match, backoff = false, backoffMultiplier = 2, backoffMax = 10000, } = options;
|
|
787
|
+
let lastError = 'Unknown error';
|
|
788
|
+
let attempt = 0;
|
|
789
|
+
while (attempt < max) {
|
|
790
|
+
try {
|
|
791
|
+
return await fn();
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
lastError = error;
|
|
795
|
+
// Check if we should retry this error
|
|
796
|
+
if (!shouldRetry(lastError, match)) {
|
|
797
|
+
throw lastError;
|
|
798
|
+
}
|
|
799
|
+
attempt++;
|
|
800
|
+
// If we've reached max attempts, throw the last error
|
|
801
|
+
if (attempt >= max) {
|
|
802
|
+
throw lastError;
|
|
803
|
+
}
|
|
804
|
+
// Calculate delay for next retry
|
|
805
|
+
let delay;
|
|
806
|
+
if (backoff) {
|
|
807
|
+
delay = calculateBackoffDelay(attempt, timeout, backoffMultiplier, backoffMax);
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
delay = timeout;
|
|
811
|
+
}
|
|
812
|
+
// Add some jitter to avoid thundering herd
|
|
813
|
+
const jitter = Math.random() * 100;
|
|
814
|
+
await sleep(delay + jitter);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
throw lastError;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Retry a function with exponential backoff
|
|
821
|
+
*
|
|
822
|
+
* @param fn - The async function to retry
|
|
823
|
+
* @param maxRetries - Maximum number of retries (default: 3)
|
|
824
|
+
* @param baseTimeout - Base timeout in ms (default: 1000)
|
|
825
|
+
* @param maxTimeout - Maximum timeout in ms (default: 10000)
|
|
826
|
+
* @returns The result of the function
|
|
827
|
+
*/
|
|
828
|
+
async function retryWithBackoff(fn, maxRetries = 3, baseTimeout = 1000, maxTimeout = 10000) {
|
|
829
|
+
return retry(fn, {
|
|
830
|
+
max: maxRetries,
|
|
831
|
+
timeout: baseTimeout,
|
|
832
|
+
backoff: true,
|
|
833
|
+
backoffMultiplier: 2,
|
|
834
|
+
backoffMax: maxTimeout,
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
// ==================== Array Utils ====================
|
|
838
|
+
/**
|
|
839
|
+
* Get unique values from an array
|
|
840
|
+
*/
|
|
841
|
+
function unique(arr) {
|
|
842
|
+
return Array.from(new Set(arr));
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Flatten an array of arrays
|
|
846
|
+
*/
|
|
847
|
+
function flatten(arr) {
|
|
848
|
+
return arr.flat();
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Group array items by a key
|
|
852
|
+
*/
|
|
853
|
+
function groupBy(arr, key) {
|
|
854
|
+
return arr.reduce((result, item) => {
|
|
855
|
+
const groupKey = String(item[key]);
|
|
856
|
+
if (!result[groupKey]) {
|
|
857
|
+
result[groupKey] = [];
|
|
858
|
+
}
|
|
859
|
+
result[groupKey].push(item);
|
|
860
|
+
return result;
|
|
861
|
+
}, {});
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Sort array by a key or function
|
|
865
|
+
*/
|
|
866
|
+
function sortBy(arr, iteratee) {
|
|
867
|
+
return [...arr].sort((a, b) => {
|
|
868
|
+
const aVal = typeof iteratee === 'function' ? iteratee(a) : a[iteratee];
|
|
869
|
+
const bVal = typeof iteratee === 'function' ? iteratee(b) : b[iteratee];
|
|
870
|
+
if (aVal < bVal)
|
|
871
|
+
return -1;
|
|
872
|
+
if (aVal > bVal)
|
|
873
|
+
return 1;
|
|
874
|
+
return 0;
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Sum array values
|
|
879
|
+
*/
|
|
880
|
+
function sum(arr) {
|
|
881
|
+
return arr.reduce((total, n) => total + n, 0);
|
|
882
|
+
}
|
|
883
|
+
// ==================== Additional String Utilities ====================
|
|
884
|
+
/**
|
|
885
|
+
* Convert string to snake_case (alias for snakeCase)
|
|
886
|
+
*/
|
|
887
|
+
function underscore(str) {
|
|
888
|
+
return snakeCase(str);
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* String includes method
|
|
892
|
+
*/
|
|
893
|
+
function includes(str, searchString) {
|
|
894
|
+
return str.includes(searchString);
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* String startsWith method
|
|
898
|
+
*/
|
|
899
|
+
function startsWith(str, searchString) {
|
|
900
|
+
return str.startsWith(searchString);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* String endsWith method
|
|
904
|
+
*/
|
|
905
|
+
function endsWith(str, searchString) {
|
|
906
|
+
return str.endsWith(searchString);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* String repeat method
|
|
910
|
+
*/
|
|
911
|
+
function repeat(str, count) {
|
|
912
|
+
return str.repeat(count);
|
|
913
|
+
}
|
|
914
|
+
function reverse(input) {
|
|
915
|
+
if (typeof input === 'string') {
|
|
916
|
+
return [...input].reverse().join('');
|
|
917
|
+
}
|
|
918
|
+
return [...input].reverse();
|
|
919
|
+
}
|
|
920
|
+
// ==================== Object Utilities ====================
|
|
921
|
+
/**
|
|
922
|
+
* Deep clone an object
|
|
923
|
+
*/
|
|
924
|
+
function clone(obj) {
|
|
925
|
+
return JSON.parse(JSON.stringify(obj));
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get property from object
|
|
929
|
+
*/
|
|
930
|
+
function get(obj, path) {
|
|
931
|
+
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Set property on object
|
|
935
|
+
*/
|
|
936
|
+
function set(obj, path, value) {
|
|
937
|
+
const parts = path.split('.');
|
|
938
|
+
let current = obj;
|
|
939
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
940
|
+
if (!current[parts[i]]) {
|
|
941
|
+
current[parts[i]] = {};
|
|
942
|
+
}
|
|
943
|
+
current = current[parts[i]];
|
|
944
|
+
}
|
|
945
|
+
current[parts[parts.length - 1]] = value;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Check if object has property
|
|
949
|
+
*/
|
|
950
|
+
function has(obj, path) {
|
|
951
|
+
const parts = path.split('.');
|
|
952
|
+
let current = obj;
|
|
953
|
+
for (const part of parts) {
|
|
954
|
+
if (current == null || !(part in current)) {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
current = current[part];
|
|
958
|
+
}
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
// ==================== Type Check Utilities ====================
|
|
962
|
+
/**
|
|
963
|
+
* Check if value is a string
|
|
964
|
+
*/
|
|
965
|
+
function isString(value) {
|
|
966
|
+
return typeof value === 'string';
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Check if value is a number
|
|
970
|
+
*/
|
|
971
|
+
function isNumber(value) {
|
|
972
|
+
return typeof value === 'number' && !isNaN(value);
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Check if value is a boolean
|
|
976
|
+
*/
|
|
977
|
+
function isBoolean(value) {
|
|
978
|
+
return typeof value === 'boolean';
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Check if value is a date
|
|
982
|
+
*/
|
|
983
|
+
function isDate(value) {
|
|
984
|
+
return value instanceof Date;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Check if value is an array
|
|
988
|
+
*/
|
|
989
|
+
function isArray(value) {
|
|
990
|
+
return Array.isArray(value);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Check if value is an object
|
|
994
|
+
*/
|
|
995
|
+
function isObject(value) {
|
|
996
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
997
|
+
}
|
|
998
|
+
// ==================== Format Utilities ====================
|
|
999
|
+
/**
|
|
1000
|
+
* Format a number as currency
|
|
1001
|
+
*/
|
|
1002
|
+
function formatCurrency(amount, currency = 'USD') {
|
|
1003
|
+
// useGrouping: false so callers can reliably find the raw numeric
|
|
1004
|
+
// substring (e.g. '1234.56') in the output without thousands separators
|
|
1005
|
+
// breaking it up (e.g. '1,234.56').
|
|
1006
|
+
return new Intl.NumberFormat('en-US', {
|
|
1007
|
+
style: 'currency',
|
|
1008
|
+
currency,
|
|
1009
|
+
useGrouping: false,
|
|
1010
|
+
}).format(amount);
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Format a number to a fixed number of decimal places.
|
|
1014
|
+
*
|
|
1015
|
+
* Named `format` (not re-exported by './date', see note there) so this is
|
|
1016
|
+
* the canonical `utils.format` for numeric formatting.
|
|
1017
|
+
*/
|
|
1018
|
+
function format(value, decimals = 2) {
|
|
1019
|
+
return value.toFixed(decimals);
|
|
1020
|
+
}
|
|
1021
|
+
// Export from string utils
|
|
1022
|
+
__exportStar(require("./string"), exports);
|
|
1023
|
+
// Export from date utils
|
|
1024
|
+
__exportStar(require("./date"), exports);
|
|
1025
|
+
exports.default = {
|
|
1026
|
+
camelToSnake,
|
|
1027
|
+
snakeToCamel,
|
|
1028
|
+
camelToKebab,
|
|
1029
|
+
kebabToCamel,
|
|
1030
|
+
camelize,
|
|
1031
|
+
snakeCase,
|
|
1032
|
+
pluralize,
|
|
1033
|
+
singularize,
|
|
1034
|
+
cloneDeep,
|
|
1035
|
+
merge,
|
|
1036
|
+
pick,
|
|
1037
|
+
omit,
|
|
1038
|
+
generateUUID,
|
|
1039
|
+
generateShortUUID,
|
|
1040
|
+
isValidUUID,
|
|
1041
|
+
formatValues,
|
|
1042
|
+
formatValue,
|
|
1043
|
+
escapeString,
|
|
1044
|
+
escapeId,
|
|
1045
|
+
formatWhere,
|
|
1046
|
+
isEmpty,
|
|
1047
|
+
coalesce,
|
|
1048
|
+
truncate: string_1.truncate,
|
|
1049
|
+
pad: string_1.pad,
|
|
1050
|
+
escapeLike,
|
|
1051
|
+
addTicks,
|
|
1052
|
+
removeTicks,
|
|
1053
|
+
toDefaultValue,
|
|
1054
|
+
formatDefaultValue,
|
|
1055
|
+
spliceStr,
|
|
1056
|
+
unique,
|
|
1057
|
+
flatten,
|
|
1058
|
+
groupBy,
|
|
1059
|
+
sortBy,
|
|
1060
|
+
sum,
|
|
1061
|
+
underscore,
|
|
1062
|
+
includes,
|
|
1063
|
+
startsWith,
|
|
1064
|
+
endsWith,
|
|
1065
|
+
repeat,
|
|
1066
|
+
reverse,
|
|
1067
|
+
clone,
|
|
1068
|
+
get,
|
|
1069
|
+
set,
|
|
1070
|
+
has,
|
|
1071
|
+
isString,
|
|
1072
|
+
isNumber,
|
|
1073
|
+
isBoolean,
|
|
1074
|
+
isDate,
|
|
1075
|
+
isArray,
|
|
1076
|
+
isObject,
|
|
1077
|
+
formatCurrency,
|
|
1078
|
+
formatDateString,
|
|
1079
|
+
};
|