node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
/**
* @internal
*/
export declare const asyncCurry: <Args extends any[], R>(fn: (...args: Args) => Promise<R>) => CurriedAsyncFunction<Args, R>;
/**
* @internal
*/
export type CurriedAsyncFunction<Args extends any[], R> = Args extends [infer First, ...infer Rest] ? Rest extends [] ? (arg: First) => Promise<R> : (arg: First) => CurriedAsyncFunction<Rest, R> : () => Promise<R>;
//# sourceMappingURL=async-curry.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"async-curry.d.ts","sourceRoot":"","sources":["../../src/utils/async-curry.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,eAAO,MAAM,UAAU,8BACjB,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,KAChC,qBAAqB,IAAI,EAAE,CAAC,CAS9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,CAAC,IAAI,SAAS,GAAG,EAAE,EAAE,CAAC,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,GAC/F,IAAI,SAAS,EAAE,GACb,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAC1B,CAAC,GAAG,EAAE,KAAK,KAAK,oBAAoB,CAAC,IAAI,EAAE,CAAC,CAAC,GAC/C,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1,19 @@
'use strict';
// lodash/fp curry does not handle async functions properly, and creates very "ugly" types,
// so we will use our own version to ensure curried functions are typed correctly
// TODO: Export this from root @strapi/utils so we don't have copies of it between packages
/**
* @internal
*/ const asyncCurry = (fn)=>{
const curried = (...args)=>{
if (args.length >= fn.length) {
return fn(...args);
}
return (...moreArgs)=>curried(...args, ...moreArgs);
};
return curried;
};
exports.asyncCurry = asyncCurry;
//# sourceMappingURL=async-curry.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"async-curry.js","sources":["../../src/utils/async-curry.ts"],"sourcesContent":["// lodash/fp curry does not handle async functions properly, and creates very \"ugly\" types,\n// so we will use our own version to ensure curried functions are typed correctly\n// TODO: Export this from root @strapi/utils so we don't have copies of it between packages\n\n/**\n * @internal\n */\nexport const asyncCurry = <Args extends any[], R>(\n fn: (...args: Args) => Promise<R>\n): CurriedAsyncFunction<Args, R> => {\n const curried = (...args: any[]): any => {\n if (args.length >= fn.length) {\n return fn(...(args as Args));\n }\n return (...moreArgs: any[]) => curried(...args, ...moreArgs);\n };\n\n return curried as CurriedAsyncFunction<Args, R>;\n};\n\n/**\n * @internal\n */\nexport type CurriedAsyncFunction<Args extends any[], R> = Args extends [infer First, ...infer Rest]\n ? Rest extends []\n ? (arg: First) => Promise<R>\n : (arg: First) => CurriedAsyncFunction<Rest, R>\n : () => Promise<R>;\n"],"names":["asyncCurry","fn","curried","args","length","moreArgs"],"mappings":";;AAAA;AACA;AACA;AAEA;;IAGaA,MAAAA,UAAAA,GAAa,CACxBC,EAAAA,GAAAA;IAEA,MAAMC,OAAAA,GAAU,CAAC,GAAGC,IAAAA,GAAAA;AAClB,QAAA,IAAIA,IAAKC,CAAAA,MAAM,IAAIH,EAAAA,CAAGG,MAAM,EAAE;AAC5B,YAAA,OAAOH,EAAOE,CAAAA,GAAAA,IAAAA,CAAAA;AAChB;AACA,QAAA,OAAO,CAAC,GAAGE,QAAoBH,GAAAA,OAAAA,CAAAA,GAAWC,IAASE,EAAAA,GAAAA,QAAAA,CAAAA;AACrD,KAAA;IAEA,OAAOH,OAAAA;AACT;;;;"}

View File

@@ -0,0 +1,17 @@
// lodash/fp curry does not handle async functions properly, and creates very "ugly" types,
// so we will use our own version to ensure curried functions are typed correctly
// TODO: Export this from root @strapi/utils so we don't have copies of it between packages
/**
* @internal
*/ const asyncCurry = (fn)=>{
const curried = (...args)=>{
if (args.length >= fn.length) {
return fn(...args);
}
return (...moreArgs)=>curried(...args, ...moreArgs);
};
return curried;
};
export { asyncCurry };
//# sourceMappingURL=async-curry.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"async-curry.mjs","sources":["../../src/utils/async-curry.ts"],"sourcesContent":["// lodash/fp curry does not handle async functions properly, and creates very \"ugly\" types,\n// so we will use our own version to ensure curried functions are typed correctly\n// TODO: Export this from root @strapi/utils so we don't have copies of it between packages\n\n/**\n * @internal\n */\nexport const asyncCurry = <Args extends any[], R>(\n fn: (...args: Args) => Promise<R>\n): CurriedAsyncFunction<Args, R> => {\n const curried = (...args: any[]): any => {\n if (args.length >= fn.length) {\n return fn(...(args as Args));\n }\n return (...moreArgs: any[]) => curried(...args, ...moreArgs);\n };\n\n return curried as CurriedAsyncFunction<Args, R>;\n};\n\n/**\n * @internal\n */\nexport type CurriedAsyncFunction<Args extends any[], R> = Args extends [infer First, ...infer Rest]\n ? Rest extends []\n ? (arg: First) => Promise<R>\n : (arg: First) => CurriedAsyncFunction<Rest, R>\n : () => Promise<R>;\n"],"names":["asyncCurry","fn","curried","args","length","moreArgs"],"mappings":"AAAA;AACA;AACA;AAEA;;IAGaA,MAAAA,UAAAA,GAAa,CACxBC,EAAAA,GAAAA;IAEA,MAAMC,OAAAA,GAAU,CAAC,GAAGC,IAAAA,GAAAA;AAClB,QAAA,IAAIA,IAAKC,CAAAA,MAAM,IAAIH,EAAAA,CAAGG,MAAM,EAAE;AAC5B,YAAA,OAAOH,EAAOE,CAAAA,GAAAA,IAAAA,CAAAA;AAChB;AACA,QAAA,OAAO,CAAC,GAAGE,QAAoBH,GAAAA,OAAAA,CAAAA,GAAWC,IAASE,EAAAA,GAAAA,QAAAA,CAAAA;AACrD,KAAA;IAEA,OAAOH,OAAAA;AACT;;;;"}

View File

@@ -0,0 +1,31 @@
/**
* @fileoverview This file contains utility functions for shortening identifiers for use in a database schema.
* The functions in this file are used to generate shorter names for database tables and columns
* to avoid breaking the constraints of databases.
*
* IMPORTANT
* Any changes here that result in a different output string from any of the naming methods will
* cause the schema creation to delete data it doesn't recognize because the name
* is different.
*
* If there are any test failures after updating this code, it means there is a breaking change that
* will cause data loss, so beware; do not update the test to match your changes
*
* @internal
*/
/**
* Creates a hash of the given data with the specified string length as a string of hex characters
*
* @example
* createHash("myData", 5); // "03f85"
* createHash("myData", 2); // "03"
* createHash("myData", 1); // "0"
*
* @param data - The data to be hashed
* @param len - The length of the hash
* @returns The generated hash
* @throws Error if the length is not a positive integer
* @internal
*/
export declare function createHash(data: string, len: number): string;
//# sourceMappingURL=hash.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../../src/utils/identifiers/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO5D"}

View File

