@forestadmin-experimental/datasource-cosmos 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +512 -0
  2. package/dist/collection.d.ts +14 -0
  3. package/dist/collection.d.ts.map +1 -0
  4. package/dist/collection.js +90 -0
  5. package/dist/datasource.d.ts +26 -0
  6. package/dist/datasource.d.ts.map +1 -0
  7. package/dist/datasource.js +87 -0
  8. package/dist/index.d.ts +71 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +126 -0
  11. package/dist/introspection/builder.d.ts +68 -0
  12. package/dist/introspection/builder.d.ts.map +1 -0
  13. package/dist/introspection/builder.js +31 -0
  14. package/dist/introspection/container-introspector.d.ts +27 -0
  15. package/dist/introspection/container-introspector.d.ts.map +1 -0
  16. package/dist/introspection/container-introspector.js +168 -0
  17. package/dist/introspection/introspector.d.ts +18 -0
  18. package/dist/introspection/introspector.d.ts.map +1 -0
  19. package/dist/introspection/introspector.js +55 -0
  20. package/dist/model-builder/model.d.ts +79 -0
  21. package/dist/model-builder/model.d.ts.map +1 -0
  22. package/dist/model-builder/model.js +176 -0
  23. package/dist/utils/aggregation-converter.d.ts +30 -0
  24. package/dist/utils/aggregation-converter.d.ts.map +1 -0
  25. package/dist/utils/aggregation-converter.js +181 -0
  26. package/dist/utils/model-to-collection-schema-converter.d.ts +9 -0
  27. package/dist/utils/model-to-collection-schema-converter.d.ts.map +1 -0
  28. package/dist/utils/model-to-collection-schema-converter.js +72 -0
  29. package/dist/utils/query-converter.d.ts +38 -0
  30. package/dist/utils/query-converter.d.ts.map +1 -0
  31. package/dist/utils/query-converter.js +199 -0
  32. package/dist/utils/serializer.d.ts +6 -0
  33. package/dist/utils/serializer.d.ts.map +1 -0
  34. package/dist/utils/serializer.js +29 -0
  35. package/dist/utils/type-converter.d.ts +43 -0
  36. package/dist/utils/type-converter.d.ts.map +1 -0
  37. package/dist/utils/type-converter.js +157 -0
  38. package/package.json +36 -0
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
7
+ const collection_1 = __importDefault(require("./collection"));
8
+ class CosmosDataSource extends datasource_toolkit_1.BaseDataSource {
9
+ constructor(cosmosClient, collectionModels, logger, options) {
10
+ super();
11
+ if (!cosmosClient)
12
+ throw new Error('Invalid (null) CosmosClient instance.');
13
+ this.cosmosClient = cosmosClient;
14
+ // Creating collections
15
+ this.createCollections(collectionModels, logger);
16
+ if (options?.liveQueryConnections && options?.liveQueryDatabase) {
17
+ this.addNativeQueryConnection(options.liveQueryConnections, {
18
+ instance: this.cosmosClient,
19
+ databaseName: options.liveQueryDatabase,
20
+ });
21
+ }
22
+ logger?.('Info', 'CosmosDataSource - Built');
23
+ }
24
+ async createCollections(collectionModels, logger) {
25
+ collectionModels
26
+ // avoid schema reordering
27
+ .sort((modelA, modelB) => (modelA.name > modelB.name ? 1 : -1))
28
+ .forEach(model => {
29
+ const collection = new collection_1.default(this, model, logger, this.cosmosClient);
30
+ this.addCollection(collection);
31
+ });
32
+ }
33
+ async executeNativeQuery(connectionName, query, contextVariables = {}) {
34
+ if (!this.nativeQueryConnections[connectionName]) {
35
+ throw new Error(`Unknown connection name '${connectionName}'`);
36
+ }
37
+ const connection = this.nativeQueryConnections[connectionName];
38
+ const { querySpec, containerName } = this.parseQuery(query, contextVariables);
39
+ // Execute the query on the specified container
40
+ const database = connection.instance.database(connection.databaseName);
41
+ const container = database.container(containerName);
42
+ const { resources } = await container.items.query(querySpec).fetchAll();
43
+ return resources;
44
+ }
45
+ /**
46
+ * Parse the query string and extract container name and build SqlQuerySpec
47
+ * Expected format: "SELECT * FROM containerName WHERE ..."
48
+ */
49
+ parseQuery(query, contextVariables) {
50
+ // Extract container name from query (simple regex-based approach)
51
+ const fromMatch = query.match(/FROM\s+(\w+)/i);
52
+ if (!fromMatch) {
53
+ throw new Error('Invalid query: could not extract container name from FROM clause');
54
+ }
55
+ const containerName = fromMatch[1];
56
+ // Replace placeholders in query with parameterized values
57
+ const { parameterizedQuery, parameters } = this.replacePlaceholders(query, contextVariables);
58
+ const querySpec = {
59
+ query: parameterizedQuery,
60
+ parameters,
61
+ };
62
+ return { querySpec, containerName };
63
+ }
64
+ /**
65
+ * Replace placeholders ($variableName) with Cosmos DB parameters (@paramN)
66
+ */
67
+ replacePlaceholders(query, values) {
68
+ const parameters = [];
69
+ let paramCounter = 0;
70
+ const placeholderRegex = /\$(\w+)/g;
71
+ const parameterizedQuery = query.replace(placeholderRegex, (match, key) => {
72
+ if (key in values) {
73
+ const paramName = `@param${paramCounter}`;
74
+ paramCounter += 1;
75
+ // Type assertion: contextVariables should contain JSON-serializable values
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ parameters.push({ name: paramName, value: values[key] });
78
+ return paramName;
79
+ }
80
+ // If the variable is not in contextVariables, keep the original placeholder
81
+ return match;
82
+ });
83
+ return { parameterizedQuery, parameters };
84
+ }
85
+ }
86
+ exports.default = CosmosDataSource;
87
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YXNvdXJjZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9kYXRhc291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQ0Esd0VBQXlFO0FBRXpFLDhEQUE0QztBQVE1QyxNQUFxQixnQkFBaUIsU0FBUSxtQ0FBZ0M7SUFNNUUsWUFDRSxZQUEwQixFQUMxQixnQkFBK0IsRUFDL0IsTUFBYyxFQUNkLE9BQXVFO1FBRXZFLEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxDQUFDLFlBQVk7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFFakMsdUJBQXVCO1FBQ3ZCLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUVqRCxJQUFJLE9BQU8sRUFBRSxvQkFBb0IsSUFBSSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsQ0FBQztZQUNoRSxJQUFJLENBQUMsd0JBQXdCLENBQUMsT0FBTyxDQUFDLG9CQUFvQixFQUFFO2dCQUMxRCxRQUFRLEVBQUUsSUFBSSxDQUFDLFlBQVk7Z0JBQzNCLFlBQVksRUFBRSxPQUFPLENBQUMsaUJBQWlCO2FBQ3hDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCxNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsMEJBQTBCLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBRVMsS0FBSyxDQUFDLGlCQUFpQixDQUFDLGdCQUErQixFQUFFLE1BQWM7UUFDL0UsZ0JBQWdCO1lBQ2QsMEJBQTBCO2FBQ3pCLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDOUQsT0FBTyxDQUFD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
@@ -0,0 +1,71 @@
1
+ import { CosmosClient, CosmosClientOptions } from '@azure/cosmos';
2
+ import { DataSourceFactory } from '@forestadmin/datasource-toolkit';
3
+ import { ConfigurationOptions } from './introspection/builder';
4
+ export { default as CosmosCollection } from './collection';
5
+ export { default as CosmosDataSource } from './datasource';
6
+ export { default as TypeConverter } from './utils/type-converter';
7
+ export { default as ModelCosmos } from './model-builder/model';
8
+ export type { CosmosSchema } from './model-builder/model';
9
+ /**
10
+ * Create a Cosmos DB datasource with an existing CosmosClient instance
11
+ * @param client Existing CosmosClient instance
12
+ * @param databaseName The Cosmos DB database name to introspect
13
+ * @param options Optional configuration options
14
+ * @example
15
+ * .createCosmosDataSourceWithExistingClient(existingClient, 'myDatabase', configurator =>
16
+ * configurator.addCollectionFromContainer({
17
+ * name: 'Users',
18
+ * databaseName: 'myDatabase',
19
+ * containerName: 'users-container'
20
+ * })
21
+ * )
22
+ */
23
+ export declare function createCosmosDataSourceWithExistingClient(client: CosmosClient, databaseName?: string): DataSourceFactory;
24
+ /**
25
+ * Create a Cosmos DB datasource with connection details
26
+ * @param endpoint The Cosmos DB endpoint URL
27
+ * @param key The Cosmos DB access key
28
+ * @param databaseName Optional database name for auto-introspection
29
+ * @param options Optional configuration options
30
+ * @example
31
+ * .createCosmosDataSource(
32
+ * 'https://myaccount.documents.azure.com:443/',
33
+ * 'myAccessKey',
34
+ * 'myDatabase',
35
+ * {
36
+ * builder: configurator =>
37
+ * configurator.addCollectionFromContainer({
38
+ * name: 'Users',
39
+ * databaseName: 'myDatabase',
40
+ * containerName: 'users-container'
41
+ * })
42
+ * }
43
+ * )
44
+ */
45
+ export declare function createCosmosDataSource(endpoint: string, key: string, databaseName?: string, options?: {
46
+ liveQueryConnections?: string;
47
+ liveQueryDatabase?: string;
48
+ builder?: ConfigurationOptions;
49
+ clientOptions?: CosmosClientOptions;
50
+ }): DataSourceFactory;
51
+ /**
52
+ * Create a Cosmos DB datasource for the local emulator
53
+ * @param databaseName Optional database name for auto-introspection
54
+ * @param options Optional configuration options
55
+ * @example
56
+ * .createCosmosDataSourceForEmulator('myDatabase', {
57
+ * builder: configurator =>
58
+ * configurator.addCollectionFromContainer({
59
+ * name: 'Users',
60
+ * databaseName: 'myDatabase',
61
+ * containerName: 'users'
62
+ * })
63
+ * })
64
+ */
65
+ export declare function createCosmosDataSourceForEmulator(databaseName?: string, options?: {
66
+ liveQueryConnections?: string;
67
+ liveQueryDatabase?: string;
68
+ builder?: ConfigurationOptions;
69
+ clientOptions?: CosmosClientOptions;
70
+ }): DataSourceFactory;
71
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAU,MAAM,iCAAiC,CAAC;AAG5E,OAAO,EAAE,oBAAoB,EAA2B,MAAM,yBAAyB,CAAC;AAGxF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wCAAwC,CACtD,MAAM,EAAE,YAAY,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAqBnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC,GACA,iBAAiB,CAkCnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iCAAiC,CAC/C,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC,GACA,iBAAiB,CAOnB"}
package/dist/index.js ADDED
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createCosmosDataSourceForEmulator = exports.createCosmosDataSource = exports.createCosmosDataSourceWithExistingClient = exports.ModelCosmos = exports.TypeConverter = exports.CosmosDataSource = exports.CosmosCollection = void 0;
7
+ const cosmos_1 = require("@azure/cosmos");
8
+ const datasource_1 = __importDefault(require("./datasource"));
9
+ const builder_1 = require("./introspection/builder");
10
+ const introspector_1 = __importDefault(require("./introspection/introspector"));
11
+ var collection_1 = require("./collection");
12
+ Object.defineProperty(exports, "CosmosCollection", { enumerable: true, get: function () { return __importDefault(collection_1).default; } });
13
+ var datasource_2 = require("./datasource");
14
+ Object.defineProperty(exports, "CosmosDataSource", { enumerable: true, get: function () { return __importDefault(datasource_2).default; } });
15
+ var type_converter_1 = require("./utils/type-converter");
16
+ Object.defineProperty(exports, "TypeConverter", { enumerable: true, get: function () { return __importDefault(type_converter_1).default; } });
17
+ var model_1 = require("./model-builder/model");
18
+ Object.defineProperty(exports, "ModelCosmos", { enumerable: true, get: function () { return __importDefault(model_1).default; } });
19
+ /**
20
+ * Create a Cosmos DB datasource with an existing CosmosClient instance
21
+ * @param client Existing CosmosClient instance
22
+ * @param databaseName The Cosmos DB database name to introspect
23
+ * @param options Optional configuration options
24
+ * @example
25
+ * .createCosmosDataSourceWithExistingClient(existingClient, 'myDatabase', configurator =>
26
+ * configurator.addCollectionFromContainer({
27
+ * name: 'Users',
28
+ * databaseName: 'myDatabase',
29
+ * containerName: 'users-container'
30
+ * })
31
+ * )
32
+ */
33
+ function createCosmosDataSourceWithExistingClient(client, databaseName) {
34
+ return async (logger, options) => {
35
+ let collectionModels;
36
+ if (options) {
37
+ const builder = options(new builder_1.CosmosDatasourceBuilder(client));
38
+ collectionModels = await builder.createCollectionsFromConfiguration();
39
+ }
40
+ else if (databaseName) {
41
+ collectionModels = await introspector_1.default.introspect(client, databaseName, logger);
42
+ }
43
+ else {
44
+ collectionModels = [];
45
+ }
46
+ if (collectionModels.length === 0 && !options) {
47
+ const message = 'No collections were introspected. Please provide a databaseName or use the builder.';
48
+ logger?.('Warn', message);
49
+ }
50
+ return new datasource_1.default(client, collectionModels, logger);
51
+ };
52
+ }
53
+ exports.createCosmosDataSourceWithExistingClient = createCosmosDataSourceWithExistingClient;
54
+ /**
55
+ * Create a Cosmos DB datasource with connection details
56
+ * @param endpoint The Cosmos DB endpoint URL
57
+ * @param key The Cosmos DB access key
58
+ * @param databaseName Optional database name for auto-introspection
59
+ * @param options Optional configuration options
60
+ * @example
61
+ * .createCosmosDataSource(
62
+ * 'https://myaccount.documents.azure.com:443/',
63
+ * 'myAccessKey',
64
+ * 'myDatabase',
65
+ * {
66
+ * builder: configurator =>
67
+ * configurator.addCollectionFromContainer({
68
+ * name: 'Users',
69
+ * databaseName: 'myDatabase',
70
+ * containerName: 'users-container'
71
+ * })
72
+ * }
73
+ * )
74
+ */
75
+ function createCosmosDataSource(endpoint, key, databaseName, options) {
76
+ return async (logger) => {
77
+ const client = new cosmos_1.CosmosClient({
78
+ endpoint,
79
+ key,
80
+ ...options?.clientOptions,
81
+ });
82
+ const { liveQueryConnections, liveQueryDatabase, builder } = options || {};
83
+ let collectionModels;
84
+ if (builder) {
85
+ const datasourceBuilder = builder(new builder_1.CosmosDatasourceBuilder(client));
86
+ collectionModels = await datasourceBuilder.createCollectionsFromConfiguration();
87
+ }
88
+ else if (databaseName) {
89
+ collectionModels = await introspector_1.default.introspect(client, databaseName, logger);
90
+ }
91
+ else {
92
+ collectionModels = [];
93
+ }
94
+ if (collectionModels.length === 0 && !builder) {
95
+ const message = 'No collections were introspected. Please provide a databaseName or use the builder.';
96
+ logger?.('Warn', message);
97
+ }
98
+ return new datasource_1.default(client, collectionModels, logger, {
99
+ liveQueryConnections,
100
+ liveQueryDatabase,
101
+ });
102
+ };
103
+ }
104
+ exports.createCosmosDataSource = createCosmosDataSource;
105
+ /**
106
+ * Create a Cosmos DB datasource for the local emulator
107
+ * @param databaseName Optional database name for auto-introspection
108
+ * @param options Optional configuration options
109
+ * @example
110
+ * .createCosmosDataSourceForEmulator('myDatabase', {
111
+ * builder: configurator =>
112
+ * configurator.addCollectionFromContainer({
113
+ * name: 'Users',
114
+ * databaseName: 'myDatabase',
115
+ * containerName: 'users'
116
+ * })
117
+ * })
118
+ */
119
+ function createCosmosDataSourceForEmulator(databaseName, options) {
120
+ // Default emulator connection details
121
+ const endpoint = 'https://localhost:8081';
122
+ const key = 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==';
123
+ return createCosmosDataSource(endpoint, key, databaseName, options);
124
+ }
125
+ exports.createCosmosDataSourceForEmulator = createCosmosDataSourceForEmulator;
126
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMENBQWtFO0FBR2xFLDhEQUE0QztBQUM1QyxxREFBd0Y7QUFDeEYsZ0ZBQXdEO0FBRXhELDJDQUEyRDtBQUFsRCwrSEFBQSxPQUFPLE9BQW9CO0FBQ3BDLDJDQUEyRDtBQUFsRCwrSEFBQSxPQUFPLE9BQW9CO0FBQ3BDLHlEQUFrRTtBQUF6RCxnSUFBQSxPQUFPLE9BQWlCO0FBQ2pDLCtDQUErRDtBQUF0RCxxSEFBQSxPQUFPLE9BQWU7QUFHL0I7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILFNBQWdCLHdDQUF3QyxDQUN0RCxNQUFvQixFQUNwQixZQUFxQjtJQUVyQixPQUFPLEtBQUssRUFBRSxNQUFjLEVBQUUsT0FBOEIsRUFBRSxFQUFFO1FBQzlELElBQUksZ0JBQWdCLENBQUM7UUFFckIsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNaLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLGlDQUF1QixDQUFDLE1BQU0sQ0FBQyxDQUE0QixDQUFDO1lBQ3hGLGdCQUFnQixHQUFHLE1BQU0sT0FBTyxDQUFDLGtDQUFrQyxFQUFFLENBQUM7UUFDeEUsQ0FBQzthQUFNLElBQUksWUFBWSxFQUFFLENBQUM7WUFDeEIsZ0JBQWdCLEdBQUcsTUFBTSxzQkFBWSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2pGLENBQUM7YUFBTSxDQUFDO1lBQ04sZ0JBQWdCLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLENBQUM7UUFFRCxJQUFJLGdCQUFnQixDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUM5QyxNQUFNLE9BQU8sR0FDWCxxRkFBcUYsQ0FBQztZQUN4RixNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDNUIsQ0FBQztRQUVELE9BQU8sSUFBSSxvQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDaEUsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQXhCRCw0RkF3QkM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFDSCxTQUFnQixzQkFBc0IsQ0FDcEMsUUFBZ0IsRUFDaEIsR0FBVyxFQUNYLFlBQXFCLEVBQ3JCLE9BS0M7SUFFRCxPQUFPLEtBQUssRUFBRSxNQUFjLEVBQUUsRUFBRTtRQUM5QixNQUFNLE1BQU0sR0FBRyxJQUFJLHFCQUFZLENBQUM7WUFDOUIsUUFBUTtZQUNSLEdBQUc7WUFDSCxHQUFHLE9BQU8sRUFBRSxhQUFhO1NBQzFCLENBQUMsQ0FBQztRQUVILE1BQU0sRUFBRSxvQkFBb0IsRUFBRSxpQkFBaUIsRUFBRSxPQUFPLEVBQUUsR0FBRyxPQUFPLElBQUksRUFBRSxDQUFDO1FBRTNFLElBQUksZ0JBQWdCLENBQUM7UUFFckIsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNaLE1BQU0saUJBQWlCLEdBQUcsT0FBTyxDQUMvQixJQUFJLGlDQUF1QixDQUFDLE1BQU0sQ0FBQyxDQUNULENBQUM7WUFDN0IsZ0JBQWdCLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxrQ0FBa0MsRUFBRSxDQUFDO1FBQ2xGLENBQUM7YUFBTSxJQUFJLFlBQVksRUFBRSxDQUFDO1lBQ3hCLGdCQUFnQixHQUFHLE1BQU0sc0JBQVksQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLFlBQVksRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNqRixDQUFDO2FBQU0sQ0FBQztZQUNOLGdCQUFnQixHQUFHLEVBQUUsQ0FBQztRQUN4QixDQUFDO1FBRUQsSUFBSSxnQkFBZ0IsQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDOUMsTUFBTSxPQUFPLEdBQ1gscUZBQXFGLENBQUM7WUFDeEYsTUFBTSxFQUFFLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzVCLENBQUM7UUFFRCxPQUFPLElBQUksb0JBQWdCLENBQUMsTUFBTSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sRUFBRTtZQUM1RCxvQkFBb0I7WUFDcEIsaUJBQWlCO1NBQ2xCLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQztBQUNKLENBQUM7QUE1Q0Qsd0RBNENDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILFNBQWdCLGlDQUFpQyxDQUMvQyxZQUFxQixFQUNyQixPQUtDO0lBRUQsc0NBQXNDO0lBQ3RDLE1BQU0sUUFBUSxHQUFHLHdCQUF3QixDQUFDO0lBQzFDLE1BQU0sR0FBRyxHQUNQLDBGQUEwRixDQUFDO0lBRTdGLE9BQU8sc0JBQXNCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDdEUsQ0FBQztBQWZELDhFQWVDIn0=
@@ -0,0 +1,68 @@
1
+ import { CosmosClient } from '@azure/cosmos';
2
+ import { FieldSchema } from '@forestadmin/datasource-toolkit';
3
+ import ModelCosmos from '../model-builder/model';
4
+ export type OverrideTypeConverter = (field: {
5
+ fieldName: string;
6
+ attribute: {
7
+ type: string;
8
+ nullable?: boolean;
9
+ indexed?: boolean;
10
+ };
11
+ generatedFieldSchema: FieldSchema;
12
+ }) => void | FieldSchema;
13
+ export type CosmosCollectionBase = {
14
+ /**
15
+ * Give the name of the collection (Forest Admin collection name)
16
+ */
17
+ name: string;
18
+ /**
19
+ * Allow to override the type converter
20
+ */
21
+ overrideTypeConverter?: OverrideTypeConverter;
22
+ /**
23
+ * Enabling `enableCount` allows the pagination widget to display the total number of pages
24
+ * in this collection while browsing records.
25
+ *
26
+ * _It is enabled by default but can be disabled for performance reasons._
27
+ */
28
+ enableCount?: boolean;
29
+ };
30
+ export type CosmosCollectionFromContainerOptions = {
31
+ /**
32
+ * The Cosmos DB database name
33
+ */
34
+ databaseName: string;
35
+ /**
36
+ * The Cosmos DB container name
37
+ */
38
+ containerName: string;
39
+ /**
40
+ * The partition key path for this container (e.g., "/userId")
41
+ */
42
+ partitionKeyPath?: string;
43
+ /**
44
+ * Number of sample documents to analyze for schema inference (default: 100)
45
+ */
46
+ sampleSize?: number;
47
+ } & CosmosCollectionBase;
48
+ export type ConfigurationOptions = (configurator: CosmosDatasourceOptionsBuilder) => CosmosDatasourceOptionsBuilder;
49
+ export interface CosmosDatasourceOptionsBuilder {
50
+ /**
51
+ * Add a collection from a Cosmos DB container
52
+ */
53
+ addCollectionFromContainer(options: CosmosCollectionFromContainerOptions): this;
54
+ }
55
+ /**
56
+ * Builder pattern to ease adding collections from Cosmos DB
57
+ */
58
+ export declare class CosmosDatasourceBuilder implements CosmosDatasourceOptionsBuilder {
59
+ private readonly cosmosClient;
60
+ protected collectionsPromises: Array<Promise<ModelCosmos>>;
61
+ constructor(cosmosClient: CosmosClient);
62
+ addCollectionFromContainer({ name, databaseName, containerName, partitionKeyPath, overrideTypeConverter, enableCount, sampleSize, }: CosmosCollectionFromContainerOptions): this;
63
+ /**
64
+ * Internal usages only - the client only sees CosmosDatasourceOptionsBuilder interface
65
+ */
66
+ createCollectionsFromConfiguration(): Promise<ModelCosmos[]>;
67
+ }
68
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/introspection/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAG9D,OAAO,WAAW,MAAM,wBAAwB,CAAC;AAEjD,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACnE,oBAAoB,EAAE,WAAW,CAAC;CACnC,KAAK,IAAI,GAAG,WAAW,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAE9C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,oBAAoB,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG,CACjC,YAAY,EAAE,8BAA8B,KACzC,8BAA8B,CAAC;AAEpC,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,0BAA0B,CAAC,OAAO,EAAE,oCAAoC,GAAG,IAAI,CAAC;CACjF;AAED;;GAEG;AACH,qBAAa,uBAAwB,YAAW,8BAA8B;IAC5E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAE5C,SAAS,CAAC,mBAAmB,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAM;gBAEpD,YAAY,EAAE,YAAY;IAI/B,0BAA0B,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,WAAW,EACX,UAAgB,GACjB,EAAE,oCAAoC,GAAG,IAAI;IAqB9C;;OAEG;IACU,kCAAkC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;CAG1E"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CosmosDatasourceBuilder = void 0;
7
+ const container_introspector_1 = __importDefault(require("./container-introspector"));
8
+ /**
9
+ * Builder pattern to ease adding collections from Cosmos DB
10
+ */
11
+ class CosmosDatasourceBuilder {
12
+ constructor(cosmosClient) {
13
+ this.collectionsPromises = [];
14
+ this.cosmosClient = cosmosClient;
15
+ }
16
+ addCollectionFromContainer({ name, databaseName, containerName, partitionKeyPath, overrideTypeConverter, enableCount, sampleSize = 100, }) {
17
+ this.collectionsPromises.push((async () => {
18
+ const model = await (0, container_introspector_1.default)(this.cosmosClient, name, databaseName, containerName, partitionKeyPath, sampleSize, overrideTypeConverter, enableCount);
19
+ return model;
20
+ })());
21
+ return this;
22
+ }
23
+ /**
24
+ * Internal usages only - the client only sees CosmosDatasourceOptionsBuilder interface
25
+ */
26
+ async createCollectionsFromConfiguration() {
27
+ return Promise.all(this.collectionsPromises);
28
+ }
29
+ }
30
+ exports.CosmosDatasourceBuilder = CosmosDatasourceBuilder;
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbnRyb3NwZWN0aW9uL2J1aWxkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBR0Esc0ZBQTJEO0FBOEQzRDs7R0FFRztBQUNILE1BQWEsdUJBQXVCO0lBS2xDLFlBQVksWUFBMEI7UUFGNUIsd0JBQW1CLEdBQWdDLEVBQUUsQ0FBQztRQUc5RCxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztJQUNuQyxDQUFDO0lBRU0sMEJBQTBCLENBQUMsRUFDaEMsSUFBSSxFQUNKLFlBQVksRUFDWixhQUFhLEVBQ2IsZ0JBQWdCLEVBQ2hCLHFCQUFxQixFQUNyQixXQUFXLEVBQ1gsVUFBVSxHQUFHLEdBQUcsR0FDcUI7UUFDckMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FDM0IsQ0FBQyxLQUFLLElBQUksRUFBRTtZQUNWLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBQSxnQ0FBbUIsRUFDckMsSUFBSSxDQUFDLFlBQVksRUFDakIsSUFBSSxFQUNKLFlBQVksRUFDWixhQUFhLEVBQ2IsZ0JBQWdCLEVBQ2hCLFVBQVUsRUFDVixxQkFBcUIsRUFDckIsV0FBVyxDQUNaLENBQUM7WUFFRixPQUFPLEtBQUssQ0FBQztRQUNmLENBQUMsQ0FBQyxFQUFFLENBQ0wsQ0FBQztRQUVGLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGtDQUFrQztRQUM3QyxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7SUFDL0MsQ0FBQztDQUNGO0FBNUNELDBEQTRDQyJ9
@@ -0,0 +1,27 @@
1
+ import { CosmosClient } from '@azure/cosmos';
2
+ import { OverrideTypeConverter } from './builder';
3
+ import ModelCosmos from '../model-builder/model';
4
+ export interface IntrospectionOptions {
5
+ /**
6
+ * Whether to flatten nested objects into dot-notation fields
7
+ * Example: { address: { city: 'NYC' } } becomes { 'address.city': 'NYC' }
8
+ * Default: true (recommended for Forest Admin compatibility)
9
+ */
10
+ flattenNestedObjects?: boolean;
11
+ /**
12
+ * Maximum depth for nested object introspection
13
+ * Default: 5
14
+ */
15
+ maxDepth?: number;
16
+ /**
17
+ * Whether to include array items introspection
18
+ * Default: false (arrays are treated as Json type)
19
+ */
20
+ introspectArrayItems?: boolean;
21
+ }
22
+ /**
23
+ * Introspect a Cosmos DB container to infer the schema from sample documents
24
+ * with support for complex nested objects
25
+ */
26
+ export default function introspectContainerV2(cosmosClient: CosmosClient, collectionName: string, databaseName: string, containerName: string, partitionKeyPath?: string, sampleSize?: number, overrideTypeConverter?: OverrideTypeConverter, enableCount?: boolean, options?: IntrospectionOptions): Promise<ModelCosmos>;
27
+ //# sourceMappingURL=container-introspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-introspector.d.ts","sourceRoot":"","sources":["../../src/introspection/container-introspector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,WAA6B,MAAM,wBAAwB,CAAC;AAGnE,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,wBAA8B,qBAAqB,CACjD,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,gBAAgB,CAAC,EAAE,MAAM,EACzB,UAAU,SAAM,EAChB,qBAAqB,CAAC,EAAE,qBAAqB,EAC7C,WAAW,CAAC,EAAE,OAAO,EACrB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,WAAW,CAAC,CAuCtB"}
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const model_1 = __importDefault(require("../model-builder/model"));
7
+ const type_converter_1 = __importDefault(require("../utils/type-converter"));
8
+ /**
9
+ * Introspect a Cosmos DB container to infer the schema from sample documents
10
+ * with support for complex nested objects
11
+ */
12
+ async function introspectContainerV2(cosmosClient, collectionName, databaseName, containerName, partitionKeyPath, sampleSize = 100, overrideTypeConverter, enableCount, options = {}) {
13
+ const { flattenNestedObjects = true, maxDepth = 5, introspectArrayItems = false } = options;
14
+ const database = cosmosClient.database(databaseName);
15
+ const container = database.container(containerName);
16
+ // Get container metadata to determine partition key if not provided
17
+ let actualPartitionKeyPath = partitionKeyPath;
18
+ if (!actualPartitionKeyPath) {
19
+ const { resource: containerDef } = await container.read();
20
+ actualPartitionKeyPath = containerDef.partitionKey?.paths?.[0] || '/id';
21
+ }
22
+ // Sample documents to infer schema
23
+ const querySpec = {
24
+ query: `SELECT TOP ${sampleSize} * FROM c`,
25
+ };
26
+ const { resources: sampleDocuments } = await container.items.query(querySpec).fetchAll();
27
+ // Infer schema from sample documents with nested object support
28
+ const schema = inferSchemaFromDocuments(sampleDocuments, flattenNestedObjects, maxDepth, introspectArrayItems);
29
+ return new model_1.default(cosmosClient, collectionName, databaseName, containerName, actualPartitionKeyPath, schema, overrideTypeConverter, enableCount);
30
+ }
31
+ exports.default = introspectContainerV2;
32
+ /**
33
+ * Infer schema from a collection of sample documents with nested support
34
+ */
35
+ function inferSchemaFromDocuments(
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ documents, flattenNestedObjects, maxDepth, introspectArrayItems) {
38
+ if (documents.length === 0) {
39
+ return {};
40
+ }
41
+ const schema = {};
42
+ const fieldTypes = {};
43
+ const fieldPresence = {}; // Track how many docs have each field
44
+ // Analyze each document to collect field types
45
+ for (const doc of documents) {
46
+ const fieldsInDoc = new Set();
47
+ analyzeDocument(doc, fieldTypes, '', 0, maxDepth, flattenNestedObjects, introspectArrayItems, fieldsInDoc);
48
+ // Track field presence
49
+ for (const field of fieldsInDoc) {
50
+ fieldPresence[field] = (fieldPresence[field] || 0) + 1;
51
+ }
52
+ }
53
+ // Convert collected field types to schema
54
+ for (const [fieldName, types] of Object.entries(fieldTypes)) {
55
+ // Skip Cosmos DB system fields (start with _)
56
+ if (!fieldName.startsWith('_') || fieldName === '_id') {
57
+ // Get the most specific common type
58
+ const commonType = type_converter_1.default.getMostSpecificType(types);
59
+ // Field is nullable if it contains null OR is not present in all documents
60
+ const nullable = types.includes('null') || fieldPresence[fieldName] < documents.length;
61
+ schema[fieldName] = {
62
+ type: commonType,
63
+ nullable,
64
+ indexed: true, // Assume all fields can be indexed in Cosmos DB
65
+ };
66
+ }
67
+ }
68
+ return schema;
69
+ }
70
+ /**
71
+ * Recursively analyze a document to collect field types with nested object support
72
+ */
73
+ function analyzeDocument(
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ obj, fieldTypes, prefix, depth, maxDepth, flattenNestedObjects, introspectArrayItems, fieldsInDoc) {
76
+ // Helper to record a field type
77
+ const recordField = (fieldName, type) => {
78
+ if (!fieldTypes[fieldName]) {
79
+ fieldTypes[fieldName] = [];
80
+ }
81
+ fieldTypes[fieldName].push(type);
82
+ if (fieldsInDoc && fieldName) {
83
+ fieldsInDoc.add(fieldName);
84
+ }
85
+ };
86
+ if (obj === null || obj === undefined) {
87
+ if (prefix) {
88
+ recordField(prefix, 'null');
89
+ }
90
+ return;
91
+ }
92
+ // Stop recursion if max depth reached
93
+ if (depth >= maxDepth) {
94
+ if (prefix) {
95
+ recordField(prefix, 'object');
96
+ }
97
+ return;
98
+ }
99
+ // Handle arrays
100
+ if (Array.isArray(obj)) {
101
+ if (prefix) {
102
+ recordField(prefix, 'array');
103
+ }
104
+ // Optionally introspect array items
105
+ if (introspectArrayItems && obj.length > 0) {
106
+ // Analyze first few items to infer array item type
107
+ const itemsToAnalyze = obj.slice(0, Math.min(5, obj.length));
108
+ for (const item of itemsToAnalyze) {
109
+ if (typeof item === 'object' && item !== null) {
110
+ analyzeDocument(item, fieldTypes, `${prefix}[]`, depth + 1, maxDepth, flattenNestedObjects, introspectArrayItems, fieldsInDoc);
111
+ }
112
+ }
113
+ }
114
+ return;
115
+ }
116
+ // Handle objects
117
+ if (typeof obj === 'object') {
118
+ // Check for special types first
119
+ if (obj instanceof Date) {
120
+ if (prefix) {
121
+ recordField(prefix, 'date');
122
+ }
123
+ return;
124
+ }
125
+ if (isGeoPoint(obj)) {
126
+ if (prefix) {
127
+ recordField(prefix, 'point');
128
+ }
129
+ return;
130
+ }
131
+ // Regular object - recursively analyze properties
132
+ for (const [key, value] of Object.entries(obj)) {
133
+ const fieldName = prefix ? `${prefix}.${key}` : key;
134
+ if (value &&
135
+ typeof value === 'object' &&
136
+ !Array.isArray(value) &&
137
+ flattenNestedObjects &&
138
+ !(value instanceof Date) &&
139
+ !isGeoPoint(value)) {
140
+ // Recursively analyze nested objects if flattening is enabled
141
+ analyzeDocument(value, fieldTypes, fieldName, depth + 1, maxDepth, flattenNestedObjects, introspectArrayItems, fieldsInDoc);
142
+ }
143
+ else {
144
+ // For non-nested or non-flatten mode
145
+ const inferredType = type_converter_1.default.inferTypeFromValue(value);
146
+ recordField(fieldName, inferredType);
147
+ }
148
+ }
149
+ return;
150
+ }
151
+ // Primitive values
152
+ if (prefix) {
153
+ const inferredType = type_converter_1.default.inferTypeFromValue(obj);
154
+ recordField(prefix, inferredType);
155
+ }
156
+ }
157
+ /**
158
+ * Check if an object is a GeoJSON Point
159
+ */
160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ function isGeoPoint(value) {
162
+ return (value &&
163
+ typeof value === 'object' &&
164
+ value.type === 'Point' &&
165
+ Array.isArray(value.coordinates) &&
166
+ value.coordinates.length === 2);
167
+ }
168
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGFpbmVyLWludHJvc3BlY3Rvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbnRyb3NwZWN0aW9uL2NvbnRhaW5lci1pbnRyb3NwZWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFJQSxtRUFBbUU7QUFDbkUsNkVBQXdFO0FBdUJ4RTs7O0dBR0c7QUFDWSxLQUFLLFVBQVUscUJBQXFCLENBQ2pELFlBQTBCLEVBQzFCLGNBQXNCLEVBQ3RCLFlBQW9CLEVBQ3BCLGFBQXFCLEVBQ3JCLGdCQUF5QixFQUN6QixVQUFVLEdBQUcsR0FBRyxFQUNoQixxQkFBNkMsRUFDN0MsV0FBcUIsRUFDckIsVUFBZ0MsRUFBRTtJQUVsQyxNQUFNLEVBQUUsb0JBQW9CLEdBQUcsSUFBSSxFQUFFLFFBQVEsR0FBRyxDQUFDLEVBQUUsb0JBQW9CLEdBQUcsS0FBSyxFQUFFLEdBQUcsT0FBTyxDQUFDO0lBRTVGLE1BQU0sUUFBUSxHQUFHLFlBQVksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDckQsTUFBTSxTQUFTLEdBQUcsUUFBUSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUVwRCxvRUFBb0U7SUFDcEUsSUFBSSxzQkFBc0IsR0FBRyxnQkFBZ0IsQ0FBQztJQUU5QyxJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztRQUM1QixNQUFNLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxHQUFHLE1BQU0sU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzFELHNCQUFzQixHQUFHLFlBQVksQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDO0lBQzFFLENBQUM7SUFFRCxtQ0FBbUM7SUFDbkMsTUFBTSxTQUFTLEdBQUc7UUFDaEIsS0FBSyxFQUFFLGNBQWMsVUFBVSxXQUFXO0tBQzNDLENBQUM7SUFFRixNQUFNLEVBQUUsU0FBUyxFQUFFLGVBQWUsRUFBRSxH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
@@ -0,0 +1,18 @@
1
+ import { CosmosClient } from '@azure/cosmos';
2
+ import { Logger } from '@forestadmin/datasource-toolkit';
3
+ import ModelCosmos from '../model-builder/model';
4
+ export default class Introspector {
5
+ /**
6
+ * Introspect all containers in a Cosmos DB database
7
+ */
8
+ static introspect(cosmosClient: CosmosClient, databaseName: string, logger?: Logger): Promise<ModelCosmos[]>;
9
+ /**
10
+ * Introspect all containers in the specified database
11
+ */
12
+ private static introspectAll;
13
+ /**
14
+ * Introspect a specific container
15
+ */
16
+ static introspectContainer(cosmosClient: CosmosClient, databaseName: string, containerName: string, sampleSize?: number, logger?: Logger): Promise<ModelCosmos>;
17
+ }
18
+ //# sourceMappingURL=introspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspector.d.ts","sourceRoot":"","sources":["../../src/introspection/introspector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAGzD,OAAO,WAAW,MAAM,wBAAwB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B;;OAEG;WACU,UAAU,CACrB,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,EAAE,CAAC;IAMzB;;OAEG;mBACkB,aAAa;IAuDlC;;OAEG;WACU,mBAAmB,CAC9B,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC;CAgBxB"}