@sap/cds 6.8.3 → 7.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.
- package/CHANGELOG.md +61 -2
- package/README.md +0 -1
- package/bin/cds-serve.js +50 -3
- package/bin/deploy/to-hana.js +1 -0
- package/bin/serve.js +16 -20
- package/lib/auth/basic-auth.js +6 -4
- package/lib/auth/index.js +4 -3
- package/lib/auth/jwt-auth.js +2 -5
- package/lib/compile/cds-compile.js +34 -89
- package/lib/compile/cdsc.js +11 -0
- package/lib/compile/etc/properties.js +2 -2
- package/lib/compile/for/lean_drafts.js +36 -69
- package/lib/compile/for/nodejs.js +2 -1
- package/lib/compile/load.js +1 -1
- package/lib/compile/minify.js +2 -0
- package/lib/compile/to/csn.js +74 -0
- package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +8 -6
- package/lib/dbs/cds-deploy.js +174 -114
- package/lib/env/cds-env.js +64 -79
- package/lib/env/cds-requires.js +11 -28
- package/lib/env/defaults.js +13 -3
- package/lib/env/plugins.js +1 -12
- package/lib/env/presets.js +25 -21
- package/lib/index.js +121 -147
- package/lib/{core/reflect.js → linked/models.js} +2 -2
- package/lib/{core/infer.js → linked/queries.js} +2 -0
- package/lib/{core/index.js → linked/types.js} +2 -1
- package/lib/log/cds-error.js +13 -7
- package/lib/log/format/cf.js +1 -1
- package/lib/plugins.js +49 -0
- package/lib/ql/Query.js +0 -9
- package/lib/ql/STREAM.js +0 -1
- package/lib/req/context.js +2 -7
- package/lib/req/request.js +6 -2
- package/lib/req/response.js +23 -10
- package/lib/srv/middlewares/ctx-model.js +1 -1
- package/lib/srv/middlewares/errors.js +1 -1
- package/lib/srv/protocols/_legacy.js +1 -0
- package/lib/srv/protocols/graphql.js +7 -16
- package/lib/srv/protocols/index.js +59 -45
- package/lib/srv/protocols/odata-v2-proxy.js +2 -70
- package/lib/srv/srv-api.js +9 -3
- package/lib/srv/srv-dispatch.js +12 -9
- package/lib/srv/srv-models.js +4 -21
- package/lib/srv/srv-tx.js +15 -12
- package/lib/utils/cds-test.js +14 -9
- package/lib/utils/cds-utils.js +2 -12
- package/lib/utils/check-version.js +17 -0
- package/{bin/build → lib/utils}/csv-reader.js +23 -24
- package/libx/_runtime/auth/index.js +27 -23
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
- package/libx/_runtime/cds-services/services/Service.js +79 -107
- package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
- package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
- package/libx/_runtime/cds-services/util/assert.js +65 -2
- package/libx/_runtime/common/composition/data.js +1 -0
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/restrict.js +5 -10
- package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
- package/libx/_runtime/common/generic/auth/utils.js +1 -2
- package/libx/_runtime/common/generic/crud.js +32 -16
- package/libx/_runtime/common/generic/etag.js +133 -104
- package/libx/_runtime/common/generic/input.js +6 -21
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/stream.js +52 -0
- package/libx/_runtime/common/generic/temporal.js +25 -8
- package/libx/_runtime/common/i18n/messages.properties +0 -2
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
- package/libx/_runtime/common/utils/csn.js +0 -51
- package/libx/_runtime/common/utils/etag.js +30 -0
- package/libx/_runtime/common/utils/keys.js +1 -1
- package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
- package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
- package/libx/_runtime/common/utils/stream.js +140 -0
- package/libx/_runtime/common/utils/streamProp.js +29 -12
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
- package/libx/_runtime/db/generic/index.js +0 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +2 -2
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/query/run.js +2 -2
- package/libx/_runtime/db/query/update.js +2 -2
- package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
- package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
- package/libx/_runtime/fiori/draft.js +2 -0
- package/libx/_runtime/fiori/generic/activate.js +8 -9
- package/libx/_runtime/fiori/generic/before.js +30 -20
- package/libx/_runtime/fiori/generic/cancel.js +5 -3
- package/libx/_runtime/fiori/generic/delete.js +5 -3
- package/libx/_runtime/fiori/generic/edit.js +7 -7
- package/libx/_runtime/fiori/generic/index.js +10 -16
- package/libx/_runtime/fiori/generic/new.js +5 -3
- package/libx/_runtime/fiori/generic/patch.js +11 -8
- package/libx/_runtime/fiori/generic/prepare.js +13 -6
- package/libx/_runtime/fiori/generic/read.js +12 -6
- package/libx/_runtime/fiori/lean-draft.js +207 -152
- package/libx/_runtime/fiori/utils/delete.js +10 -5
- package/libx/_runtime/fiori/utils/req.js +17 -5
- package/libx/_runtime/fiori/utils/stream.js +36 -0
- package/libx/_runtime/hana/Service.js +12 -9
- package/libx/_runtime/hana/conversion.js +10 -15
- package/libx/_runtime/hana/driver.js +2 -0
- package/libx/_runtime/hana/execute.js +28 -6
- package/libx/_runtime/hana/pool.js +36 -122
- package/libx/_runtime/hana/search2cqn4sql.js +34 -36
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/remote/Service.js +20 -1
- package/libx/_runtime/remote/utils/client.js +3 -5
- package/libx/_runtime/sqlite/Service.js +4 -6
- package/libx/_runtime/sqlite/conversion.js +3 -13
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
- package/libx/_runtime/sqlite/execute.js +5 -16
- package/libx/odata/afterburner.js +22 -6
- package/libx/odata/grammar.pegjs +6 -1
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +16 -9
- package/libx/rest/RestRequest.js +1 -1
- package/libx/rest/middleware/input.js +2 -1
- package/libx/rest/middleware/operation.js +1 -0
- package/libx/rest/middleware/parse.js +3 -2
- package/libx/rest/middleware/payload.js +9 -8
- package/libx/rest/middleware/read.js +1 -0
- package/package.json +9 -16
- package/app/fiori/preview.js +0 -270
- package/app/fiori/routes.js +0 -59
- package/bin/build/buildTaskEngine.js +0 -360
- package/bin/build/buildTaskFactory.js +0 -283
- package/bin/build/buildTaskHandler.js +0 -241
- package/bin/build/buildTaskProvider.js +0 -22
- package/bin/build/buildTaskProviderFactory.js +0 -175
- package/bin/build/cds.js +0 -5
- package/bin/build/constants.js +0 -66
- package/bin/build/index.js +0 -58
- package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
- package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
- package/bin/build/provider/buildTaskProviderInternal.js +0 -383
- package/bin/build/provider/fiori/index.js +0 -171
- package/bin/build/provider/hana/2migration.js +0 -179
- package/bin/build/provider/hana/index.js +0 -505
- package/bin/build/provider/hana/migrationtable.js +0 -472
- package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
- package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
- package/bin/build/provider/hana/template/.hdinamespace +0 -4
- package/bin/build/provider/hana/template/package.json +0 -12
- package/bin/build/provider/hana/template/undeploy.json +0 -5
- package/bin/build/provider/java/index.js +0 -111
- package/bin/build/provider/java-cf/index.js +0 -1
- package/bin/build/provider/mtx/index.js +0 -268
- package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
- package/bin/build/provider/mtx-extension/index.js +0 -131
- package/bin/build/provider/mtx-sidecar/index.js +0 -137
- package/bin/build/provider/node-cf/index.js +0 -1
- package/bin/build/provider/nodejs/index.js +0 -192
- package/bin/build/util.js +0 -299
- package/bin/cds.js +0 -125
- package/bin/deploy/to-hana/cfUtil.js +0 -355
- package/bin/deploy/to-hana/gitUtil.js +0 -57
- package/bin/deploy/to-hana/hana.js +0 -306
- package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
- package/bin/deploy/to-hana/index.js +0 -16
- package/bin/deploy/to-hana/mtaUtil.js +0 -170
- package/bin/mtx/in-cds.js +0 -17
- package/bin/plugins.js +0 -32
- package/bin/run.js +0 -24
- package/bin/utils/log.js +0 -24
- package/bin/version.js +0 -178
- package/libx/_runtime/audit/Service.js +0 -222
- package/libx/_runtime/audit/generic/personal/access.js +0 -61
- package/libx/_runtime/audit/generic/personal/index.js +0 -56
- package/libx/_runtime/audit/generic/personal/modification.js +0 -132
- package/libx/_runtime/audit/generic/personal/utils.js +0 -186
- package/libx/_runtime/audit/utils/log.js +0 -23
- package/libx/_runtime/audit/utils/v2.js +0 -176
- package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
- package/libx/_runtime/db/generic/integrity.js +0 -455
- package/srv/audit-log.cds +0 -87
- package/srv/mtx.cds +0 -2
- package/srv/mtx.js +0 -8
- /package/lib/{core → linked}/classes.js +0 -0
- /package/lib/{core → linked}/entities.js +0 -0
|
@@ -27,13 +27,10 @@ const _checkAppURL = appURL => {
|
|
|
27
27
|
)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const _oldMtx = () => cds.mtx
|
|
31
|
-
const _multitenancyEnabled = () => cds.requires.multitenancy || _oldMtx()
|
|
32
|
-
|
|
33
30
|
// REVISIT: It's bad to have to rely on the subdomain.
|
|
34
31
|
// For all interactions where we perform the token exchange ourselves,
|
|
35
32
|
// we will be able to use the zoneId instead of the subdomain.
|
|
36
|
-
const _subdomainFromContext = context => context?.http.req?.authInfo?.getSubdomain()
|
|
33
|
+
const _subdomainFromContext = context => context?.http.req?.authInfo?.getSubdomain?.()
|
|
37
34
|
|
|
38
35
|
class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
39
36
|
init() {
|
|
@@ -41,61 +38,17 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
41
38
|
return super.init()
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
// Needs to be run after `served` event, otherwise `ProvisioningService` might not be available.
|
|
45
|
-
// REVISIT: We should register the handlers before, otherwise a tenant subscription
|
|
46
|
-
// immediately after app start won't trigger those handlers.
|
|
47
|
-
async addMTXHandlers() {
|
|
48
|
-
const provisioning = await cds.connect.to('ProvisioningService')
|
|
49
|
-
const tenantPersistence = await cds.connect.to('TenantPersistenceService')
|
|
50
|
-
tenantPersistence.impl(() => {
|
|
51
|
-
tenantPersistence.on('createTenant', async (req, next) => {
|
|
52
|
-
const res = await next()
|
|
53
|
-
const subdomain = req.data.subscriptionData.subscribedSubdomain
|
|
54
|
-
const management = await this.getManagement(subdomain).waitUntilReady()
|
|
55
|
-
await management.deploy()
|
|
56
|
-
return res
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
provisioning.impl(() => {
|
|
60
|
-
provisioning.on('DELETE', 'tenant', async (req, next) => {
|
|
61
|
-
const subdomain = req.data.subscribedSubdomain
|
|
62
|
-
try {
|
|
63
|
-
const management = await this.getManagement(subdomain).waitUntilReady()
|
|
64
|
-
await management.undeploy()
|
|
65
|
-
} catch (error) {
|
|
66
|
-
this.LOG.error('Failed to delete messaging artifacts for subdomain', subdomain, '(', error, ')')
|
|
67
|
-
}
|
|
68
|
-
return next()
|
|
69
|
-
})
|
|
70
|
-
provisioning.on('dependencies', async (req, next) => {
|
|
71
|
-
this.LOG._info && this.LOG.info('Include Enterprise-Messaging as SaaS dependency')
|
|
72
|
-
const res = await next()
|
|
73
|
-
const xsappname = this.options.credentials && this.options.credentials.xsappname
|
|
74
|
-
if (xsappname) {
|
|
75
|
-
const exists = res.some(d => d.xsappname === xsappname)
|
|
76
|
-
if (!exists) res.push({ xsappname })
|
|
77
|
-
}
|
|
78
|
-
return res
|
|
79
|
-
})
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
41
|
// New mtx based on @sap/cds-mtxs
|
|
84
42
|
async addMTXSHandlers() {
|
|
85
43
|
// REVISIT: Is that tested with MTX services in sidecar?
|
|
86
44
|
const deploymentSrv = await cds.connect.to('cds.xt.DeploymentService')
|
|
87
45
|
const provisioningSrv = await cds.connect.to('cds.xt.SaasProvisioningService')
|
|
88
46
|
deploymentSrv.impl(() => {
|
|
89
|
-
deploymentSrv.after('subscribe', async (
|
|
47
|
+
deploymentSrv.after('subscribe', async (_res, req) => {
|
|
90
48
|
const { tenant } = req.data
|
|
91
49
|
let subdomain
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
subdomain = tenantInfo.subdomain
|
|
95
|
-
} catch (e) {
|
|
96
|
-
this.LOG.error("'subscribe' is not yet implemented for @sap/cds-mtxs")
|
|
97
|
-
throw e
|
|
98
|
-
}
|
|
50
|
+
const tenantInfo = await getTenantInfo(tenant)
|
|
51
|
+
subdomain = tenantInfo.subdomain
|
|
99
52
|
const management = await this.getManagement(subdomain).waitUntilReady()
|
|
100
53
|
await management.deploy()
|
|
101
54
|
})
|
|
@@ -103,10 +56,10 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
103
56
|
const { tenant } = req.data
|
|
104
57
|
let subdomain
|
|
105
58
|
try {
|
|
106
|
-
const tenantInfo = await getTenantInfo(tenant)
|
|
59
|
+
const tenantInfo = await getTenantInfo(tenant)
|
|
107
60
|
subdomain = tenantInfo.subdomain
|
|
108
61
|
} catch (e) {
|
|
109
|
-
|
|
62
|
+
if (e.status === 404) return // idempotent
|
|
110
63
|
throw e
|
|
111
64
|
}
|
|
112
65
|
try {
|
|
@@ -118,7 +71,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
118
71
|
})
|
|
119
72
|
})
|
|
120
73
|
provisioningSrv.impl(() => {
|
|
121
|
-
provisioningSrv.on('dependencies', async (
|
|
74
|
+
provisioningSrv.on('dependencies', async (_req, next) => {
|
|
122
75
|
this.LOG._info && this.LOG.info('Include Enterprise-Messaging as SaaS dependency')
|
|
123
76
|
const res = (await next()) || []
|
|
124
77
|
const xsappname = this.options.credentials?.xsappname
|
|
@@ -132,7 +85,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
132
85
|
}
|
|
133
86
|
|
|
134
87
|
startListening() {
|
|
135
|
-
const doNotDeploy =
|
|
88
|
+
const doNotDeploy = cds.requires.multitenancy && !this.options.deployForProvider
|
|
136
89
|
if (doNotDeploy) this.LOG._info && this.LOG.info('Skipping deployment of messaging artifacts for provider account')
|
|
137
90
|
super.startListening({ doNotDeploy })
|
|
138
91
|
if (!doNotDeploy && (this._listenToAll || this.subscribedTopics.size)) {
|
|
@@ -151,9 +104,8 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
151
104
|
async listenToClient(cb) {
|
|
152
105
|
_checkAppURL(this.optionsApp.appURL)
|
|
153
106
|
registerWebhookEndpoints(BASE_PATH, this.queueName, this.LOG, cb)
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
else await this.addMTXSHandlers()
|
|
107
|
+
if (cds.requires.multitenancy) {
|
|
108
|
+
await this.addMTXSHandlers()
|
|
157
109
|
registerDeployEndpoints(BASE_PATH, this.queueName, async (tenantInfo, options) => {
|
|
158
110
|
const result = { queue: this.queueName, succeeded: [], failed: [] }
|
|
159
111
|
await Promise.all(
|
|
@@ -37,7 +37,7 @@ const hasPersistentOutbox = (srv, tenant) => {
|
|
|
37
37
|
if (!cds.requires.outbox || cds.requires.outbox.kind !== 'persistent-outbox') return false
|
|
38
38
|
if (srv.options && srv.options.outbox && srv.options.outbox.kind && srv.options.outbox.kind !== 'persistent-outbox')
|
|
39
39
|
return false
|
|
40
|
-
if (
|
|
40
|
+
if (cds.requires.multitenancy && tenant && _isProviderTenant(tenant)) return false // no persistence for provider account
|
|
41
41
|
return true
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -7,13 +7,19 @@ const { getKind, run, getDestination, getAdditionalOptions, getReqOptions } = re
|
|
|
7
7
|
const { formatVal } = require('../../odata/utils')
|
|
8
8
|
const { hasAliasedColumns } = require('./utils/data')
|
|
9
9
|
|
|
10
|
-
let _cloudSdkConnectivity
|
|
10
|
+
let _cloudSdkConnectivity, _cloudSdkResilience
|
|
11
11
|
const cloudSdkConnectivity = () => {
|
|
12
12
|
if (_cloudSdkConnectivity) return _cloudSdkConnectivity
|
|
13
13
|
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
14
14
|
_cloudSdkConnectivity = require('@sap-cloud-sdk/connectivity')
|
|
15
15
|
return _cloudSdkConnectivity
|
|
16
16
|
}
|
|
17
|
+
const cloudSdkResilience = () => {
|
|
18
|
+
if (_cloudSdkResilience !== undefined) return _cloudSdkResilience
|
|
19
|
+
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
20
|
+
_cloudSdkResilience = require('@sap-cloud-sdk/resilience')
|
|
21
|
+
return _cloudSdkResilience
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
const _isSimpleCqnQuery = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
|
|
19
25
|
|
|
@@ -200,9 +206,15 @@ class RemoteService extends cds.Service {
|
|
|
200
206
|
getDestination(this.definition?.name ?? this.datasource, this.options.credentials)
|
|
201
207
|
this.path = this.options.credentials.path
|
|
202
208
|
|
|
209
|
+
// `requestTimeout` API is kept as it was public
|
|
203
210
|
this.requestTimeout = this.options.credentials.requestTimeout
|
|
204
211
|
if (this.requestTimeout == null) this.requestTimeout = 60000
|
|
205
212
|
|
|
213
|
+
// we're using this as an object to allow remote services without the need for Cloud SDK
|
|
214
|
+
// required for BAS creating remote services only for events
|
|
215
|
+
// at first request the middlewares are created
|
|
216
|
+
this._resilienceMiddlewares = {}
|
|
217
|
+
|
|
206
218
|
// REVISIT: remove cds.env.features.fetch_csrf in next major ^7
|
|
207
219
|
this.csrf = cds.env.features.fetch_csrf ?? this.options.csrf
|
|
208
220
|
this.csrfInBatch = this.options.csrfInBatch
|
|
@@ -250,6 +262,13 @@ class RemoteService extends cds.Service {
|
|
|
250
262
|
}
|
|
251
263
|
|
|
252
264
|
this.on('*', async (req, next) => {
|
|
265
|
+
// early validation on first request for use case without remote API
|
|
266
|
+
// ideally, that's done on bootstrap of the remote service
|
|
267
|
+
if (typeof this.destination === 'object' && !this.destination.url)
|
|
268
|
+
throw new Error(`"url" or "destination" property must be configured in "credentials" of "${this.name}".`)
|
|
269
|
+
if (this._resilienceMiddlewares && !this._resilienceMiddlewares.timeout)
|
|
270
|
+
this._resilienceMiddlewares.timeout = cloudSdkResilience().timeout(this.requestTimeout)
|
|
271
|
+
|
|
253
272
|
const { query } = req
|
|
254
273
|
if (!query && !(typeof req.path === 'string')) return next()
|
|
255
274
|
|
|
@@ -77,10 +77,6 @@ const cloudSdk = () => {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const getDestination = (name, credentials) => {
|
|
80
|
-
if (!credentials.url) {
|
|
81
|
-
throw new Error(`"url" or "destination" property must be configured in "credentials" of "${name}".`)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
80
|
// Cloud SDK wants property "queryParameters" but we have documented "queries"
|
|
85
81
|
if (credentials.queries && !credentials.queryParameters) {
|
|
86
82
|
credentials.queryParameters = credentials.queries
|
|
@@ -466,7 +462,9 @@ const getReqOptions = (req, query, service) => {
|
|
|
466
462
|
}
|
|
467
463
|
|
|
468
464
|
reqOptions.headers = { accept: 'application/json,text/plain' }
|
|
469
|
-
|
|
465
|
+
|
|
466
|
+
// add resilience middlewares for Cloud SDK
|
|
467
|
+
reqOptions.middleware = [service._resilienceMiddlewares.timeout]
|
|
470
468
|
|
|
471
469
|
if (!_hasHeader(req.headers, 'accept-language')) {
|
|
472
470
|
// Forward the locale properties from the original request (including region variants or weight factors),
|
|
@@ -56,12 +56,12 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
56
56
|
* tx
|
|
57
57
|
*/
|
|
58
58
|
this.on(['BEGIN', 'COMMIT', 'ROLLBACK'], function (req) {
|
|
59
|
+
// REVISIT: better?
|
|
60
|
+
this.dbc._closed = req.event !== 'BEGIN'
|
|
61
|
+
|
|
59
62
|
return this._run(this.model, this.dbc, req.event)
|
|
60
63
|
})
|
|
61
64
|
|
|
62
|
-
// REVISIT: register only if needed?
|
|
63
|
-
this.before('COMMIT', this._integrity.performCheck)
|
|
64
|
-
|
|
65
65
|
/*
|
|
66
66
|
* generic
|
|
67
67
|
*/
|
|
@@ -79,9 +79,6 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
79
79
|
this.before('READ', '*', convertAssocToOneManaged)
|
|
80
80
|
this.before('READ', '*', localized) // > has to run after rewrite
|
|
81
81
|
this.before('READ', '*', this._virtual)
|
|
82
|
-
|
|
83
|
-
// REVISIT: get data to be deleted for integrity check
|
|
84
|
-
this.before('DELETE', '*', this._integrity.beforeDelete)
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
_registerOnHandlers() {
|
|
@@ -104,6 +101,7 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
104
101
|
getDbUrl(tenant) {
|
|
105
102
|
return this.url4(tenant)
|
|
106
103
|
}
|
|
104
|
+
|
|
107
105
|
url4(tenant) {
|
|
108
106
|
const credentials = this.options.credentials || this.options || {}
|
|
109
107
|
let dbUrl = credentials.database || credentials.url || credentials.host || ':memory:'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const normalizeTimestamp = require('../common/utils/normalizeTimestamp')
|
|
2
2
|
|
|
3
3
|
const convertToBoolean = boolean => {
|
|
4
4
|
if (boolean === null) return null
|
|
@@ -21,9 +21,9 @@ const convertInt64ToString = int64 => {
|
|
|
21
21
|
const convertToISOTime = value => {
|
|
22
22
|
if (value === null) return value
|
|
23
23
|
|
|
24
|
-
if (!value)
|
|
24
|
+
if (!value) return normalizeTimestamp(new Date(0))
|
|
25
25
|
|
|
26
|
-
return
|
|
26
|
+
return normalizeTimestamp(value)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const convertToISONoMillis = element => {
|
|
@@ -44,14 +44,4 @@ const SQLITE_TYPE_CONVERSION_MAP = new Map([
|
|
|
44
44
|
['cds.Timestamp', convertToISOTime]
|
|
45
45
|
])
|
|
46
46
|
|
|
47
|
-
if (cds.env.features.bigjs) {
|
|
48
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
49
|
-
const Big = require('big.js')
|
|
50
|
-
const convertToBig = value => new Big(value)
|
|
51
|
-
|
|
52
|
-
SQLITE_TYPE_CONVERSION_MAP.set('cds.Integer64', convertToBig)
|
|
53
|
-
SQLITE_TYPE_CONVERSION_MAP.set('cds.Int64', convertToBig)
|
|
54
|
-
SQLITE_TYPE_CONVERSION_MAP.set('cds.Decimal', convertToBig)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
47
|
module.exports = { SQLITE_TYPE_CONVERSION_MAP }
|
|
@@ -46,13 +46,16 @@ class CustomFunctionBuilder extends FunctionBuilder {
|
|
|
46
46
|
default:
|
|
47
47
|
break
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (typeof args === 'string') {
|
|
52
|
-
this._outputObj.sql.push(args, ')')
|
|
49
|
+
if (functionName === 'INSTR') {
|
|
50
|
+
super._handleIndexof(args, functionName)
|
|
53
51
|
} else {
|
|
54
|
-
this.
|
|
55
|
-
|
|
52
|
+
this._outputObj.sql.push(functionName, '(')
|
|
53
|
+
if (typeof args === 'string') {
|
|
54
|
+
this._outputObj.sql.push(args, ')')
|
|
55
|
+
} else {
|
|
56
|
+
this._addFunctionArgs(args)
|
|
57
|
+
this._outputObj.sql.push(')')
|
|
58
|
+
}
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -2,6 +2,11 @@ const InsertBuilder = require('../../db/sql-builder').InsertBuilder
|
|
|
2
2
|
const getAnnotatedColumns = require('../../db/sql-builder/annotations')
|
|
3
3
|
|
|
4
4
|
class CustomUpsertBuilder extends InsertBuilder {
|
|
5
|
+
constructor(obj, options, csn) {
|
|
6
|
+
super(obj, options, csn)
|
|
7
|
+
this._UPSERT = true
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
annotatedColumns(entityName, csn) {
|
|
6
11
|
const { updateAnnotatedColumns } = getAnnotatedColumns(entityName, csn)
|
|
7
12
|
|
|
@@ -18,7 +23,7 @@ class CustomUpsertBuilder extends InsertBuilder {
|
|
|
18
23
|
super.build()
|
|
19
24
|
const csnKeys =
|
|
20
25
|
(this._obj._target ? this._obj._target.keys : this._csn.definitions[this._obj.INSERT.into].keys) || {}
|
|
21
|
-
const keys = Object.keys(csnKeys).filter(k => !csnKeys[k].isAssociation)
|
|
26
|
+
const keys = Object.keys(csnKeys).filter(k => !csnKeys[k].isAssociation && k !== 'IsActiveEntity')
|
|
22
27
|
const updates = []
|
|
23
28
|
const columns = this._obj.INSERT.columns || Object.keys(this._obj.INSERT.entries[0])
|
|
24
29
|
if (this.managedCols) {
|
|
@@ -71,23 +71,9 @@ function executeSelectSQL(dbc, sql, values, isOne, postMapper) {
|
|
|
71
71
|
return new Promise((resolve, reject) => {
|
|
72
72
|
_exec(dbc, isOne ? 'get' : 'all', sql, values, (err, result) => {
|
|
73
73
|
if (err) return reject(err)
|
|
74
|
-
|
|
75
|
-
// REVISIT
|
|
76
|
-
// .get returns undefined if nothing in db
|
|
77
|
-
// our coding expects the result to be null if isOne does not return anything
|
|
78
|
-
// REVISIT: -> we should definitely fix that coding which expects null
|
|
79
|
-
if (isOne && result === undefined) {
|
|
80
|
-
result = null
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
result = postProcess(result, postMapper)
|
|
85
|
-
resolve(result)
|
|
86
|
-
} catch (e) {
|
|
87
|
-
reject(e)
|
|
88
|
-
}
|
|
74
|
+
resolve(result)
|
|
89
75
|
})
|
|
90
|
-
})
|
|
76
|
+
}).then(result => postProcess(result, postMapper))
|
|
91
77
|
}
|
|
92
78
|
|
|
93
79
|
function _processExpand(model, dbc, cqn, user, locale, txTimestamp) {
|
|
@@ -162,6 +148,9 @@ const _executeBulkInsertSQL = (dbc, sql, values) =>
|
|
|
162
148
|
const stmt = _exec(dbc, 'prepare', sql, err => {
|
|
163
149
|
if (err) return reject(err)
|
|
164
150
|
|
|
151
|
+
// REVISIT: better?
|
|
152
|
+
if (dbc._closed) return reject(new Error('Transaction is already closed'))
|
|
153
|
+
|
|
165
154
|
if (!Array.isArray(values[0])) values = [values]
|
|
166
155
|
|
|
167
156
|
// guarantee order through counters in closure
|
|
@@ -2,6 +2,7 @@ const cds = require('../_runtime/cds')
|
|
|
2
2
|
|
|
3
3
|
const { where2obj, resolveFromSelect } = require('../_runtime/common/utils/cqn')
|
|
4
4
|
const { findCsnTargetFor } = require('../_runtime/common/utils/csn')
|
|
5
|
+
const normalizeTimestamp = require('../_runtime/common/utils/normalizeTimestamp')
|
|
5
6
|
|
|
6
7
|
const _addKeysDeep = (keys, keysCollector, ignoreManagedBacklinks) => {
|
|
7
8
|
for (const keyName in keys) {
|
|
@@ -184,6 +185,9 @@ function _convertVal(element, value) {
|
|
|
184
185
|
case 'cds.Boolean':
|
|
185
186
|
return typeof value === 'string' ? value === 'true' : value
|
|
186
187
|
|
|
188
|
+
case 'cds.Timestamp':
|
|
189
|
+
return normalizeTimestamp(value)
|
|
190
|
+
|
|
187
191
|
default:
|
|
188
192
|
return value
|
|
189
193
|
}
|
|
@@ -395,20 +399,32 @@ function _addKeys(columns, target) {
|
|
|
395
399
|
}
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
// remove duplicate * in expand (e.g. expand=*,*)
|
|
403
|
+
function _removeDuplicateAsterix(columns) {
|
|
404
|
+
let hasExpandStar = false
|
|
405
|
+
for (let i = columns.length - 1; i > 0; i--) {
|
|
406
|
+
const column = columns[i]
|
|
407
|
+
if (!hasExpandStar && !column.ref && column?.expand?.[0] === '*') hasExpandStar = true
|
|
408
|
+
else if (hasExpandStar && !column.ref && column?.expand[0] === '*') {
|
|
409
|
+
columns.splice(i, 1)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
398
414
|
function _processColumns(cqn, target) {
|
|
399
415
|
if (cqn.SELECT.from.SELECT) _processColumns(cqn.SELECT.from, target)
|
|
400
416
|
|
|
401
|
-
|
|
417
|
+
let columns = cqn.SELECT.columns
|
|
402
418
|
|
|
403
419
|
// REVISIT Keys should be added only in case of odata, not e.g. rest.
|
|
404
420
|
// Currently odata is detected via odata_new_parser flag -> find a better indicator.
|
|
405
|
-
if (columns && !cqn.SELECT.groupBy
|
|
421
|
+
if (columns && !cqn.SELECT.groupBy) {
|
|
406
422
|
let entity
|
|
407
423
|
if (target.kind === 'entity') entity = target
|
|
408
424
|
else if (target.kind === 'action' && target.returns?.kind === 'entity') entity = target.returns
|
|
409
425
|
if (!entity) return
|
|
410
|
-
|
|
411
|
-
_addKeys(columns, entity)
|
|
426
|
+
_removeDuplicateAsterix(columns)
|
|
427
|
+
if (cds.env.features.odata_new_parser) _addKeys(columns, entity)
|
|
412
428
|
}
|
|
413
429
|
|
|
414
430
|
if (!Array.isArray(columns)) return
|
|
@@ -514,7 +530,7 @@ function _4service(service) {
|
|
|
514
530
|
|
|
515
531
|
// REVISIT: better
|
|
516
532
|
// set target (csn definition) for later retrieval
|
|
517
|
-
cqn.__target = current
|
|
533
|
+
cqn.__target = current.parent?.kind === 'entity' ? `${current.parent.name}:$:${current.name}` : current.name
|
|
518
534
|
|
|
519
535
|
// target <=> endpoint entity, all navigation refs must be resolvable accordingly
|
|
520
536
|
if (cds.env.effective.odata.structs) _resolveAliasesInNavigation(cqn, target)
|
|
@@ -522,7 +538,7 @@ function _4service(service) {
|
|
|
522
538
|
/*
|
|
523
539
|
* add default aggregation function (and alias)
|
|
524
540
|
*/
|
|
525
|
-
_processColumns(cqn,
|
|
541
|
+
_processColumns(cqn, current)
|
|
526
542
|
|
|
527
543
|
return cqn
|
|
528
544
|
}
|
package/libx/odata/grammar.pegjs
CHANGED
|
@@ -656,6 +656,8 @@
|
|
|
656
656
|
|
|
657
657
|
val
|
|
658
658
|
= val:(bool / date) {return {val}}
|
|
659
|
+
/ val:time {return {val}}
|
|
660
|
+
/ val:date {return {val}}
|
|
659
661
|
/ val:guid {return {val}}
|
|
660
662
|
/ val:number {return typeof val === 'number' ? {val} : { val, literal:'number' }}
|
|
661
663
|
/ val:string {return {val}}
|
|
@@ -846,6 +848,9 @@
|
|
|
846
848
|
word "a string"
|
|
847
849
|
= $([^ \t\n()"&;]+)
|
|
848
850
|
|
|
851
|
+
time "a time"
|
|
852
|
+
= $([0-9][0-9]":"[0-9][0-9]":"[0-9][0-9])
|
|
853
|
+
|
|
849
854
|
date "a date"
|
|
850
855
|
= s:$( [0-9]+"-"[0-9][0-9]"-"[0-9][0-9] // date
|
|
851
856
|
( "T"[0-9][0-9]":"[0-9][0-9](":"[0-9][0-9]("."[0-9]+)?)? // time
|
|
@@ -867,7 +872,7 @@
|
|
|
867
872
|
= s:$( [+-]? [0-9]+ ) { return parseInt(s) }
|
|
868
873
|
|
|
869
874
|
identifier "an identifier"
|
|
870
|
-
= !bool !guid s:$([_a-zA-Z][_a-zA-Z0-9"."]*) { return s }
|
|
875
|
+
= !bool !guid s:$([_a-zA-Z][_a-zA-Z0-9"."\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]*) { return s }
|
|
871
876
|
|
|
872
877
|
guid "a guid"
|
|
873
878
|
= $( hex16 hex16 "-"? hex16 "-"? hex16 "-"? hex16 "-"? hex16 hex16 hex16 )
|