@@ -0,0 +1,30 @@
'use strict';
var crypto = require('node:crypto');
var _ = require('lodash/fp');
/**
* Creates a hash of the given data with the specified string length as a string of hex characters
*
* @example
* createHash("myData", 5); // "03f85"
* createHash("myData", 2); // "03"
* createHash("myData", 1); // "0"
*
* @param data - The data to be hashed
* @param len - The length of the hash
* @returns The generated hash
* @throws Error if the length is not a positive integer
* @internal
*/ function createHash(data, len) {
if (!_.isInteger(len) || len <= 0) {
throw new Error(`createHash length must be a positive integer, received ${len}`);
}
const hash = crypto.createHash('shake256', {
outputLength: Math.ceil(len / 2)
}).update(data);
return hash.digest('hex').substring(0, len);
}
exports.createHash = createHash;
//# sourceMappingURL=hash.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hash.js","sources":["../../../src/utils/identifiers/hash.ts"],"sourcesContent":["/**\n * @fileoverview This file contains utility functions for shortening identifiers for use in a database schema.\n * The functions in this file are used to generate shorter names for database tables and columns\n * to avoid breaking the constraints of databases.\n *\n * IMPORTANT\n * Any changes here that result in a different output string from any of the naming methods will\n * cause the schema creation to delete data it doesn't recognize because the name\n * is different.\n *\n * If there are any test failures after updating this code, it means there is a breaking change that\n * will cause data loss, so beware; do not update the test to match your changes\n *\n * @internal\n */\n\nimport crypto from 'node:crypto';\nimport { isInteger } from 'lodash/fp';\n\n/**\n * Creates a hash of the given data with the specified string length as a string of hex characters\n *\n * @example\n * createHash(\"myData\", 5); // \"03f85\"\n * createHash(\"myData\", 2); // \"03\"\n * createHash(\"myData\", 1); // \"0\"\n *\n * @param data - The data to be hashed\n * @param len - The length of the hash\n * @returns The generated hash\n * @throws Error if the length is not a positive integer\n * @internal\n */\nexport function createHash(data: string, len: number): string {\n if (!isInteger(len) || len <= 0) {\n throw new Error(`createHash length must be a positive integer, received ${len}`);\n }\n\n const hash = crypto.createHash('shake256', { outputLength: Math.ceil(len / 2) }).update(data);\n return hash.digest('hex').substring(0, len);\n}\n"],"names":["createHash","data","len","isInteger","Error","hash","crypto","outputLength","Math","ceil","update","digest","substring"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;AAaC,IACM,SAASA,UAAWC,CAAAA,IAAY,EAAEC,GAAW,EAAA;AAClD,IAAA,IAAI,CAACC,WAAAA,CAAUD,GAAQA,CAAAA,IAAAA,GAAAA,IAAO,CAAG,EAAA;AAC/B,QAAA,MAAM,IAAIE,KAAM,CAAA,CAAC,uDAAuD,EAAEF,IAAI,CAAC,CAAA;AACjF;AAEA,IAAA,MAAMG,IAAOC,GAAAA,MAAAA,CAAON,UAAU,CAAC,UAAY,EAAA;QAAEO,YAAcC,EAAAA,IAAAA,CAAKC,IAAI,CAACP,GAAM,GAAA,CAAA;AAAG,KAAA,CAAA,CAAGQ,MAAM,CAACT,IAAAA,CAAAA;AACxF,IAAA,OAAOI,KAAKM,MAAM,CAAC,KAAOC,CAAAA,CAAAA,SAAS,CAAC,CAAGV,EAAAA,GAAAA,CAAAA;AACzC;;;;"}

View File

@@ -0,0 +1,28 @@
import crypto from 'node:crypto';
import { isInteger } from 'lodash/fp';
/**
* Creates a hash of the given data with the specified string length as a string of hex characters
*
* @example
* createHash("myData", 5); // "03f85"
* createHash("myData", 2); // "03"
* createHash("myData", 1); // "0"
*
* @param data - The data to be hashed
* @param len - The length of the hash
* @returns The generated hash
* @throws Error if the length is not a positive integer
* @internal
*/ function createHash(data, len) {
if (!isInteger(len) || len <= 0) {
throw new Error(`createHash length must be a positive integer, received ${len}`);
}
const hash = crypto.createHash('shake256', {
outputLength: Math.ceil(len / 2)
}).update(data);
return hash.digest('hex').substring(0, len);
}
export { createHash };
//# sourceMappingURL=hash.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hash.mjs","sources":["../../../src/utils/identifiers/hash.ts"],"sourcesContent":["/**\n * @fileoverview This file contains utility functions for shortening identifiers for use in a database schema.\n * The functions in this file are used to generate shorter names for database tables and columns\n * to avoid breaking the constraints of databases.\n *\n * IMPORTANT\n * Any changes here that result in a different output string from any of the naming methods will\n * cause the schema creation to delete data it doesn't recognize because the name\n * is different.\n *\n * If there are any test failures after updating this code, it means there is a breaking change that\n * will cause data loss, so beware; do not update the test to match your changes\n *\n * @internal\n */\n\nimport crypto from 'node:crypto';\nimport { isInteger } from 'lodash/fp';\n\n/**\n * Creates a hash of the given data with the specified string length as a string of hex characters\n *\n * @example\n * createHash(\"myData\", 5); // \"03f85\"\n * createHash(\"myData\", 2); // \"03\"\n * createHash(\"myData\", 1); // \"0\"\n *\n * @param data - The data to be hashed\n * @param len - The length of the hash\n * @returns The generated hash\n * @throws Error if the length is not a positive integer\n * @internal\n */\nexport function createHash(data: string, len: number): string {\n if (!isInteger(len) || len <= 0) {\n throw new Error(`createHash length must be a positive integer, received ${len}`);\n }\n\n const hash = crypto.createHash('shake256', { outputLength: Math.ceil(len / 2) }).update(data);\n return hash.digest('hex').substring(0, len);\n}\n"],"names":["createHash","data","len","isInteger","Error","hash","crypto","outputLength","Math","ceil","update","digest","substring"],"mappings":";;;AAmBA;;;;;;;;;;;;;AAaC,IACM,SAASA,UAAWC,CAAAA,IAAY,EAAEC,GAAW,EAAA;AAClD,IAAA,IAAI,CAACC,SAAAA,CAAUD,GAAQA,CAAAA,IAAAA,GAAAA,IAAO,CAAG,EAAA;AAC/B,QAAA,MAAM,IAAIE,KAAM,CAAA,CAAC,uDAAuD,EAAEF,IAAI,CAAC,CAAA;AACjF;AAEA,IAAA,MAAMG,IAAOC,GAAAA,MAAAA,CAAON,UAAU,CAAC,UAAY,EAAA;QAAEO,YAAcC,EAAAA,IAAAA,CAAKC,IAAI,CAACP,GAAM,GAAA,CAAA;AAAG,KAAA,CAAA,CAAGQ,MAAM,CAACT,IAAAA,CAAAA;AACxF,IAAA,OAAOI,KAAKM,MAAM,CAAC,KAAOC,CAAAA,CAAAA,SAAS,CAAC,CAAGV,EAAAA,GAAAA,CAAAA;AACzC;;;;"}

View File

