diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 39396fc5..09842933 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,5 +1,6 @@
# FROM node:18-alpine
-FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-20-bullseye
+FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:dev-22-bookworm
+
# [Optional] Uncomment this section to install additional OS packages.
@@ -11,19 +12,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-20-bullseye
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
#alpine
-# RUN apk update
-# RUN apk add git
-# RUN apk add libnsl
-# RUN apk add gcompat
-# RUN ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
-# RUN apk --no-cache add curl
-# RUN apk --no-cache add unixodbc
-# RUN apk add sudo
-# odbc alpine
-# https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16
-# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
-# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/mssql-tools18_18.1.1.1-1_amd64.apk
-# RUN sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
+
# RUN sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
#debian
@@ -35,6 +24,21 @@ RUN sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18
#RUN ~/.bashrc
RUN apt-get install -y unixodbc-dev
+#alpine
+# RUN apk update
+# RUN apk --no-cache add curl
+# RUN apk --no-cache add unixodbc
+# RUN apk add sudo
+# # - RUN apk add unzip
+# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
+# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/mssql-tools18_18.1.1.1-1_amd64.apk
+# RUN sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
+# RUN sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
+# RUN sudo apk add libnsl
+# RUN sudo apk add gcompat
+# RUN ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
+
+
# RUN mkdir -p /usr/config
# WORKDIR /usr/config
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ae5f0e31..04ba3da8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- container: ["node:18-alpine", "node:20-alpine", "node:22-alpine"]
+ container: ["node:18-bookworm", "node:20-bookworm", "node:22-bookworm"]
container:
image: ${{ matrix.container }}
services:
@@ -69,28 +69,17 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- - run: apk update
- - run: apk --no-cache add curl
- - run: apk --no-cache add unixodbc
- - run: apk add sudo
- # - run: apk add unzip
- - run: curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
- - run: curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/mssql-tools18_18.1.1.1-1_amd64.apk
- - run: sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
- - run: sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
- - run: sudo apk add libnsl
- - run: sudo apk add gcompat
- - run: ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
- # - run: npm install
- # - run: wget https://download.oracle.com/otn_software/linux/instantclient/193000/instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && unzip instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && cp -r instantclient_19_3/* /lib && rm -rf instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && apk --no-cache add libaio && cd /lib && ln -s libnsl.so.2 /usr/lib/libnsl.so.1 && ln -s libc.so /usr/lib/libresolv.so.2
- # - run: ldd /__w/rdb/rdb/tests/libsybdrvodb.so
+ - run: curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
+ - run: curl https://packages.microsoft.com/config/debian/11/prod.list > /etc/apt/sources.list.d/mssql-release.list
+ - run: apt-get update
+ - run: ACCEPT_EULA=Y apt-get install -y msodbcsql18
+ - run: apt-get install -y unixodbc-dev
- run: npm install
- run: npm run lint
- run: npm run tscheck
- run: npm run test
- - run: npm run coverage
- # Only run the coverage once
- - if: ${{ matrix.container == 'node:16-alpine' && github.ref == 'refs/heads/master'}}
+ - run: npm run coverage # Only run the coverage once
+ - if: ${{ matrix.container == 'node:18-bookworm' && github.ref == 'refs/heads/master'}}
name: Get Coverage for badge
run: |
# var SUMMARY = [
diff --git a/.gitignore b/.gitignore
index 30834652..e8606379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,5 @@ package-lock.json
.vscode
demo.db
demo*.db*
-coverage
\ No newline at end of file
+coverage
+.env
\ No newline at end of file
diff --git a/README.md b/README.md
index 443f25be..e476107a 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,8 @@ The ultimate Object Relational Mapper for Node.js and Typescript, offering seaml
✅ MySQL
✅ Oracle
✅ SAP ASE
-✅ SQLite
+✅ SQLite
+✅ Cloudflare D1
This is the _Modern Typescript Documentation_. Are you looking for the [_Classic Documentation_](https://github.com/alfateam/orange-orm/blob/master/docs/docs.md) ?
@@ -319,6 +320,7 @@ import map from './map';
const db = map.http('http://localhost:3000/orange');
```
+
__MySQL__
```bash
$ npm install mysql2
@@ -364,6 +366,38 @@ With schema
import map from './map';
const db = map.postgres('postgres://postgres:postgres@postgres/postgres?search_path=custom');
```
+__Cloudflare D1__
+📄 wrangler.toml
+```toml
+name = "d1-tutorial"
+main = "src/index.ts"
+compatibility_date = "2025-02-04"
+
+# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
+# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases
+[[d1_databases]]
+binding = "DB"
+database_name = "
(NOTE: Transactions are not supported for Cloudflare D1)
+ ```javascript import map from './map'; @@ -1597,6 +1633,7 @@ async function execute() { } ``` +You enable logging by listening to the query event on the `orange` object. During this event, both the SQL statement and any associated parameters are logged.
+You enable logging by listening to the query event on the `orange` object. During this event, both the SQL statement and any associated parameters are logged. The logged output reveals the sequence of SQL commands executed, offering developers a transparent view into database operations, which aids in debugging and ensures data integrity.
```javascript import orange from 'orange-orm'; @@ -1996,8 +2033,10 @@ async function updateRow() { output: ```bash +BEGIN select _order.id as s_order0,_order.orderDate as s_order1,_order.customerId as s_order2 from _order _order where _order.id=2 order by _order.id limit 1 select orderLine.id as sorderLine0,orderLine.orderId as sorderLine1,orderLine.product as sorderLine2,orderLine.amount as sorderLine3 from orderLine orderLine where orderLine.orderId in (2) order by orderLine.id +COMMIT BEGIN select _order.id as s_order0,_order.orderDate as s_order1,_order.customerId as s_order2 from _order _order where _order.id=2 order by _order.id limit 1 INSERT INTO orderLine (orderId,product,amount) VALUES (2,?,300) diff --git a/docs/changelog.md b/docs/changelog.md index 34215c7e..2367bfd6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,6 @@ ## Changelog +__4.5.0__ +Support for Cloudflare D1. __4.4.2__ Support for schema in connection string. Postgrs only. [#116](https://github.com/alfateam/orange-orm/issues/118) __4.4.1__ diff --git a/package.json b/package.json index 1ebd2736..0500fa72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orange-orm", - "version": "4.4.2", + "version": "4.5.0", "main": "./src/index.js", "browser": "./src/client/index.mjs", "bin": { @@ -48,23 +48,21 @@ "lint": "eslint ./", "fix": "eslint ./ --fix", "owasp": "owasp-dependency-check --project \"MY_PROJECT\" --scan \"package-lock.json\" --exclude \"dependency-check-bin\" --out \"owasp\" --format HTML", - "beta": "publish --tag beta" + "beta": "npm publish --tag beta" }, "dependencies": { "@lroal/on-change": "^4.0.2", "@tediousjs/connection-string": "^0.4.1", "@types/express": "^4.17.13", + "@cloudflare/workers-types": "^4.20241106.0", "@types/oracledb": "^6.0.4", "@types/tedious": "^4.0.14", "ajv": "^6.10.2", "axios": "^1.6.2", - "deferred": "^0.7.5", "fast-json-patch": "^3.1.1", "findup-sync": "^5.0.0", "glob": "^10.3.4", "module-definition": "^4.0.0", - "node-cls": "^1.0.5", - "promise": "^8.0.3", "rfdc": "^1.2.0", "uuid": "^8.3.2" }, @@ -99,10 +97,15 @@ }, "tedious": { "optional": true + }, + "oracledb": { + "optional": true } }, - "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", + "devDependencies": { + "@miniflare/d1": "^2.14.4", + "@rollup/plugin-commonjs": "^28.0.2", + "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^13.0.0", "@typescript-eslint/eslint-plugin": "^6.x", "@typescript-eslint/parser": "^6.x", @@ -111,6 +114,7 @@ "eslint": "^8.57.0", "eslint-plugin-jest": "^27.1.7", "express": "^4.18.2", + "miniflare": "^3.20250129.0", "msnodesqlv8": "^4.1.0", "mysql2": "^3.9.4", "oracledb": "^6.3.0", @@ -121,7 +125,7 @@ "sqlite3": "^5.0.2", "tedious": "^18.2.0", "typescript": "^5.4.5", - "vitest": "^0.34.1" + "vitest": "^0.34.6" }, "engines": { "node": ">= 8.0.0" diff --git a/src/applyPatch.js b/src/applyPatch.js index aba898be..82b0f301 100644 --- a/src/applyPatch.js +++ b/src/applyPatch.js @@ -1,6 +1,4 @@ const fastjson = require('fast-json-patch'); -let { inspect } = require('util'); -let assert = require('assert'); let fromCompareObject = require('./fromCompareObject'); let toCompareObject = require('./toCompareObject'); @@ -43,12 +41,12 @@ function applyPatch({ options = {} }, dto, changes, column) { assertDatesEqual(oldValue, expectedOldValue); } else - assert.deepEqual(oldValue, expectedOldValue); + assertDeepEqual(oldValue, expectedOldValue); } catch (e) { if (concurrency === 'skipOnConflict') return false; - throw new Error(`The field ${change.path.replace('/', '')} was changed by another user. Expected ${inspect(fromCompareObject(expectedOldValue), false, 10)}, but was ${inspect(fromCompareObject(oldValue), false, 10)}.`); + throw new Error(`The field ${change.path.replace('/', '')} was changed by another user. Expected ${inspect(fromCompareObject(expectedOldValue))}, but was ${inspect(fromCompareObject(oldValue))}.`); } } return true; @@ -99,7 +97,16 @@ function assertDatesEqual(date1, date2) { date1 = `${parts1[0]}T${time1parts[0]}`; date2 = `${parts2[0]}T${time2parts[0]}`; } - assert.deepEqual(date1, date2); + assertDeepEqual(date1, date2); +} + +function assertDeepEqual(a, b) { + if (JSON.stringify(a) !== JSON.stringify(b)) + throw new Error('A, b are not equal'); +} + +function inspect(obj) { + return JSON.stringify(obj, null, 2); } module.exports = applyPatch; \ No newline at end of file diff --git a/src/client/clientMap.js b/src/client/clientMap.js index 880e0d01..2a957225 100644 --- a/src/client/clientMap.js +++ b/src/client/clientMap.js @@ -39,6 +39,7 @@ function map(index, _fn) { dbMap.sap = throwDb; dbMap.oracle = throwDb; dbMap.sqlite = throwDb; + dbMap.d1 = throwDb; function throwDb() { throw new Error('Cannot create pool for database outside node'); @@ -65,6 +66,7 @@ function map(index, _fn) { onFinal.sap = () => index({ db: throwDb, providers: dbMap }); onFinal.oracle = () => index({ db: throwDb, providers: dbMap }); onFinal.sqlite = () => index({ db: throwDb, providers: dbMap }); + onFinal.d1 = () => index({ db: throwDb, providers: dbMap }); return new Proxy(onFinal, handler); } diff --git a/src/client/createProviders.js b/src/client/createProviders.js index 780d29c0..83419278 100644 --- a/src/client/createProviders.js +++ b/src/client/createProviders.js @@ -48,6 +48,11 @@ function createProviders(index) { return createPool.bind(null, 'sqlite'); } }); + Object.defineProperty(dbMap, 'd1', { + get: function() { + return createPool.bind(null, 'd1'); + } + }); Object.defineProperty(dbMap, 'http', { get: function() { return createPool.bind(null, 'http'); @@ -97,12 +102,19 @@ function negotiateCachedPool(fn, providers) { get sqlite() { return createPool.bind(null, 'sqlite'); }, + get d1() { + return createPool.bind(null, 'd1'); + }, get http() { return createPool.bind(null, 'http'); } }; function createPool(providerName, ...args) { + //todo + if (providerName === 'd1') { + return providers[providerName].apply(null, args); + } const key = JSON.stringify(args); if (!cache[providerName]) cache[providerName] = {}; diff --git a/src/client/index.js b/src/client/index.js index 48330ed9..016226a9 100644 --- a/src/client/index.js +++ b/src/client/index.js @@ -57,6 +57,7 @@ function rdbClient(options = {}) { client.mssqlNative = onProvider.bind(null, 'mssqlNative'); client.pg = onProvider.bind(null, 'pg'); client.postgres = onProvider.bind(null, 'postgres'); + client.d1 = onProvider.bind(null, 'd1'); client.sqlite = onProvider.bind(null, 'sqlite'); client.sap = onProvider.bind(null, 'sap'); client.oracle = onProvider.bind(null, 'oracle'); @@ -128,7 +129,8 @@ function rdbClient(options = {}) { } async function query() { - return netAdapter(baseUrl, undefined, { tableOptions: { db: baseUrl, transaction } }).query.apply(null, arguments); + const adapter = netAdapter(baseUrl, undefined, { tableOptions: { db: baseUrl, transaction } }); + return adapter.query.apply(null, arguments); } function express(arg) { @@ -168,13 +170,14 @@ function rdbClient(options = {}) { if (!db.createTransaction) throw new Error('Transaction not supported through http'); const transaction = db.createTransaction(_options); + try { const nextClient = client({ transaction }); await fn(nextClient); - await transaction(db.commit); + await transaction(transaction.commit); } catch (e) { - await transaction(db.rollback.bind(null, e)); + await transaction(transaction.rollback.bind(null, e)); } } diff --git a/src/client/index.mjs b/src/client/index.mjs index 59e81add..6bb149a6 100644 --- a/src/client/index.mjs +++ b/src/client/index.mjs @@ -1,6927 +1,13992 @@ void !function() { typeof self === 'undefined' && typeof global === 'object' ? global.self = global : null; -}();var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - -function getAugmentedNamespace(n) { - if (n.__esModule) return n; - var a = Object.defineProperty({}, '__esModule', {value: true}); - Object.keys(n).forEach(function (k) { - var d = Object.getOwnPropertyDescriptor(n, k); - Object.defineProperty(a, k, d.get ? d : { - enumerable: true, - get: function () { - return n[k]; - } - }); - }); - return a; -} - -/*! - * https://github.com/Starcounter-Jack/JSON-Patch - * (c) 2017-2022 Joachim Wester - * MIT licensed - */ -var __extends = (undefined && undefined.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var _hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwnProperty$1(obj, key) { - return _hasOwnProperty.call(obj, key); -} -function _objectKeys(obj) { - if (Array.isArray(obj)) { - var keys_1 = new Array(obj.length); - for (var k = 0; k < keys_1.length; k++) { - keys_1[k] = "" + k; - } - return keys_1; - } - if (Object.keys) { - return Object.keys(obj); - } - var keys = []; - for (var i in obj) { - if (hasOwnProperty$1(obj, i)) { - keys.push(i); - } - } - return keys; -} -/** -* Deeply clone the object. -* https://jsperf.com/deep-copy-vs-json-stringify-json-parse/25 (recursiveDeepCopy) -* @param {any} obj value to clone -* @return {any} cloned obj -*/ -function _deepClone(obj) { - switch (typeof obj) { - case "object": - return JSON.parse(JSON.stringify(obj)); //Faster than ES5 clone - http://jsperf.com/deep-cloning-of-objects/5 - case "undefined": - return null; //this is how JSON.stringify behaves for array items - default: - return obj; //no need to clone primitives - } -} -//3x faster than cached /^\d+$/.test(str) -function isInteger(str) { - var i = 0; - var len = str.length; - var charCode; - while (i < len) { - charCode = str.charCodeAt(i); - if (charCode >= 48 && charCode <= 57) { - i++; - continue; - } - return false; - } - return true; -} -/** -* Escapes a json pointer path -* @param path The raw pointer -* @return the Escaped path -*/ -function escapePathComponent(path) { - if (path.indexOf('/') === -1 && path.indexOf('~') === -1) - return path; - return path.replace(/~/g, '~0').replace(/\//g, '~1'); -} -/** - * Unescapes a json pointer path - * @param path The escaped pointer - * @return The unescaped path - */ -function unescapePathComponent(path) { - return path.replace(/~1/g, '/').replace(/~0/g, '~'); -} -/** -* Recursively checks whether an object has any undefined values inside. -*/ -function hasUndefined(obj) { - if (obj === undefined) { - return true; - } - if (obj) { - if (Array.isArray(obj)) { - for (var i_1 = 0, len = obj.length; i_1 < len; i_1++) { - if (hasUndefined(obj[i_1])) { - return true; - } - } - } - else if (typeof obj === "object") { - var objKeys = _objectKeys(obj); - var objKeysLength = objKeys.length; - for (var i = 0; i < objKeysLength; i++) { - if (hasUndefined(obj[objKeys[i]])) { - return true; - } - } - } - } - return false; -} -function patchErrorMessageFormatter(message, args) { - var messageParts = [message]; - for (var key in args) { - var value = typeof args[key] === 'object' ? JSON.stringify(args[key], null, 2) : args[key]; // pretty print - if (typeof value !== 'undefined') { - messageParts.push(key + ": " + value); - } - } - return messageParts.join('\n'); -} -var PatchError = /** @class */ (function (_super) { - __extends(PatchError, _super); - function PatchError(message, name, index, operation, tree) { - var _newTarget = this.constructor; - var _this = _super.call(this, patchErrorMessageFormatter(message, { name: name, index: index, operation: operation, tree: tree })) || this; - _this.name = name; - _this.index = index; - _this.operation = operation; - _this.tree = tree; - Object.setPrototypeOf(_this, _newTarget.prototype); // restore prototype chain, see https://stackoverflow.com/a/48342359 - _this.message = patchErrorMessageFormatter(message, { name: name, index: index, operation: operation, tree: tree }); - return _this; - } - return PatchError; -}(Error)); - -var JsonPatchError = PatchError; -var deepClone = _deepClone; -/* We use a Javascript hash to store each - function. Each hash entry (property) uses - the operation identifiers specified in rfc6902. - In this way, we can map each patch operation - to its dedicated function in efficient way. - */ -/* The operations applicable to an object */ -var objOps = { - add: function (obj, key, document) { - obj[key] = this.value; - return { newDocument: document }; - }, - remove: function (obj, key, document) { - var removed = obj[key]; - delete obj[key]; - return { newDocument: document, removed: removed }; - }, - replace: function (obj, key, document) { - var removed = obj[key]; - obj[key] = this.value; - return { newDocument: document, removed: removed }; - }, - move: function (obj, key, document) { - /* in case move target overwrites an existing value, - return the removed value, this can be taxing performance-wise, - and is potentially unneeded */ - var removed = getValueByPointer(document, this.path); - if (removed) { - removed = _deepClone(removed); - } - var originalValue = applyOperation(document, { op: "remove", path: this.from }).removed; - applyOperation(document, { op: "add", path: this.path, value: originalValue }); - return { newDocument: document, removed: removed }; - }, - copy: function (obj, key, document) { - var valueToCopy = getValueByPointer(document, this.from); - // enforce copy by value so further operations don't affect source (see issue #177) - applyOperation(document, { op: "add", path: this.path, value: _deepClone(valueToCopy) }); - return { newDocument: document }; - }, - test: function (obj, key, document) { - return { newDocument: document, test: _areEquals(obj[key], this.value) }; - }, - _get: function (obj, key, document) { - this.value = obj[key]; - return { newDocument: document }; - } -}; -/* The operations applicable to an array. Many are the same as for the object */ -var arrOps = { - add: function (arr, i, document) { - if (isInteger(i)) { - arr.splice(i, 0, this.value); - } - else { // array props - arr[i] = this.value; - } - // this may be needed when using '-' in an array - return { newDocument: document, index: i }; - }, - remove: function (arr, i, document) { - var removedList = arr.splice(i, 1); - return { newDocument: document, removed: removedList[0] }; - }, - replace: function (arr, i, document) { - var removed = arr[i]; - arr[i] = this.value; - return { newDocument: document, removed: removed }; - }, - move: objOps.move, - copy: objOps.copy, - test: objOps.test, - _get: objOps._get -}; -/** - * Retrieves a value from a JSON document by a JSON pointer. - * Returns the value. - * - * @param document The document to get the value from - * @param pointer an escaped JSON pointer - * @return The retrieved value - */ -function getValueByPointer(document, pointer) { - if (pointer == '') { - return document; - } - var getOriginalDestination = { op: "_get", path: pointer }; - applyOperation(document, getOriginalDestination); - return getOriginalDestination.value; -} -/** - * Apply a single JSON Patch Operation on a JSON document. - * Returns the {newDocument, result} of the operation. - * It modifies the `document` and `operation` objects - it gets the values by reference. - * If you would like to avoid touching your values, clone them: - * `jsonpatch.applyOperation(document, jsonpatch._deepClone(operation))`. - * - * @param document The document to patch - * @param operation The operation to apply - * @param validateOperation `false` is without validation, `true` to use default jsonpatch's validation, or you can pass a `validateOperation` callback to be used for validation. - * @param mutateDocument Whether to mutate the original document or clone it before applying - * @param banPrototypeModifications Whether to ban modifications to `__proto__`, defaults to `true`. - * @return `{newDocument, result}` after the operation - */ -function applyOperation(document, operation, validateOperation, mutateDocument, banPrototypeModifications, index) { - if (validateOperation === void 0) { validateOperation = false; } - if (mutateDocument === void 0) { mutateDocument = true; } - if (banPrototypeModifications === void 0) { banPrototypeModifications = true; } - if (index === void 0) { index = 0; } - if (validateOperation) { - if (typeof validateOperation == 'function') { - validateOperation(operation, 0, document, operation.path); - } - else { - validator$1(operation, 0); - } - } - /* ROOT OPERATIONS */ - if (operation.path === "") { - var returnValue = { newDocument: document }; - if (operation.op === 'add') { - returnValue.newDocument = operation.value; - return returnValue; - } - else if (operation.op === 'replace') { - returnValue.newDocument = operation.value; - returnValue.removed = document; //document we removed - return returnValue; - } - else if (operation.op === 'move' || operation.op === 'copy') { // it's a move or copy to root - returnValue.newDocument = getValueByPointer(document, operation.from); // get the value by json-pointer in `from` field - if (operation.op === 'move') { // report removed item - returnValue.removed = document; - } - return returnValue; - } - else if (operation.op === 'test') { - returnValue.test = _areEquals(document, operation.value); - if (returnValue.test === false) { - throw new JsonPatchError("Test operation failed", 'TEST_OPERATION_FAILED', index, operation, document); - } - returnValue.newDocument = document; - return returnValue; - } - else if (operation.op === 'remove') { // a remove on root - returnValue.removed = document; - returnValue.newDocument = null; - return returnValue; - } - else if (operation.op === '_get') { - operation.value = document; - return returnValue; - } - else { /* bad operation */ - if (validateOperation) { - throw new JsonPatchError('Operation `op` property is not one of operations defined in RFC-6902', 'OPERATION_OP_INVALID', index, operation, document); - } - else { - return returnValue; - } - } - } /* END ROOT OPERATIONS */ - else { - if (!mutateDocument) { - document = _deepClone(document); - } - var path = operation.path || ""; - var keys = path.split('/'); - var obj = document; - var t = 1; //skip empty element - http://jsperf.com/to-shift-or-not-to-shift - var len = keys.length; - var existingPathFragment = undefined; - var key = void 0; - var validateFunction = void 0; - if (typeof validateOperation == 'function') { - validateFunction = validateOperation; - } - else { - validateFunction = validator$1; - } - while (true) { - key = keys[t]; - if (key && key.indexOf('~') != -1) { - key = unescapePathComponent(key); - } - if (banPrototypeModifications && - (key == '__proto__' || - (key == 'prototype' && t > 0 && keys[t - 1] == 'constructor'))) { - throw new TypeError('JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README'); - } - if (validateOperation) { - if (existingPathFragment === undefined) { - if (obj[key] === undefined) { - existingPathFragment = keys.slice(0, t).join('/'); - } - else if (t == len - 1) { - existingPathFragment = operation.path; - } - if (existingPathFragment !== undefined) { - validateFunction(operation, 0, document, existingPathFragment); - } - } - } - t++; - if (Array.isArray(obj)) { - if (key === '-') { - key = obj.length; - } - else { - if (validateOperation && !isInteger(key)) { - throw new JsonPatchError("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index", "OPERATION_PATH_ILLEGAL_ARRAY_INDEX", index, operation, document); - } // only parse key when it's an integer for `arr.prop` to work - else if (isInteger(key)) { - key = ~~key; - } - } - if (t >= len) { - if (validateOperation && operation.op === "add" && key > obj.length) { - throw new JsonPatchError("The specified index MUST NOT be greater than the number of elements in the array", "OPERATION_VALUE_OUT_OF_BOUNDS", index, operation, document); - } - var returnValue = arrOps[operation.op].call(operation, obj, key, document); // Apply patch - if (returnValue.test === false) { - throw new JsonPatchError("Test operation failed", 'TEST_OPERATION_FAILED', index, operation, document); - } - return returnValue; - } - } - else { - if (t >= len) { - var returnValue = objOps[operation.op].call(operation, obj, key, document); // Apply patch - if (returnValue.test === false) { - throw new JsonPatchError("Test operation failed", 'TEST_OPERATION_FAILED', index, operation, document); - } - return returnValue; - } - } - obj = obj[key]; - // If we have more keys in the path, but the next value isn't a non-null object, - // throw an OPERATION_PATH_UNRESOLVABLE error instead of iterating again. - if (validateOperation && t < len && (!obj || typeof obj !== "object")) { - throw new JsonPatchError('Cannot perform operation at the desired path', 'OPERATION_PATH_UNRESOLVABLE', index, operation, document); - } - } - } -} -/** - * Apply a full JSON Patch array on a JSON document. - * Returns the {newDocument, result} of the patch. - * It modifies the `document` object and `patch` - it gets the values by reference. - * If you would like to avoid touching your values, clone them: - * `jsonpatch.applyPatch(document, jsonpatch._deepClone(patch))`. - * - * @param document The document to patch - * @param patch The patch to apply - * @param validateOperation `false` is without validation, `true` to use default jsonpatch's validation, or you can pass a `validateOperation` callback to be used for validation. - * @param mutateDocument Whether to mutate the original document or clone it before applying - * @param banPrototypeModifications Whether to ban modifications to `__proto__`, defaults to `true`. - * @return An array of `{newDocument, result}` after the patch - */ -function applyPatch(document, patch, validateOperation, mutateDocument, banPrototypeModifications) { - if (mutateDocument === void 0) { mutateDocument = true; } - if (banPrototypeModifications === void 0) { banPrototypeModifications = true; } - if (validateOperation) { - if (!Array.isArray(patch)) { - throw new JsonPatchError('Patch sequence must be an array', 'SEQUENCE_NOT_AN_ARRAY'); - } - } - if (!mutateDocument) { - document = _deepClone(document); - } - var results = new Array(patch.length); - for (var i = 0, length_1 = patch.length; i < length_1; i++) { - // we don't need to pass mutateDocument argument because if it was true, we already deep cloned the object, we'll just pass `true` - results[i] = applyOperation(document, patch[i], validateOperation, true, banPrototypeModifications, i); - document = results[i].newDocument; // in case root was replaced - } - results.newDocument = document; - return results; -} -/** - * Apply a single JSON Patch Operation on a JSON document. - * Returns the updated document. - * Suitable as a reducer. - * - * @param document The document to patch - * @param operation The operation to apply - * @return The updated document - */ -function applyReducer(document, operation, index) { - var operationResult = applyOperation(document, operation); - if (operationResult.test === false) { // failed test - throw new JsonPatchError("Test operation failed", 'TEST_OPERATION_FAILED', index, operation, document); - } - return operationResult.newDocument; -} -/** - * Validates a single operation. Called from `jsonpatch.validate`. Throws `JsonPatchError` in case of an error. - * @param {object} operation - operation object (patch) - * @param {number} index - index of operation in the sequence - * @param {object} [document] - object where the operation is supposed to be applied - * @param {string} [existingPathFragment] - comes along with `document` - */ -function validator$1(operation, index, document, existingPathFragment) { - if (typeof operation !== 'object' || operation === null || Array.isArray(operation)) { - throw new JsonPatchError('Operation is not an object', 'OPERATION_NOT_AN_OBJECT', index, operation, document); - } - else if (!objOps[operation.op]) { - throw new JsonPatchError('Operation `op` property is not one of operations defined in RFC-6902', 'OPERATION_OP_INVALID', index, operation, document); - } - else if (typeof operation.path !== 'string') { - throw new JsonPatchError('Operation `path` property is not a string', 'OPERATION_PATH_INVALID', index, operation, document); - } - else if (operation.path.indexOf('/') !== 0 && operation.path.length > 0) { - // paths that aren't empty string should start with "/" - throw new JsonPatchError('Operation `path` property must start with "/"', 'OPERATION_PATH_INVALID', index, operation, document); - } - else if ((operation.op === 'move' || operation.op === 'copy') && typeof operation.from !== 'string') { - throw new JsonPatchError('Operation `from` property is not present (applicable in `move` and `copy` operations)', 'OPERATION_FROM_REQUIRED', index, operation, document); - } - else if ((operation.op === 'add' || operation.op === 'replace' || operation.op === 'test') && operation.value === undefined) { - throw new JsonPatchError('Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)', 'OPERATION_VALUE_REQUIRED', index, operation, document); - } - else if ((operation.op === 'add' || operation.op === 'replace' || operation.op === 'test') && hasUndefined(operation.value)) { - throw new JsonPatchError('Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)', 'OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED', index, operation, document); - } - else if (document) { - if (operation.op == "add") { - var pathLen = operation.path.split("/").length; - var existingPathLen = existingPathFragment.split("/").length; - if (pathLen !== existingPathLen + 1 && pathLen !== existingPathLen) { - throw new JsonPatchError('Cannot perform an `add` operation at the desired path', 'OPERATION_PATH_CANNOT_ADD', index, operation, document); - } - } - else if (operation.op === 'replace' || operation.op === 'remove' || operation.op === '_get') { - if (operation.path !== existingPathFragment) { - throw new JsonPatchError('Cannot perform the operation at a path that does not exist', 'OPERATION_PATH_UNRESOLVABLE', index, operation, document); - } - } - else if (operation.op === 'move' || operation.op === 'copy') { - var existingValue = { op: "_get", path: operation.from, value: undefined }; - var error = validate$1([existingValue], document); - if (error && error.name === 'OPERATION_PATH_UNRESOLVABLE') { - throw new JsonPatchError('Cannot perform the operation from a path that does not exist', 'OPERATION_FROM_UNRESOLVABLE', index, operation, document); - } - } - } -} -/** - * Validates a sequence of operations. If `document` parameter is provided, the sequence is additionally validated against the object document. - * If error is encountered, returns a JsonPatchError object - * @param sequence - * @param document - * @returns {JsonPatchError|undefined} - */ -function validate$1(sequence, document, externalValidator) { - try { - if (!Array.isArray(sequence)) { - throw new JsonPatchError('Patch sequence must be an array', 'SEQUENCE_NOT_AN_ARRAY'); - } - if (document) { - //clone document and sequence so that we can safely try applying operations - applyPatch(_deepClone(document), _deepClone(sequence), externalValidator || true); - } - else { - externalValidator = externalValidator || validator$1; - for (var i = 0; i < sequence.length; i++) { - externalValidator(sequence[i], i, document, undefined); - } - } - } - catch (e) { - if (e instanceof JsonPatchError) { - return e; - } - else { - throw e; - } - } -} -// based on https://github.com/epoberezkin/fast-deep-equal -// MIT License -// Copyright (c) 2017 Evgeny Poberezkin -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -function _areEquals(a, b) { - if (a === b) - return true; - if (a && b && typeof a == 'object' && typeof b == 'object') { - var arrA = Array.isArray(a), arrB = Array.isArray(b), i, length, key; - if (arrA && arrB) { - length = a.length; - if (length != b.length) - return false; - for (i = length; i-- !== 0;) - if (!_areEquals(a[i], b[i])) - return false; - return true; - } - if (arrA != arrB) - return false; - var keys = Object.keys(a); - length = keys.length; - if (length !== Object.keys(b).length) - return false; - for (i = length; i-- !== 0;) - if (!b.hasOwnProperty(keys[i])) - return false; - for (i = length; i-- !== 0;) { - key = keys[i]; - if (!_areEquals(a[key], b[key])) - return false; - } - return true; - } - return a !== a && b !== b; -} - -var core = /*#__PURE__*/Object.freeze({ - __proto__: null, - JsonPatchError: JsonPatchError, - deepClone: deepClone, - getValueByPointer: getValueByPointer, - applyOperation: applyOperation, - applyPatch: applyPatch, - applyReducer: applyReducer, - validator: validator$1, - validate: validate$1, - _areEquals: _areEquals -}); - -/*! - * https://github.com/Starcounter-Jack/JSON-Patch - * (c) 2017-2021 Joachim Wester - * MIT license - */ -var beforeDict = new WeakMap(); -var Mirror = /** @class */ (function () { - function Mirror(obj) { - this.observers = new Map(); - this.obj = obj; - } - return Mirror; -}()); -var ObserverInfo = /** @class */ (function () { - function ObserverInfo(callback, observer) { - this.callback = callback; - this.observer = observer; - } - return ObserverInfo; -}()); -function getMirror(obj) { - return beforeDict.get(obj); -} -function getObserverFromMirror(mirror, callback) { - return mirror.observers.get(callback); -} -function removeObserverFromMirror(mirror, observer) { - mirror.observers.delete(observer.callback); -} -/** - * Detach an observer from an object - */ -function unobserve(root, observer) { - observer.unobserve(); -} -/** - * Observes changes made to an object, which can then be retrieved using generate - */ -function observe(obj, callback) { - var patches = []; - var observer; - var mirror = getMirror(obj); - if (!mirror) { - mirror = new Mirror(obj); - beforeDict.set(obj, mirror); - } - else { - var observerInfo = getObserverFromMirror(mirror, callback); - observer = observerInfo && observerInfo.observer; - } - if (observer) { - return observer; - } - observer = {}; - mirror.value = _deepClone(obj); - if (callback) { - observer.callback = callback; - observer.next = null; - var dirtyCheck = function () { - generate(observer); - }; - var fastCheck = function () { - clearTimeout(observer.next); - observer.next = setTimeout(dirtyCheck); - }; - if (typeof window !== 'undefined') { //not Node - window.addEventListener('mouseup', fastCheck); - window.addEventListener('keyup', fastCheck); - window.addEventListener('mousedown', fastCheck); - window.addEventListener('keydown', fastCheck); - window.addEventListener('change', fastCheck); - } - } - observer.patches = patches; - observer.object = obj; - observer.unobserve = function () { - generate(observer); - clearTimeout(observer.next); - removeObserverFromMirror(mirror, observer); - if (typeof window !== 'undefined') { - window.removeEventListener('mouseup', fastCheck); - window.removeEventListener('keyup', fastCheck); - window.removeEventListener('mousedown', fastCheck); - window.removeEventListener('keydown', fastCheck); - window.removeEventListener('change', fastCheck); - } - }; - mirror.observers.set(callback, new ObserverInfo(callback, observer)); - return observer; -} -/** - * Generate an array of patches from an observer - */ -function generate(observer, invertible) { - if (invertible === void 0) { invertible = false; } - var mirror = beforeDict.get(observer.object); - _generate(mirror.value, observer.object, observer.patches, "", invertible); - if (observer.patches.length) { - applyPatch(mirror.value, observer.patches); - } - var temp = observer.patches; - if (temp.length > 0) { - observer.patches = []; - if (observer.callback) { - observer.callback(temp); - } - } - return temp; -} -// Dirty check if obj is different from mirror, generate patches and update mirror -function _generate(mirror, obj, patches, path, invertible) { - if (obj === mirror) { - return; - } - if (typeof obj.toJSON === "function") { - obj = obj.toJSON(); - } - var newKeys = _objectKeys(obj); - var oldKeys = _objectKeys(mirror); - var deleted = false; - //if ever "move" operation is implemented here, make sure this test runs OK: "should not generate the same patch twice (move)" - for (var t = oldKeys.length - 1; t >= 0; t--) { - var key = oldKeys[t]; - var oldVal = mirror[key]; - if (hasOwnProperty$1(obj, key) && !(obj[key] === undefined && oldVal !== undefined && Array.isArray(obj) === false)) { - var newVal = obj[key]; - if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null && Array.isArray(oldVal) === Array.isArray(newVal)) { - _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key), invertible); - } - else { - if (oldVal !== newVal) { - if (invertible) { - patches.push({ op: "test", path: path + "/" + escapePathComponent(key), value: _deepClone(oldVal) }); - } - patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: _deepClone(newVal) }); - } - } - } - else if (Array.isArray(mirror) === Array.isArray(obj)) { - if (invertible) { - patches.push({ op: "test", path: path + "/" + escapePathComponent(key), value: _deepClone(oldVal) }); - } - patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) }); - deleted = true; // property has been deleted - } - else { - if (invertible) { - patches.push({ op: "test", path: path, value: mirror }); - } - patches.push({ op: "replace", path: path, value: obj }); - } - } - if (!deleted && newKeys.length == oldKeys.length) { - return; - } - for (var t = 0; t < newKeys.length; t++) { - var key = newKeys[t]; - if (!hasOwnProperty$1(mirror, key) && obj[key] !== undefined) { - patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: _deepClone(obj[key]) }); - } - } -} -/** - * Create an array of patches from the differences in two objects - */ -function compare(tree1, tree2, invertible) { - if (invertible === void 0) { invertible = false; } - var patches = []; - _generate(tree1, tree2, patches, '', invertible); - return patches; -} - -var duplex = /*#__PURE__*/Object.freeze({ - __proto__: null, - unobserve: unobserve, - observe: observe, - generate: generate, - compare: compare -}); - -var index = Object.assign({}, core, duplex, { - JsonPatchError: PatchError, - deepClone: _deepClone, - escapePathComponent, - unescapePathComponent -}); - -var fastJsonPatch = /*#__PURE__*/Object.freeze({ - __proto__: null, - 'default': index, - JsonPatchError: PatchError, - deepClone: _deepClone, - escapePathComponent: escapePathComponent, - unescapePathComponent: unescapePathComponent, - getValueByPointer: getValueByPointer, - applyOperation: applyOperation, - applyPatch: applyPatch, - applyReducer: applyReducer, - validator: validator$1, - validate: validate$1, - _areEquals: _areEquals, - unobserve: unobserve, - observe: observe, - generate: generate, - compare: compare -}); - -var require$$0 = /*@__PURE__*/getAugmentedNamespace(fastJsonPatch); - -function dateToISOString$2(date) { - let tzo = -date.getTimezoneOffset(); - let dif = tzo >= 0 ? '+' : '-'; - - function pad(num) { - let norm = Math.floor(Math.abs(num)); - return (norm < 10 ? '0' : '') + norm; - } - - function padMilli(d) { - return (d.getMilliseconds() + '').padStart(3, '0'); - } - - return date.getFullYear() + - '-' + pad(date.getMonth() + 1) + - '-' + pad(date.getDate()) + - 'T' + pad(date.getHours()) + - ':' + pad(date.getMinutes()) + - ':' + pad(date.getSeconds()) + - '.' + padMilli(date) + - dif + pad(tzo / 60) + - ':' + pad(tzo % 60); -} - -var dateToISOString_1 = dateToISOString$2; - -let dateToISOString$1 = dateToISOString_1; -const isNode = (typeof window === 'undefined'); - -function stringify$4(value) { - return JSON.stringify(value, replacer); -} - -function replacer(key, value) { - if (isNode && isNodeBuffer(value)) - return value.toString('base64'); - // @ts-ignore - else if (value instanceof Date && !isNaN(value)) - return dateToISOString$1(value); - else - return value; +}();import * as fastJsonPatch from 'fast-json-patch'; +import * as uuid from 'uuid'; +import * as axios from 'axios'; +import * as _default from 'rfdc/default'; +import * as ajv from 'ajv'; +import * as onChange from '@lroal/on-change'; + +function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } -function isNodeBuffer(object) { - return Buffer.isBuffer(object); +function getDefaultExportFromNamespaceIfPresent (n) { + return n && Object.prototype.hasOwnProperty.call(n, 'default') ? n['default'] : n; } -var stringify_1 = stringify$4; - -// Unique ID creation requires a high quality random # generator. In the browser we therefore -// require the crypto API and do not support built-in fallback to lower quality random number -// generators (like Math.random()). -var getRandomValues; -var rnds8 = new Uint8Array(16); -function rng() { - // lazy load so that environments that need to polyfill have a chance to do so - if (!getRandomValues) { - // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also, - // find the complete implementation of crypto (msCrypto) on IE11. - getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto); +var getTSDefinition_1; +var hasRequiredGetTSDefinition; + +function requireGetTSDefinition () { + if (hasRequiredGetTSDefinition) return getTSDefinition_1; + hasRequiredGetTSDefinition = 1; + const typeMap = { + StringColumn: 'string', + BooleanColumn: 'boolean', + UUIDColumn: 'string', + BinaryColumn: 'string', + JSONColumn: 'object', + DateColumn: 'Date | string', + NumberColumn: 'number', + }; - if (!getRandomValues) { - throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); - } - } + function getTSDefinition(tableConfigs, {isNamespace = false, isHttp = false} = {}) { + const rootTablesAdded = new Map(); + const tableNames = new Set(); + const tablesAdded = new Map(); + let src = ''; + const defs = tableConfigs.map(getTSDefinitionTable).join(''); + const tables = tableConfigs.reduce((tables, x) => { + tables[x.name] = x.table; + return tables; + }, {}); + src += getPrefixTs(isNamespace); + if (isNamespace) + src += startNamespace(tables, isHttp); + src += defs; + src += getRdbClientTs(tables, isHttp); + if (isNamespace) + src += '}'; + return src; + + + function getTSDefinitionTable({table, customFilters, name}) { + let Name = name.substr(0, 1).toUpperCase() + name.substr(1); + name = name.substr(0, 1).toLowerCase() + name.substr(1); + let result = '' + getTable(table, Name, name, customFilters); + return result; + } - return getRandomValues(rnds8); + function getTable(table, Name, name, customFilters) { + const _columns = columns(table); + const _tableRelations = tableRelations(table); + return ` +export interface ${Name}Table { + count(filter?: RawFilter): Promise {
+// parent: P;
+// }
+
+// interface AType extends HasParent {
+// title: string;
+// }
+
+// // Here is the key part: By using a circular type definition, we create a
+// // specific pair (CyclicA, CyclicB) that point to each other generically.
+// // 'CyclicA' extends 'AType' and expects a child of type 'CyclicB'.
+// // 'CyclicB' extends 'BType' and expects a parent of type 'CyclicA'.
+
+// // Note: TypeScript allows this pattern as long as the interfaces are defined first.
+// // The cycle is established by the subsequent type definitions.
+
+// type CyclicA = AType