@@ -0,0 +1,108 @@
import { IdentifiersOptions, NameInput, NameOptions, NameToken } from './types';
export declare class Identifiers {
#private;
ID_COLUMN: "id";
ORDER_COLUMN: "order";
FIELD_COLUMN: "field";
HASH_LENGTH: 5;
HASH_SEPARATOR: "";
IDENTIFIER_SEPARATOR: "_";
MIN_TOKEN_LENGTH: 3;
constructor(options: {
maxLength: number;
});
get replacementMap(): {
links: string;
order_inv_fk: string;
order: string;
morphs: string;
index: string;
inv_fk: string;
order_fk: string;
id_column_index: string;
order_index: string;
unique: string;
primary: string;
};
get options(): IdentifiersOptions;
mapshortNames: (name: string) => string | undefined;
/**
* TODO: we should be requiring snake_case inputs for all names here, but we
* aren't and it will require some refactoring to make it work. Currently if
* we get names 'myModel' and 'my_model' they would be converted to the same
* final string my_model which generally works but is not entirely safe
* */
getName: (names: NameInput, options?: NameOptions) => string;
/**
* TABLES
*/
getTableName: (name: string, options?: NameOptions) => string;
getJoinTableName: (collectionName: string, attributeName: string, options?: NameOptions) => string;
getMorphTableName: (collectionName: string, attributeName: string, options?: NameOptions) => string;
/**
* COLUMNS
*/
getColumnName: (attributeName: string, options?: NameOptions) => string;
getJoinColumnAttributeIdName: (attributeName: string, options?: NameOptions) => string;
getInverseJoinColumnAttributeIdName: (attributeName: string, options?: NameOptions) => string;
getOrderColumnName: (singularName: string, options?: NameOptions) => string;
getInverseOrderColumnName: (singularName: string, options?: NameOptions) => string;
/**
* Morph Join Tables
*/
getMorphColumnJoinTableIdName: (singularName: string, options?: NameOptions) => string;
getMorphColumnAttributeIdName: (attributeName: string, options?: NameOptions) => string;
getMorphColumnTypeName: (attributeName: string, options?: NameOptions) => string;
/**
* INDEXES
* Note that these methods are generally used to reference full table names + attribute(s), which
* may already be shortened strings rather than individual parts.
* That is fine and expected to compress the previously incompressible parts of those strings,
* because in these cases the relevant information is the table name and we can't really do
* any better; shortening the individual parts again might make it even more confusing.
*
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
* mytable_myattr4567d_loc63bf2_fk
*/
getIndexName: (names: NameInput, options?: NameOptions) => string;
getFkIndexName: (names: NameInput, options?: NameOptions) => string;
getUniqueIndexName: (names: NameInput, options?: NameOptions) => string;
getPrimaryIndexName: (names: NameInput, options?: NameOptions) => string;
getInverseFkIndexName: (names: NameInput, options?: NameOptions) => string;
getOrderFkIndexName: (names: NameInput, options?: NameOptions) => string;
getOrderInverseFkIndexName: (names: NameInput, options?: NameOptions) => string;
getIdColumnIndexName: (names: NameInput, options?: NameOptions) => string;
getOrderIndexName: (names: NameInput, options?: NameOptions) => string;
/**
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
*
* @example
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
* // we don't want to end up with "longstrin" and "longstrin"
* // we want something such as "longs0b23" and "longs953f"
* const token1 = generateToken("longstring1", 9); // "longs0b23"
* const token2 = generateToken("longstring2", 9); // "longs953f"
*
* @param name - The base name
* @param len - The desired length of the token.
* @returns The generated token with hash.
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
* @internal
*/
getShortenedName: (name: string, len: number) => string;
/**
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
* incompressible tokens.
* @internal
*/
getNameFromTokens: (nameTokens: NameToken[]) => string;
nameMap: Map<string, string>;
getUnshortenedName: (shortName: string) => string;
setUnshortenedName: (shortName: string, fullName: string) => void;
serializeKey: (shortName: string) => string;
}
export declare const identifiers: Identifiers;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/identifiers/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,SAAS,EAEV,MAAM,SAAS,CAAC;AAIjB,qBAAa,WAAW;;IACtB,SAAS,OAAiB;IAE1B,YAAY,UAAoB;IAEhC,YAAY,UAAoB;IAEhC,WAAW,IAAc;IAEzB,cAAc,KAAe;IAE7B,oBAAoB,MAAgB;IAEpC,gBAAgB,IAAc;gBAmBlB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;IAI1C,IAAI,cAAc;;;;;;;;;;;;MAEjB;IAED,IAAI,OAAO,uBAEV;IAED,aAAa,SAAU,MAAM,KAAG,MAAM,GAAG,SAAS,CAKhD;IAGF;;;;;SAKK;IACL,OAAO,UAAW,SAAS,YAAY,WAAW,YAyBhD;IAEF;;OAEG;IAEH,YAAY,SAAU,MAAM,YAAY,WAAW,YAEjD;IAEF,gBAAgB,mBAAoB,MAAM,iBAAiB,MAAM,YAAY,WAAW,YAKtF;IAEF,iBAAiB,mBAAoB,MAAM,iBAAiB,MAAM,YAAY,WAAW,YAKvF;IAEF;;OAEG;IAEH,aAAa,kBAAmB,MAAM,YAAY,WAAW,YAE3D;IAEF,4BAA4B,kBAAmB,MAAM,YAAY,WAAW,YAE1E;IAEF,mCAAmC,kBAAmB,MAAM,YAAY,WAAW,YAEjF;IAEF,kBAAkB,iBAAkB,MAAM,YAAY,WAAW,YAE/D;IAEF,yBAAyB,iBAAkB,MAAM,YAAY,WAAW,YAMtE;IAEF;;OAEG;IACH,6BAA6B,iBAAkB,MAAM,YAAY,WAAW,YAE1E;IAEF,6BAA6B,kBAAmB,MAAM,YAAY,WAAW,YAE3E;IAEF,sBAAsB,kBAAmB,MAAM,YAAY,WAAW,YAEpE;IAEF;;;;;;;;;;OAUG;IAGH,YAAY,UAAW,SAAS,YAAY,WAAW,YAErD;IAEF,cAAc,UAAW,SAAS,YAAY,WAAW,YAEvD;IAEF,kBAAkB,UAAW,SAAS,YAAY,WAAW,YAE3D;IAEF,mBAAmB,UAAW,SAAS,YAAY,WAAW,YAE5D;IAGF,qBAAqB,UAAW,SAAS,YAAY,WAAW,YAE9D;IAEF,mBAAmB,UAAW,SAAS,YAAY,WAAW,YAE5D;IAEF,0BAA0B,UAAW,SAAS,YAAY,WAAW,YAEnE;IAEF,oBAAoB,UAAW,SAAS,YAAY,WAAW,YAE7D;IAEF,iBAAiB,UAAW,SAAS,YAAY,WAAW,YAE1D;IAEF;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,SAAU,MAAM,OAAO,MAAM,YAwB3C;IAEF;;;;;;;OAOG;IACH,iBAAiB,eAAgB,SAAS,EAAE,KAAG,MAAM,CAqJnD;IAIF,OAAO,sBAA6B;IAEpC,kBAAkB,cAAe,MAAM,YAErC;IAEF,kBAAkB,cAAe,MAAM,YAAY,MAAM,UAUvD;IAEF,YAAY,cAAe,MAAM,YAE/B;CACH;AAID,eAAO,MAAM,WAAW,aAAwD,CAAC"}

View File

@@ -0,0 +1,414 @@
'use strict';
var _ = require('lodash/fp');
var hash = require('./hash.js');
/**
* This file contains utility functions for generating names used in the database.
* These names include table names, column names, join table names, index names, and more.
* The generated names can be customized with prefixes, suffixes, and maximum length.
* These utility functions are used throughout the codebase to ensure consistent and standardized naming conventions in the database.
*
* The reason for checking maxLength for suffixes and prefixes and using the long ones from Strapi 4 is so that we always
* have access to the full length names, in particular for migration purposes, but also so that (in theory) the feature
* could be disabled and stay compatible with v4 database structure.
*/ function _class_private_field_loose_base(receiver, privateKey) {
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
throw new TypeError("attempted to use private field on non-instance");
}
return receiver;
}
var id = 0;
function _class_private_field_loose_key(name) {
return "__private_" + id++ + "_" + name;
}
const IDENTIFIER_MAX_LENGTH = 55;
var // Fixed compression map for suffixes and prefixes
_replacementMap = /*#__PURE__*/ _class_private_field_loose_key("_replacementMap"), _options = /*#__PURE__*/ _class_private_field_loose_key("_options");
class Identifiers {
get replacementMap() {
return _class_private_field_loose_base(this, _replacementMap)[_replacementMap];
}
get options() {
return _class_private_field_loose_base(this, _options)[_options];
}
constructor(options){
Object.defineProperty(this, _replacementMap, {
writable: true,
value: void 0
});
Object.defineProperty(this, _options, {
writable: true,
value: void 0
});
this.ID_COLUMN = 'id';
this.ORDER_COLUMN = 'order';
this.FIELD_COLUMN = 'field';
this.HASH_LENGTH = 5;
this.HASH_SEPARATOR = '' // no separator is needed, we will just attach hash directly to shortened name
;
this.IDENTIFIER_SEPARATOR = '_';
this.MIN_TOKEN_LENGTH = 3 // the min characters required at the beginning of a name part
;
_class_private_field_loose_base(this, _replacementMap)[_replacementMap] = {
links: 'lnk',
order_inv_fk: 'oifk',
order: 'ord',
morphs: 'mph',
index: 'idx',
inv_fk: 'ifk',
order_fk: 'ofk',
id_column_index: 'idix',
order_index: 'oidx',
unique: 'uq',
primary: 'pk'
};
this.mapshortNames = (name)=>{
if (name in this.replacementMap) {
return this.replacementMap[name];
}
return undefined;
};
// Generic name handler that must be used by all helper functions
/**
* TODO: we should be requiring snake_case inputs for all names here, but we
* aren't and it will require some refactoring to make it work. Currently if
* we get names 'myModel' and 'my_model' they would be converted to the same
* final string my_model which generally works but is not entirely safe
* */ this.getName = (names, options)=>{
const tokens = _.castArray(names).map((name)=>{
return {
name,
compressible: true
};
});
if (options?.suffix) {
tokens.push({
name: options.suffix,
compressible: false,
shortName: this.mapshortNames(options.suffix)
});
}
if (options?.prefix) {
tokens.unshift({
name: options.prefix,
compressible: false,
shortName: this.mapshortNames(options.prefix)
});
}
return this.getNameFromTokens(tokens);
};
/**
* TABLES
*/ this.getTableName = (name, options)=>{
return this.getName(name, options);
};
this.getJoinTableName = (collectionName, attributeName, options)=>{
return this.getName([
collectionName,
attributeName
], {
suffix: 'links',
...options
});
};
this.getMorphTableName = (collectionName, attributeName, options)=>{
return this.getName([
_.snakeCase(collectionName),
_.snakeCase(attributeName)
], {
suffix: 'morphs',
...options
});
};
/**
* COLUMNS
*/ this.getColumnName = (attributeName, options)=>{
return this.getName(attributeName, options);
};
this.getJoinColumnAttributeIdName = (attributeName, options)=>{
return this.getName(attributeName, {
suffix: 'id',
...options
});
};
this.getInverseJoinColumnAttributeIdName = (attributeName, options)=>{
return this.getName(_.snakeCase(attributeName), {
suffix: 'id',
prefix: 'inv',
...options
});
};
this.getOrderColumnName = (singularName, options)=>{
return this.getName(singularName, {
suffix: 'order',
...options
});
};
this.getInverseOrderColumnName = (singularName, options)=>{
return this.getName(singularName, {
suffix: 'order',
prefix: 'inv',
...options
});
};
/**
* Morph Join Tables
*/ this.getMorphColumnJoinTableIdName = (singularName, options)=>{
return this.getName(_.snakeCase(singularName), {
suffix: 'id',
...options
});
};
this.getMorphColumnAttributeIdName = (attributeName, options)=>{
return this.getName(_.snakeCase(attributeName), {
suffix: 'id',
...options
});
};
this.getMorphColumnTypeName = (attributeName, options)=>{
return this.getName(_.snakeCase(attributeName), {
suffix: 'type',
...options
});
};
/**
* INDEXES
* Note that these methods are generally used to reference full table names + attribute(s), which
* may already be shortened strings rather than individual parts.
* That is fine and expected to compress the previously incompressible parts of those strings,
* because in these cases the relevant information is the table name and we can't really do
* any better; shortening the individual parts again might make it even more confusing.
*
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
* mytable_myattr4567d_loc63bf2_fk
*/ // base index types
this.getIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'index',
...options
});
};
this.getFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'fk',
...options
});
};
this.getUniqueIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'unique',
...options
});
};
this.getPrimaryIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'primary',
...options
});
};
// custom index types
this.getInverseFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'inv_fk',
...options
});
};
this.getOrderFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'order_fk',
...options
});
};
this.getOrderInverseFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'order_inv_fk',
...options
});
};
this.getIdColumnIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'id_column_index',
...options
});
};
this.getOrderIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'order_index',
...options
});
};
/**
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
*
* @example
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
* // we don't want to end up with "longstrin" and "longstrin"
* // we want something such as "longs0b23" and "longs953f"
* const token1 = generateToken("longstring1", 9); // "longs0b23"
* const token2 = generateToken("longstring2", 9); // "longs953f"
*
* @param name - The base name
* @param len - The desired length of the token.
* @returns The generated token with hash.
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
* @internal
*/ this.getShortenedName = (name, len)=>{
if (!_.isInteger(len) || len <= 0) {
throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
}
if (name.length <= len) {
return name;
}
if (len < this.MIN_TOKEN_LENGTH + this.HASH_LENGTH) {
throw new Error(`length for part of identifier too short, minimum is hash length (${this.HASH_LENGTH}) plus min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`);
}
const availableLength = len - this.HASH_LENGTH - this.HASH_SEPARATOR.length;
if (availableLength < this.MIN_TOKEN_LENGTH) {
throw new Error(`length for part of identifier minimum is less than min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`);
}
return `${name.substring(0, availableLength)}${this.HASH_SEPARATOR}${hash.createHash(name, this.HASH_LENGTH)}`;
};
/**
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
* incompressible tokens.
* @internal
*/ this.getNameFromTokens = (nameTokens)=>{
const { maxLength } = this.options;
if (!_.isInteger(maxLength) || maxLength < 0) {
throw new Error('maxLength must be a positive integer or 0 (for unlimited length)');
}
const unshortenedName = nameTokens.map((token)=>{
return token.name;
}).join(this.IDENTIFIER_SEPARATOR);
// if maxLength == 0 we want the legacy v4 name without any shortening
if (maxLength === 0) {
this.setUnshortenedName(unshortenedName, unshortenedName);
return unshortenedName;
}
// check the full length name (but with incompressible tokens using shortNames if available)
const fullLengthName = nameTokens.map((token)=>{
if (token.compressible) {
return token.name;
}
return token.shortName ?? token.name;
}).join(this.IDENTIFIER_SEPARATOR);
if (fullLengthName.length <= maxLength) {
this.setUnshortenedName(fullLengthName, unshortenedName);
return fullLengthName;
}
// Split tokens by compressibility
const [compressible, incompressible] = _.partition((token)=>token.compressible, nameTokens);
const totalIncompressibleLength = _.sumBy((token)=>token.compressible === false && token.shortName !== undefined ? token.shortName.length : token.name.length)(incompressible);
const totalSeparatorsLength = nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1;
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
const availablePerToken = Math.floor(available / compressible.length);
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < this.MIN_TOKEN_LENGTH) {
throw new Error('Maximum length is too small to accommodate all tokens');
}
// Calculate the remainder from the division and add it to the surplus
let surplus = available % compressible.length;
// Check that it's even possible to proceed
const minHashedLength = this.HASH_LENGTH + this.HASH_SEPARATOR.length + this.MIN_TOKEN_LENGTH;
const totalLength = nameTokens.reduce((total, token)=>{
if (token.compressible) {
if (token.name.length < availablePerToken) {
return total + token.name.length;
}
return total + minHashedLength;
}
const tokenName = token.shortName ?? token.name;
return total + tokenName.length;
}, nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1);
// TODO: this is the weakest thing of the shortener, but fortunately it can be improved later without a breaking change if it turns out to be a problem (for example, if there is some case we need 6+ name parts in one identifier). We could take this "shortest string we could generate" that is too long and apply the hash directly to that, which would work fine even though it would be very difficult to determine what it was actually referring to
// Check if the maximum length is less than the total length
if (maxLength < totalLength) {
throw new Error('Maximum length is too small to accommodate all tokens');
}
// Calculate total surplus length from shorter strings and total deficit length from longer strings
let deficits = [];
compressible.forEach((token)=>{
const actualLength = token.name.length;
if (actualLength < availablePerToken) {
surplus += availablePerToken - actualLength;
token.allocatedLength = actualLength;
} else {
token.allocatedLength = availablePerToken;
deficits.push(token);
}
});
// Redistribute surplus length to longer strings, one character at a time
// This way we avoid issues with greed and trying to handle floating points by dividing available length
function filterAndIncreaseLength(token) {
if (token.allocatedLength < token.name.length && surplus > 0) {
token.allocatedLength += 1;
surplus -= 1;
// if it hasn't reached its full length, keep it in array for next round
return token.allocatedLength < token.name.length;
}
return false; // Remove this token from the deficits array
}
// Redistribute surplus length to longer strings, one character at a time
let previousSurplus = surplus + 1; // infinite loop protection
while(surplus > 0 && deficits.length > 0){
deficits = deficits.filter((token)=>filterAndIncreaseLength(token));
// infinite loop protection; if the surplus hasn't changed, there was nothing left to distribute it to
if (surplus === previousSurplus) {
break;
}
previousSurplus = surplus;
}
// Build final string
const shortenedName = nameTokens.map((token)=>{
// if it is compressible, shorten it
if (token.compressible && 'allocatedLength' in token && token.allocatedLength !== undefined) {
return this.getShortenedName(token.name, token.allocatedLength);
}
// if is is only compressible as a fixed value, use that
if (token.compressible === false && token.shortName) {
return token.shortName;
}
// otherwise return it as-is
return token.name;
}).join(this.IDENTIFIER_SEPARATOR);
// this should be unreachable, but add a final check for potential edge cases we missed
if (shortenedName.length > maxLength) {
throw new Error(`name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`);
}
this.setUnshortenedName(shortenedName, unshortenedName);
return shortenedName;
};
// We need to be able to find the full-length name for any shortened name, primarily for migration purposes
// Therefore we store every name that passes through so we can retrieve the original later
this.nameMap = new Map();
this.getUnshortenedName = (shortName)=>{
return this.nameMap.get(this.serializeKey(shortName)) ?? shortName;
};
this.setUnshortenedName = (shortName, fullName)=>{
// This is protection against cases where a name is shortened twice, for example shortened in a model outside of createMetadata
// and then run through the shortener against inside createMetadata, which would do nothing at all but replace the original
// name in this mapping
if (this.nameMap.get(this.serializeKey(shortName)) && shortName === fullName) {
return;
}
// set the name
this.nameMap.set(this.serializeKey(shortName), fullName);
};
this.serializeKey = (shortName)=>{
return `${shortName}.${this.options.maxLength}`;
};
_class_private_field_loose_base(this, _options)[_options] = options;
}
}
// TODO: instead of instantiating this here as a global metadata should create its own to use
// However, that would require refactoring all of the metadata methods to be instantiated to keep a centralized identifiers
const identifiers = new Identifiers({
maxLength: IDENTIFIER_MAX_LENGTH
});
exports.Identifiers = Identifiers;
exports.identifiers = identifiers;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,411 @@
import _, { snakeCase, isInteger, partition, sumBy } from 'lodash/fp';
import { createHash } from './hash.mjs';
/**
* This file contains utility functions for generating names used in the database.
* These names include table names, column names, join table names, index names, and more.
* The generated names can be customized with prefixes, suffixes, and maximum length.
* These utility functions are used throughout the codebase to ensure consistent and standardized naming conventions in the database.
*
* The reason for checking maxLength for suffixes and prefixes and using the long ones from Strapi 4 is so that we always
* have access to the full length names, in particular for migration purposes, but also so that (in theory) the feature
* could be disabled and stay compatible with v4 database structure.
*/ function _class_private_field_loose_base(receiver, privateKey) {
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
throw new TypeError("attempted to use private field on non-instance");
}
return receiver;
}
var id = 0;
function _class_private_field_loose_key(name) {
return "__private_" + id++ + "_" + name;
}
const IDENTIFIER_MAX_LENGTH = 55;
var // Fixed compression map for suffixes and prefixes
_replacementMap = /*#__PURE__*/ _class_private_field_loose_key("_replacementMap"), _options = /*#__PURE__*/ _class_private_field_loose_key("_options");
class Identifiers {
get replacementMap() {
return _class_private_field_loose_base(this, _replacementMap)[_replacementMap];
}
get options() {
return _class_private_field_loose_base(this, _options)[_options];
}
constructor(options){
Object.defineProperty(this, _replacementMap, {
writable: true,
value: void 0
});
Object.defineProperty(this, _options, {
writable: true,
value: void 0
});
this.ID_COLUMN = 'id';
this.ORDER_COLUMN = 'order';
this.FIELD_COLUMN = 'field';
this.HASH_LENGTH = 5;
this.HASH_SEPARATOR = '' // no separator is needed, we will just attach hash directly to shortened name
;
this.IDENTIFIER_SEPARATOR = '_';
this.MIN_TOKEN_LENGTH = 3 // the min characters required at the beginning of a name part
;
_class_private_field_loose_base(this, _replacementMap)[_replacementMap] = {
links: 'lnk',
order_inv_fk: 'oifk',
order: 'ord',
morphs: 'mph',
index: 'idx',
inv_fk: 'ifk',
order_fk: 'ofk',
id_column_index: 'idix',
order_index: 'oidx',
unique: 'uq',
primary: 'pk'
};
this.mapshortNames = (name)=>{
if (name in this.replacementMap) {
return this.replacementMap[name];
}
return undefined;
};
// Generic name handler that must be used by all helper functions
/**
* TODO: we should be requiring snake_case inputs for all names here, but we
* aren't and it will require some refactoring to make it work. Currently if
* we get names 'myModel' and 'my_model' they would be converted to the same
* final string my_model which generally works but is not entirely safe
* */ this.getName = (names, options)=>{
const tokens = _.castArray(names).map((name)=>{
return {
name,
compressible: true
};
});
if (options?.suffix) {
tokens.push({
name: options.suffix,
compressible: false,
shortName: this.mapshortNames(options.suffix)
});
}
if (options?.prefix) {
tokens.unshift({
name: options.prefix,
compressible: false,
shortName: this.mapshortNames(options.prefix)
});
}
return this.getNameFromTokens(tokens);
};
/**
* TABLES
*/ this.getTableName = (name, options)=>{
return this.getName(name, options);
};
this.getJoinTableName = (collectionName, attributeName, options)=>{
return this.getName([
collectionName,
attributeName
], {
suffix: 'links',
...options
});
};
this.getMorphTableName = (collectionName, attributeName, options)=>{
return this.getName([
snakeCase(collectionName),
snakeCase(attributeName)
], {
suffix: 'morphs',
...options
});
};
/**
* COLUMNS
*/ this.getColumnName = (attributeName, options)=>{
return this.getName(attributeName, options);
};
this.getJoinColumnAttributeIdName = (attributeName, options)=>{
return this.getName(attributeName, {
suffix: 'id',
...options
});
};
this.getInverseJoinColumnAttributeIdName = (attributeName, options)=>{
return this.getName(snakeCase(attributeName), {
suffix: 'id',
prefix: 'inv',
...options
});
};
this.getOrderColumnName = (singularName, options)=>{
return this.getName(singularName, {
suffix: 'order',
...options
});
};
this.getInverseOrderColumnName = (singularName, options)=>{
return this.getName(singularName, {
suffix: 'order',
prefix: 'inv',
...options
});
};
/**
* Morph Join Tables
*/ this.getMorphColumnJoinTableIdName = (singularName, options)=>{
return this.getName(snakeCase(singularName), {
suffix: 'id',
...options
});
};
this.getMorphColumnAttributeIdName = (attributeName, options)=>{
return this.getName(snakeCase(attributeName), {
suffix: 'id',
...options
});
};
this.getMorphColumnTypeName = (attributeName, options)=>{
return this.getName(snakeCase(attributeName), {
suffix: 'type',
...options
});
};
/**
* INDEXES
* Note that these methods are generally used to reference full table names + attribute(s), which
* may already be shortened strings rather than individual parts.
* That is fine and expected to compress the previously incompressible parts of those strings,
* because in these cases the relevant information is the table name and we can't really do
* any better; shortening the individual parts again might make it even more confusing.
*
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
* mytable_myattr4567d_loc63bf2_fk
*/ // base index types
this.getIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'index',
...options
});
};
this.getFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'fk',
...options
});
};
this.getUniqueIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'unique',
...options
});
};
this.getPrimaryIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'primary',
...options
});
};
// custom index types
this.getInverseFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'inv_fk',
...options
});
};
this.getOrderFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'order_fk',
...options
});
};
this.getOrderInverseFkIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'order_inv_fk',
...options
});
};
this.getIdColumnIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'id_column_index',
...options
});
};
this.getOrderIndexName = (names, options)=>{
return this.getName(names, {
suffix: 'order_index',
...options
});
};
/**
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
*
* @example
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
* // we don't want to end up with "longstrin" and "longstrin"
* // we want something such as "longs0b23" and "longs953f"
* const token1 = generateToken("longstring1", 9); // "longs0b23"
* const token2 = generateToken("longstring2", 9); // "longs953f"
*
* @param name - The base name
* @param len - The desired length of the token.
* @returns The generated token with hash.
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
* @internal
*/ this.getShortenedName = (name, len)=>{
if (!isInteger(len) || len <= 0) {
throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
}
if (name.length <= len) {
return name;
}
if (len < this.MIN_TOKEN_LENGTH + this.HASH_LENGTH) {
throw new Error(`length for part of identifier too short, minimum is hash length (${this.HASH_LENGTH}) plus min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`);
}
const availableLength = len - this.HASH_LENGTH - this.HASH_SEPARATOR.length;
if (availableLength < this.MIN_TOKEN_LENGTH) {
throw new Error(`length for part of identifier minimum is less than min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`);
}
return `${name.substring(0, availableLength)}${this.HASH_SEPARATOR}${createHash(name, this.HASH_LENGTH)}`;
};
/**
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
* incompressible tokens.
* @internal
*/ this.getNameFromTokens = (nameTokens)=>{
const { maxLength } = this.options;
if (!isInteger(maxLength) || maxLength < 0) {
throw new Error('maxLength must be a positive integer or 0 (for unlimited length)');
}
const unshortenedName = nameTokens.map((token)=>{
return token.name;
}).join(this.IDENTIFIER_SEPARATOR);
// if maxLength == 0 we want the legacy v4 name without any shortening
if (maxLength === 0) {
this.setUnshortenedName(unshortenedName, unshortenedName);
return unshortenedName;
}
// check the full length name (but with incompressible tokens using shortNames if available)
const fullLengthName = nameTokens.map((token)=>{
if (token.compressible) {
return token.name;
}
return token.shortName ?? token.name;
}).join(this.IDENTIFIER_SEPARATOR);
if (fullLengthName.length <= maxLength) {
this.setUnshortenedName(fullLengthName, unshortenedName);
return fullLengthName;
}
// Split tokens by compressibility
const [compressible, incompressible] = partition((token)=>token.compressible, nameTokens);
const totalIncompressibleLength = sumBy((token)=>token.compressible === false && token.shortName !== undefined ? token.shortName.length : token.name.length)(incompressible);
const totalSeparatorsLength = nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1;
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
const availablePerToken = Math.floor(available / compressible.length);
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < this.MIN_TOKEN_LENGTH) {
throw new Error('Maximum length is too small to accommodate all tokens');
}
// Calculate the remainder from the division and add it to the surplus
let surplus = available % compressible.length;
// Check that it's even possible to proceed
const minHashedLength = this.HASH_LENGTH + this.HASH_SEPARATOR.length + this.MIN_TOKEN_LENGTH;
const totalLength = nameTokens.reduce((total, token)=>{
if (token.compressible) {
if (token.name.length < availablePerToken) {
return total + token.name.length;
}
return total + minHashedLength;
}
const tokenName = token.shortName ?? token.name;
return total + tokenName.length;
}, nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1);
// TODO: this is the weakest thing of the shortener, but fortunately it can be improved later without a breaking change if it turns out to be a problem (for example, if there is some case we need 6+ name parts in one identifier). We could take this "shortest string we could generate" that is too long and apply the hash directly to that, which would work fine even though it would be very difficult to determine what it was actually referring to
// Check if the maximum length is less than the total length
if (maxLength < totalLength) {
throw new Error('Maximum length is too small to accommodate all tokens');
}
// Calculate total surplus length from shorter strings and total deficit length from longer strings
let deficits = [];
compressible.forEach((token)=>{
const actualLength = token.name.length;
if (actualLength < availablePerToken) {
surplus += availablePerToken - actualLength;
token.allocatedLength = actualLength;
} else {
token.allocatedLength = availablePerToken;
deficits.push(token);
}
});
// Redistribute surplus length to longer strings, one character at a time
// This way we avoid issues with greed and trying to handle floating points by dividing available length
function filterAndIncreaseLength(token) {
if (token.allocatedLength < token.name.length && surplus > 0) {
token.allocatedLength += 1;
surplus -= 1;
// if it hasn't reached its full length, keep it in array for next round
return token.allocatedLength < token.name.length;
}
return false; // Remove this token from the deficits array
}
// Redistribute surplus length to longer strings, one character at a time
let previousSurplus = surplus + 1; // infinite loop protection
while(surplus > 0 && deficits.length > 0){
deficits = deficits.filter((token)=>filterAndIncreaseLength(token));
// infinite loop protection; if the surplus hasn't changed, there was nothing left to distribute it to
if (surplus === previousSurplus) {
break;
}
previousSurplus = surplus;
}
// Build final string
const shortenedName = nameTokens.map((token)=>{
// if it is compressible, shorten it
if (token.compressible && 'allocatedLength' in token && token.allocatedLength !== undefined) {
return this.getShortenedName(token.name, token.allocatedLength);
}
// if is is only compressible as a fixed value, use that
if (token.compressible === false && token.shortName) {
return token.shortName;
}
// otherwise return it as-is
return token.name;
}).join(this.IDENTIFIER_SEPARATOR);
// this should be unreachable, but add a final check for potential edge cases we missed
if (shortenedName.length > maxLength) {
throw new Error(`name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`);
}
this.setUnshortenedName(shortenedName, unshortenedName);
return shortenedName;
};
// We need to be able to find the full-length name for any shortened name, primarily for migration purposes
// Therefore we store every name that passes through so we can retrieve the original later
this.nameMap = new Map();
this.getUnshortenedName = (shortName)=>{
return this.nameMap.get(this.serializeKey(shortName)) ?? shortName;
};
this.setUnshortenedName = (shortName, fullName)=>{
// This is protection against cases where a name is shortened twice, for example shortened in a model outside of createMetadata
// and then run through the shortener against inside createMetadata, which would do nothing at all but replace the original
// name in this mapping
if (this.nameMap.get(this.serializeKey(shortName)) && shortName === fullName) {
return;
}
// set the name
this.nameMap.set(this.serializeKey(shortName), fullName);
};
this.serializeKey = (shortName)=>{
return `${shortName}.${this.options.maxLength}`;
};
_class_private_field_loose_base(this, _options)[_options] = options;
}
}
// TODO: instead of instantiating this here as a global metadata should create its own to use
// However, that would require refactoring all of the metadata methods to be instantiated to keep a centralized identifiers
const identifiers = new Identifiers({
maxLength: IDENTIFIER_MAX_LENGTH
});
export { Identifiers, identifiers };
//# sourceMappingURL=index.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
export type NameInput = string | string[];
export type CompressibleNameToken = {
compressible: true;
name: string;
allocatedLength?: number;
};
export type IncompressibleNameToken = {
compressible: false;
name: string;
allocatedLength?: number;
shortName?: string;
};
export type NameToken = CompressibleNameToken | IncompressibleNameToken;
export type NameFromTokenOptions = {
maxLength: number;
};
export type IdentifiersOptions = {
maxLength: number;
};
export type NameOptions = {
suffix?: string;
prefix?: string;
};
export type NameTokenWithAllocation = NameToken & {
allocatedLength: number;
};
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/identifiers/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;AAE1C,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,EAAE,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,EAAE,KAAK,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,qBAAqB,GAAG,uBAAuB,CAAC;AAExE,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC"}

View File

@@ -0,0 +1,12 @@
import type { Knex } from 'knex';
import type { Database } from '..';
/**
* @internal
*/
export declare function isKnexQuery(value: unknown): value is Knex.Raw | Knex.QueryBuilder;
/**
* Adds the name of the schema to the table name if the schema was defined by the user.
* Users can set the db schema only for Postgres in strapi database config.
*/
export declare const addSchema: (db: Database, tableName: string) => string;
//# sourceMappingURL=knex.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"knex.d.ts","sourceRoot":"","sources":["../../src/utils/knex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAKjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAEnC;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,YAAY,CAEjF;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,OAAQ,QAAQ,aAAa,MAAM,KAAG,MAG3D,CAAC"}

View File

@@ -0,0 +1,21 @@
'use strict';
var KnexBuilder = require('knex/lib/query/querybuilder');
var KnexRaw = require('knex/lib/raw');
/**
* @internal
*/ function isKnexQuery(value) {
return value instanceof KnexBuilder || value instanceof KnexRaw;
}
/**
* Adds the name of the schema to the table name if the schema was defined by the user.
* Users can set the db schema only for Postgres in strapi database config.
*/ const addSchema = (db, tableName)=>{
const schemaName = db.getSchemaName();
return schemaName ? `${schemaName}.${tableName}` : tableName;
};
exports.addSchema = addSchema;
exports.isKnexQuery = isKnexQuery;
//# sourceMappingURL=knex.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"knex.js","sources":["../../src/utils/knex.ts"],"sourcesContent":["import type { Knex } from 'knex';\n\nimport KnexBuilder from 'knex/lib/query/querybuilder';\nimport KnexRaw from 'knex/lib/raw';\n\nimport type { Database } from '..';\n\n/**\n * @internal\n */\nexport function isKnexQuery(value: unknown): value is Knex.Raw | Knex.QueryBuilder {\n return value instanceof KnexBuilder || value instanceof KnexRaw;\n}\n\n/**\n * Adds the name of the schema to the table name if the schema was defined by the user.\n * Users can set the db schema only for Postgres in strapi database config.\n */\nexport const addSchema = (db: Database, tableName: string): string => {\n const schemaName = db.getSchemaName();\n return schemaName ? `${schemaName}.${tableName}` : tableName;\n};\n"],"names":["isKnexQuery","value","KnexBuilder","KnexRaw","addSchema","db","tableName","schemaName","getSchemaName"],"mappings":";;;;;AAOA;;IAGO,SAASA,WAAAA,CAAYC,KAAc,EAAA;IACxC,OAAOA,KAAAA,YAAiBC,eAAeD,KAAiBE,YAAAA,OAAAA;AAC1D;AAEA;;;AAGC,IACM,MAAMC,SAAY,GAAA,CAACC,EAAcC,EAAAA,SAAAA,GAAAA;IACtC,MAAMC,UAAAA,GAAaF,GAAGG,aAAa,EAAA;IACnC,OAAOD,UAAAA,GAAa,CAAC,EAAEA,UAAAA,CAAW,CAAC,EAAED,SAAAA,CAAU,CAAC,GAAGA,SAAAA;AACrD;;;;;"}

View File

@@ -0,0 +1,18 @@
import KnexBuilder from 'knex/lib/query/querybuilder';
import KnexRaw from 'knex/lib/raw';
/**
* @internal
*/ function isKnexQuery(value) {
return value instanceof KnexBuilder || value instanceof KnexRaw;
}
/**
* Adds the name of the schema to the table name if the schema was defined by the user.
* Users can set the db schema only for Postgres in strapi database config.
*/ const addSchema = (db, tableName)=>{
const schemaName = db.getSchemaName();
return schemaName ? `${schemaName}.${tableName}` : tableName;
};
export { addSchema, isKnexQuery };
//# sourceMappingURL=knex.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"knex.mjs","sources":["../../src/utils/knex.ts"],"sourcesContent":["import type { Knex } from 'knex';\n\nimport KnexBuilder from 'knex/lib/query/querybuilder';\nimport KnexRaw from 'knex/lib/raw';\n\nimport type { Database } from '..';\n\n/**\n * @internal\n */\nexport function isKnexQuery(value: unknown): value is Knex.Raw | Knex.QueryBuilder {\n return value instanceof KnexBuilder || value instanceof KnexRaw;\n}\n\n/**\n * Adds the name of the schema to the table name if the schema was defined by the user.\n * Users can set the db schema only for Postgres in strapi database config.\n */\nexport const addSchema = (db: Database, tableName: string): string => {\n const schemaName = db.getSchemaName();\n return schemaName ? `${schemaName}.${tableName}` : tableName;\n};\n"],"names":["isKnexQuery","value","KnexBuilder","KnexRaw","addSchema","db","tableName","schemaName","getSchemaName"],"mappings":";;;AAOA;;IAGO,SAASA,WAAAA,CAAYC,KAAc,EAAA;IACxC,OAAOA,KAAAA,YAAiBC,eAAeD,KAAiBE,YAAAA,OAAAA;AAC1D;AAEA;;;AAGC,IACM,MAAMC,SAAY,GAAA,CAACC,EAAcC,EAAAA,SAAAA,GAAAA;IACtC,MAAMC,UAAAA,GAAaF,GAAGG,aAAa,EAAA;IACnC,OAAOD,UAAAA,GAAa,CAAC,EAAEA,UAAAA,CAAW,CAAC,EAAED,SAAAA,CAAU,CAAC,GAAGA,SAAAA;AACrD;;;;"}

View File

@@ -0,0 +1,8 @@
import type { Attribute, ScalarAttribute, RelationalAttribute } from '../types';
export declare const isString: (type: string) => boolean;
export declare const isNumber: (type: string) => boolean;
export declare const isScalar: (type: string) => boolean;
export declare const isRelation: (type: string) => boolean;
export declare const isScalarAttribute: (attribute: Attribute) => attribute is ScalarAttribute;
export declare const isRelationalAttribute: (attribute: Attribute) => attribute is RelationalAttribute;
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AA2BhF,eAAO,MAAM,QAAQ,SAAU,MAAM,YAAgC,CAAC;AACtE,eAAO,MAAM,QAAQ,SAAU,MAAM,YAAgC,CAAC;AACtE,eAAO,MAAM,QAAQ,SAAU,MAAM,YAAgC,CAAC;AACtE,eAAO,MAAM,UAAU,SAAU,MAAM,YAAwB,CAAC;AAChE,eAAO,MAAM,iBAAiB,cAAe,SAAS,iCAC5B,CAAC;AAC3B,eAAO,MAAM,qBAAqB,cAAe,SAAS,qCAC9B,CAAC"}

View File

@@ -0,0 +1,51 @@
'use strict';
const SCALAR_TYPES = [
'increments',
'password',
'email',
'string',
'uid',
'richtext',
'text',
'json',
'enumeration',
'integer',
'biginteger',
'float',
'decimal',
'date',
'time',
'datetime',
'timestamp',
'boolean',
'blocks'
];
const STRING_TYPES = [
'string',
'text',
'uid',
'email',
'enumeration',
'richtext'
];
const NUMBER_TYPES = [
'biginteger',
'integer',
'decimal',
'float'
];
const isString = (type)=>STRING_TYPES.includes(type);
const isNumber = (type)=>NUMBER_TYPES.includes(type);
const isScalar = (type)=>SCALAR_TYPES.includes(type);
const isRelation = (type)=>type === 'relation';
const isScalarAttribute = (attribute)=>isScalar(attribute.type);
const isRelationalAttribute = (attribute)=>isRelation(attribute.type);
exports.isNumber = isNumber;
exports.isRelation = isRelation;
exports.isRelationalAttribute = isRelationalAttribute;
exports.isScalar = isScalar;
exports.isScalarAttribute = isScalarAttribute;
exports.isString = isString;
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sources":["../../src/utils/types.ts"],"sourcesContent":["import type { Attribute, ScalarAttribute, RelationalAttribute } from '../types';\n\nconst SCALAR_TYPES = [\n 'increments',\n 'password',\n 'email',\n 'string',\n 'uid',\n 'richtext',\n 'text',\n 'json',\n 'enumeration',\n 'integer',\n 'biginteger',\n 'float',\n 'decimal',\n 'date',\n 'time',\n 'datetime',\n 'timestamp',\n 'boolean',\n 'blocks',\n];\n\nconst STRING_TYPES = ['string', 'text', 'uid', 'email', 'enumeration', 'richtext'];\nconst NUMBER_TYPES = ['biginteger', 'integer', 'decimal', 'float'];\n\nexport const isString = (type: string) => STRING_TYPES.includes(type);\nexport const isNumber = (type: string) => NUMBER_TYPES.includes(type);\nexport const isScalar = (type: string) => SCALAR_TYPES.includes(type);\nexport const isRelation = (type: string) => type === 'relation';\nexport const isScalarAttribute = (attribute: Attribute): attribute is ScalarAttribute =>\n isScalar(attribute.type);\nexport const isRelationalAttribute = (attribute: Attribute): attribute is RelationalAttribute =>\n isRelation(attribute.type);\n"],"names":["SCALAR_TYPES","STRING_TYPES","NUMBER_TYPES","isString","type","includes","isNumber","isScalar","isRelation","isScalarAttribute","attribute","isRelationalAttribute"],"mappings":";;AAEA,MAAMA,YAAe,GAAA;AACnB,IAAA,YAAA;AACA,IAAA,UAAA;AACA,IAAA,OAAA;AACA,IAAA,QAAA;AACA,IAAA,KAAA;AACA,IAAA,UAAA;AACA,IAAA,MAAA;AACA,IAAA,MAAA;AACA,IAAA,aAAA;AACA,IAAA,SAAA;AACA,IAAA,YAAA;AACA,IAAA,OAAA;AACA,IAAA,SAAA;AACA,IAAA,MAAA;AACA,IAAA,MAAA;AACA,IAAA,UAAA;AACA,IAAA,WAAA;AACA,IAAA,SAAA;AACA,IAAA;AACD,CAAA;AAED,MAAMC,YAAe,GAAA;AAAC,IAAA,QAAA;AAAU,IAAA,MAAA;AAAQ,IAAA,KAAA;AAAO,IAAA,OAAA;AAAS,IAAA,aAAA;AAAe,IAAA;AAAW,CAAA;AAClF,MAAMC,YAAe,GAAA;AAAC,IAAA,YAAA;AAAc,IAAA,SAAA;AAAW,IAAA,SAAA;AAAW,IAAA;AAAQ,CAAA;MAErDC,QAAW,GAAA,CAACC,OAAiBH,YAAaI,CAAAA,QAAQ,CAACD,IAAM;MACzDE,QAAW,GAAA,CAACF,OAAiBF,YAAaG,CAAAA,QAAQ,CAACD,IAAM;MACzDG,QAAW,GAAA,CAACH,OAAiBJ,YAAaK,CAAAA,QAAQ,CAACD,IAAM;AACzDI,MAAAA,UAAAA,GAAa,CAACJ,IAAAA,GAAiBA,SAAS;MACxCK,iBAAoB,GAAA,CAACC,YAChCH,QAASG,CAAAA,SAAAA,CAAUN,IAAI;MACZO,qBAAwB,GAAA,CAACD,YACpCF,UAAWE,CAAAA,SAAAA,CAAUN,IAAI;;;;;;;;;"}

View File

@@ -0,0 +1,44 @@
const SCALAR_TYPES = [
'increments',
'password',
'email',
'string',
'uid',
'richtext',
'text',
'json',
'enumeration',
'integer',
'biginteger',
'float',
'decimal',
'date',
'time',
'datetime',
'timestamp',
'boolean',
'blocks'
];
const STRING_TYPES = [
'string',
'text',
'uid',
'email',
'enumeration',
'richtext'
];
const NUMBER_TYPES = [
'biginteger',
'integer',
'decimal',
'float'
];
const isString = (type)=>STRING_TYPES.includes(type);
const isNumber = (type)=>NUMBER_TYPES.includes(type);
const isScalar = (type)=>SCALAR_TYPES.includes(type);
const isRelation = (type)=>type === 'relation';
const isScalarAttribute = (attribute)=>isScalar(attribute.type);
const isRelationalAttribute = (attribute)=>isRelation(attribute.type);
export { isNumber, isRelation, isRelationalAttribute, isScalar, isScalarAttribute, isString };
//# sourceMappingURL=types.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.mjs","sources":["../../src/utils/types.ts"],"sourcesContent":["import type { Attribute, ScalarAttribute, RelationalAttribute } from '../types';\n\nconst SCALAR_TYPES = [\n 'increments',\n 'password',\n 'email',\n 'string',\n 'uid',\n 'richtext',\n 'text',\n 'json',\n 'enumeration',\n 'integer',\n 'biginteger',\n 'float',\n 'decimal',\n 'date',\n 'time',\n 'datetime',\n 'timestamp',\n 'boolean',\n 'blocks',\n];\n\nconst STRING_TYPES = ['string', 'text', 'uid', 'email', 'enumeration', 'richtext'];\nconst NUMBER_TYPES = ['biginteger', 'integer', 'decimal', 'float'];\n\nexport const isString = (type: string) => STRING_TYPES.includes(type);\nexport const isNumber = (type: string) => NUMBER_TYPES.includes(type);\nexport const isScalar = (type: string) => SCALAR_TYPES.includes(type);\nexport const isRelation = (type: string) => type === 'relation';\nexport const isScalarAttribute = (attribute: Attribute): attribute is ScalarAttribute =>\n isScalar(attribute.type);\nexport const isRelationalAttribute = (attribute: Attribute): attribute is RelationalAttribute =>\n isRelation(attribute.type);\n"],"names":["SCALAR_TYPES","STRING_TYPES","NUMBER_TYPES","isString","type","includes","isNumber","isScalar","isRelation","isScalarAttribute","attribute","isRelationalAttribute"],"mappings":"AAEA,MAAMA,YAAe,GAAA;AACnB,IAAA,YAAA;AACA,IAAA,UAAA;AACA,IAAA,OAAA;AACA,IAAA,QAAA;AACA,IAAA,KAAA;AACA,IAAA,UAAA;AACA,IAAA,MAAA;AACA,IAAA,MAAA;AACA,IAAA,aAAA;AACA,IAAA,SAAA;AACA,IAAA,YAAA;AACA,IAAA,OAAA;AACA,IAAA,SAAA;AACA,IAAA,MAAA;AACA,IAAA,MAAA;AACA,IAAA,UAAA;AACA,IAAA,WAAA;AACA,IAAA,SAAA;AACA,IAAA;AACD,CAAA;AAED,MAAMC,YAAe,GAAA;AAAC,IAAA,QAAA;AAAU,IAAA,MAAA;AAAQ,IAAA,KAAA;AAAO,IAAA,OAAA;AAAS,IAAA,aAAA;AAAe,IAAA;AAAW,CAAA;AAClF,MAAMC,YAAe,GAAA;AAAC,IAAA,YAAA;AAAc,IAAA,SAAA;AAAW,IAAA,SAAA;AAAW,IAAA;AAAQ,CAAA;MAErDC,QAAW,GAAA,CAACC,OAAiBH,YAAaI,CAAAA,QAAQ,CAACD,IAAM;MACzDE,QAAW,GAAA,CAACF,OAAiBF,YAAaG,CAAAA,QAAQ,CAACD,IAAM;MACzDG,QAAW,GAAA,CAACH,OAAiBJ,YAAaK,CAAAA,QAAQ,CAACD,IAAM;AACzDI,MAAAA,UAAAA,GAAa,CAACJ,IAAAA,GAAiBA,SAAS;MACxCK,iBAAoB,GAAA,CAACC,YAChCH,QAASG,CAAAA,SAAAA,CAAUN,IAAI;MACZO,qBAAwB,GAAA,CAACD,YACpCF,UAAWE,CAAAA,SAAAA,CAAUN,IAAI;;;;"}