velocious 1.0.470 → 1.0.472

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 (83) hide show
  1. package/README.md +5 -3
  2. package/build/configuration-types.js +1 -0
  3. package/build/configuration.js +27 -1
  4. package/build/controller.js +13 -1
  5. package/build/database/datetime-storage.js +49 -5
  6. package/build/database/record/index.js +14 -4
  7. package/build/environment-handlers/base.js +46 -0
  8. package/build/environment-handlers/node/cli/commands/generate/base-models.js +4 -1
  9. package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +368 -43
  10. package/build/environment-handlers/node/cli/commands/generate/generated-file-banner.js +15 -0
  11. package/build/environment-handlers/node.js +84 -8
  12. package/build/frontend-model-controller.js +7 -7
  13. package/build/frontend-models/base.js +75 -9
  14. package/build/frontend-models/transport-serialization.js +41 -8
  15. package/build/frontend-models/websocket-channel.js +18 -1
  16. package/build/frontend-models/websocket-publishers.js +12 -1
  17. package/build/http-server/client/request-runner.js +13 -1
  18. package/build/src/configuration-types.d.ts +5 -0
  19. package/build/src/configuration-types.d.ts.map +1 -1
  20. package/build/src/configuration-types.js +2 -1
  21. package/build/src/configuration.d.ts +14 -1
  22. package/build/src/configuration.d.ts.map +1 -1
  23. package/build/src/configuration.js +25 -2
  24. package/build/src/controller.d.ts +5 -0
  25. package/build/src/controller.d.ts.map +1 -1
  26. package/build/src/controller.js +12 -2
  27. package/build/src/database/datetime-storage.d.ts +12 -4
  28. package/build/src/database/datetime-storage.d.ts.map +1 -1
  29. package/build/src/database/datetime-storage.js +48 -6
  30. package/build/src/database/record/index.d.ts +5 -0
  31. package/build/src/database/record/index.d.ts.map +1 -1
  32. package/build/src/database/record/index.js +13 -5
  33. package/build/src/environment-handlers/base.d.ts +19 -0
  34. package/build/src/environment-handlers/base.d.ts.map +1 -1
  35. package/build/src/environment-handlers/base.js +41 -1
  36. package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
  37. package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +4 -2
  38. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +219 -40
  39. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
  40. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +317 -43
  41. package/build/src/environment-handlers/node/cli/commands/generate/generated-file-banner.d.ts +9 -0
  42. package/build/src/environment-handlers/node/cli/commands/generate/generated-file-banner.d.ts.map +1 -0
  43. package/build/src/environment-handlers/node/cli/commands/generate/generated-file-banner.js +13 -0
  44. package/build/src/environment-handlers/node.d.ts +1 -0
  45. package/build/src/environment-handlers/node.d.ts.map +1 -1
  46. package/build/src/environment-handlers/node.js +76 -9
  47. package/build/src/frontend-model-controller.js +8 -8
  48. package/build/src/frontend-models/base.d.ts +4 -0
  49. package/build/src/frontend-models/base.d.ts.map +1 -1
  50. package/build/src/frontend-models/base.js +64 -9
  51. package/build/src/frontend-models/transport-serialization.d.ts +30 -1
  52. package/build/src/frontend-models/transport-serialization.d.ts.map +1 -1
  53. package/build/src/frontend-models/transport-serialization.js +34 -9
  54. package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
  55. package/build/src/frontend-models/websocket-channel.js +16 -2
  56. package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
  57. package/build/src/frontend-models/websocket-publishers.js +12 -2
  58. package/build/src/http-server/client/request-runner.d.ts.map +1 -1
  59. package/build/src/http-server/client/request-runner.js +13 -2
  60. package/build/src/time-zone.d.ts +16 -0
  61. package/build/src/time-zone.d.ts.map +1 -0
  62. package/build/src/time-zone.js +56 -0
  63. package/build/time-zone.js +65 -0
  64. package/build/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +2 -1
  66. package/scripts/test-browser.js +1 -0
  67. package/src/configuration-types.js +1 -0
  68. package/src/configuration.js +27 -1
  69. package/src/controller.js +13 -1
  70. package/src/database/datetime-storage.js +49 -5
  71. package/src/database/record/index.js +14 -4
  72. package/src/environment-handlers/base.js +46 -0
  73. package/src/environment-handlers/node/cli/commands/generate/base-models.js +4 -1
  74. package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +368 -43
  75. package/src/environment-handlers/node/cli/commands/generate/generated-file-banner.js +15 -0
  76. package/src/environment-handlers/node.js +84 -8
  77. package/src/frontend-model-controller.js +7 -7
  78. package/src/frontend-models/base.js +75 -9
  79. package/src/frontend-models/transport-serialization.js +41 -8
  80. package/src/frontend-models/websocket-channel.js +18 -1
  81. package/src/frontend-models/websocket-publishers.js +12 -1
  82. package/src/http-server/client/request-runner.js +13 -1
  83. package/src/time-zone.js +65 -0
@@ -22,6 +22,7 @@ import {requestDetails} from "./error-reporting/request-details.js"
22
22
  import {frontendModelResourceClassFromDefinition, frontendModelResourceConfigurationFromDefinition, frontendModelResourcesForBackendProject} from "./frontend-models/resource-definition.js"
23
23
  import PluginRoutes from "./routes/plugin-routes.js"
24
24
  import restArgsError from "./utils/rest-args-error.js"
25
+ import {validateTimeZone} from "./time-zone.js"
25
26
  import {withTrackedStack} from "./utils/with-tracked-stack.js"
26
27
 
27
28
  export {CurrentConfigurationNotSetError}
@@ -125,7 +126,7 @@ export default class VelociousConfiguration {
125
126
  * Runs constructor.
126
127
  * @param {import("./configuration-types.js").ConfigurationArgsType} args - Configuration arguments.
127
128
  */
128
- constructor({abilityResolver, abilityResources, attachments, autoload = true, backgroundJobs, backendProjects, beacon, cookieSecret, cors, database, debug = false, debugEndpoint = false, directory, enforceTenantDatabaseScopes = true, environment, environmentHandler, exposeInternalErrorsToClients = false, httpServer, initializeModels, initializers, locale, localeFallbacks, locales, logging, mailerBackend, requestTimeoutMs, routeResolverHooks, scheduledBackgroundJobs, structureSql, tenantDatabaseProviders, tenantDatabaseResolver, tenantResolver, testing, timezoneOffsetMinutes, trustedProxies, websocketChannelResolver, websocketMessageHandlerResolver, ...restArgs}) {
129
+ constructor({abilityResolver, abilityResources, attachments, autoload = true, backgroundJobs, backendProjects, beacon, cookieSecret, cors, database, debug = false, debugEndpoint = false, directory, enforceTenantDatabaseScopes = true, environment, environmentHandler, exposeInternalErrorsToClients = false, httpServer, initializeModels, initializers, locale, localeFallbacks, locales, logging, mailerBackend, requestTimeoutMs, routeResolverHooks, scheduledBackgroundJobs, structureSql, tenantDatabaseProviders, tenantDatabaseResolver, tenantResolver, testing, timeZone, timezoneOffsetMinutes, trustedProxies, websocketChannelResolver, websocketMessageHandlerResolver, ...restArgs}) {
129
130
  restArgsError(restArgs)
130
131
 
131
132
  this._abilityResolver = abilityResolver
@@ -183,6 +184,7 @@ export default class VelociousConfiguration {
183
184
  this.locales = locales
184
185
  this._initializers = initializers
185
186
  this._testing = testing
187
+ this._timeZone = timeZone
186
188
  this._timezoneOffsetMinutes = timezoneOffsetMinutes
187
189
  this._trustedProxies = trustedProxies
188
190
  this._requestTimeoutMs = requestTimeoutMs
@@ -1848,6 +1850,20 @@ export default class VelociousConfiguration {
1848
1850
  return new Date().getTimezoneOffset()
1849
1851
  }
1850
1852
 
1853
+ /**
1854
+ * Runs get time zone.
1855
+ * @returns {string | undefined} - Configured timezone identifier.
1856
+ */
1857
+ getTimeZone() {
1858
+ const timeZone = typeof this._timeZone === "function"
1859
+ ? this._timeZone()
1860
+ : this._timeZone
1861
+
1862
+ if (timeZone === undefined || timeZone === null) return undefined
1863
+
1864
+ return validateTimeZone(timeZone, "configuration timeZone")
1865
+ }
1866
+
1851
1867
  /**
1852
1868
  * Runs get websocket events.
1853
1869
  * @returns {import("./http-server/websocket-events.js").default | undefined} - The websocket events.
@@ -2330,6 +2346,16 @@ export default class VelociousConfiguration {
2330
2346
  return await this.getEnvironmentHandler().runWithRequestTiming(requestTiming, callback)
2331
2347
  }
2332
2348
 
2349
+ /**
2350
+ * Runs run with timezone.
2351
+ * @param {string} timeZone - IANA timezone identifier.
2352
+ * @param {() => Promise<?>} callback - Callback.
2353
+ * @returns {Promise<?>} - Callback result.
2354
+ */
2355
+ async runWithTimezone(timeZone, callback) {
2356
+ return await this.getEnvironmentHandler().runWithTimezone(timeZone, callback)
2357
+ }
2358
+
2333
2359
  /**
2334
2360
  * Runs get current ability.
2335
2361
  * @returns {import("./authorization/ability.js").default | undefined} - Current ability from context.
package/src/controller.js CHANGED
@@ -83,6 +83,18 @@ export default class VelociousController {
83
83
  */
84
84
  getRequest() { return this._request }
85
85
 
86
+ /**
87
+ * Runs transport serialization options.
88
+ * @returns {import("./frontend-models/transport-serialization.js").FrontendModelTransportSerializationOptions} - Serialization options.
89
+ */
90
+ transportSerializationOptions() {
91
+ const configuration = this.getConfiguration()
92
+
93
+ return {
94
+ timeZone: configuration.getEnvironmentHandler().getTimeZone(configuration)
95
+ }
96
+ }
97
+
86
98
  /**
87
99
  * Runs set cookie.
88
100
  * @param {string} name - Cookie name.
@@ -257,7 +269,7 @@ export default class VelociousController {
257
269
  */
258
270
  renderJsonArg(json) {
259
271
  return this._measureViewRender(() => {
260
- const body = JSON.stringify(serializeFrontendModelTransportValue(json))
272
+ const body = JSON.stringify(serializeFrontendModelTransportValue(json, this.transportSerializationOptions()))
261
273
 
262
274
  this._response.setHeader("Content-Type", "application/json; charset=UTF-8")
263
275
  this._response.setBody(body)
@@ -1,9 +1,12 @@
1
1
  // @ts-check
2
2
 
3
3
  import isDate from "../utils/is-date.js"
4
+ import {Temporal} from "@js-temporal/polyfill"
5
+ import {validateTimeZone} from "../time-zone.js"
4
6
 
5
7
  const dateTimeWithTimezonePattern = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:[zZ]|[+-]\d{2}:\d{2})$/
6
8
  const dateTimeWithoutTimezonePattern = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?$/
9
+ const dateTimeWithoutTimezonePartsPattern = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?$/
7
10
  const timezoneSuffixPattern = /(?:[zZ]|[+-]\d{2}:\d{2})$/
8
11
 
9
12
  /**
@@ -85,6 +88,42 @@ function parseTimezoneLessDateTimeStringWithOffset(value, legacyLocalOffsetMinut
85
88
  return new Date(utcDate.getTime() + (legacyLocalOffsetMinutes * 60 * 1000))
86
89
  }
87
90
 
91
+ /**
92
+ * Parses a timezone-less datetime string in a named timezone.
93
+ * @param {string} value - Datetime string.
94
+ * @param {string} timeZone - IANA timezone identifier.
95
+ * @returns {Date | string} - Parsed date or the original string when it is not a recognized datetime.
96
+ */
97
+ function parseTimezoneLessDateTimeStringWithTimeZone(value, timeZone) {
98
+ const match = value.match(dateTimeWithoutTimezonePartsPattern)
99
+
100
+ if (!match) return value
101
+
102
+ const normalizedTimeZone = validateTimeZone(timeZone, "timeZone")
103
+ const fraction = (match[7] || "").padEnd(9, "0")
104
+
105
+ try {
106
+ const zonedDateTime = Temporal.ZonedDateTime.from({
107
+ day: Number(match[3]),
108
+ hour: Number(match[4]),
109
+ microsecond: Number(fraction.slice(3, 6)),
110
+ millisecond: Number(fraction.slice(0, 3)),
111
+ minute: Number(match[5]),
112
+ month: Number(match[2]),
113
+ nanosecond: Number(fraction.slice(6, 9)),
114
+ second: Number(match[6]),
115
+ timeZone: normalizedTimeZone,
116
+ year: Number(match[1])
117
+ })
118
+
119
+ return new Date(Number(zonedDateTime.epochMilliseconds))
120
+ } catch (error) {
121
+ if (error instanceof RangeError) return value
122
+
123
+ throw error
124
+ }
125
+ }
126
+
88
127
  /**
89
128
  * Checks whether a string has a datetime timezone suffix.
90
129
  * @param {string} value - Value to check.
@@ -123,26 +162,31 @@ export function formatDateForDatabase(value, {databaseType}) {
123
162
 
124
163
  /**
125
164
  * Normalizes a record write string into a Date when it is a recognized datetime string.
126
- * Timezone-less strings are external input and are treated as UTC.
165
+ * Timezone-less strings are interpreted in the given timezone when present, otherwise UTC.
127
166
  * @param {string} value - Value to normalize.
167
+ * @param {object} [options] - Parse options.
168
+ * @param {string | undefined} [options.timeZone] - Timezone for timezone-less strings.
128
169
  * @returns {Date | string} - Normalized value.
129
170
  */
130
- export function normalizeDateStringForWrite(value) {
171
+ export function normalizeDateStringForWrite(value, {timeZone} = {}) {
131
172
  if (hasDateTimeTimezone(value)) return parseTimezoneQualifiedDateTimeString(value)
173
+ if (timeZone !== undefined) return parseTimezoneLessDateTimeStringWithTimeZone(value, timeZone)
132
174
 
133
175
  return parseTimezoneLessDateTimeStringAsUtc(value)
134
176
  }
135
177
 
136
178
  /**
137
179
  * Normalizes a record write value into a Date when it is a recognized datetime string.
138
- * Timezone-less strings are external input and are treated as UTC.
180
+ * Timezone-less strings are interpreted in the given timezone when present, otherwise UTC.
139
181
  * @param {Date | string | null | undefined} value - Value to normalize.
182
+ * @param {object} [options] - Parse options.
183
+ * @param {string | undefined} [options.timeZone] - Timezone for timezone-less strings.
140
184
  * @returns {Date | string | null | undefined} - Normalized value.
141
185
  */
142
- export function normalizeDateValueForWrite(value) {
186
+ export function normalizeDateValueForWrite(value, {timeZone} = {}) {
143
187
  if (typeof value != "string") return value
144
188
 
145
- return normalizeDateStringForWrite(value)
189
+ return normalizeDateStringForWrite(value, {timeZone})
146
190
  }
147
191
 
148
192
  /**
@@ -1800,7 +1800,7 @@ class VelociousDatabaseRecord {
1800
1800
  * @returns {?} - The date value.
1801
1801
  */
1802
1802
  _normalizeDateValue(value) {
1803
- return normalizeDateValueForWrite(value)
1803
+ return normalizeDateValueForWrite(value, {timeZone: this.getModelClass()._timeZoneForDateWrite()})
1804
1804
  }
1805
1805
 
1806
1806
  /**
@@ -2164,7 +2164,7 @@ class VelociousDatabaseRecord {
2164
2164
  * @returns {?} - Normalized value.
2165
2165
  */
2166
2166
  static _normalizeDateValueForInsert(value) {
2167
- return normalizeDateValueForWrite(value)
2167
+ return normalizeDateValueForWrite(value, {timeZone: this._timeZoneForDateWrite()})
2168
2168
  }
2169
2169
 
2170
2170
  /**
@@ -2173,7 +2173,17 @@ class VelociousDatabaseRecord {
2173
2173
  * @returns {string | Date} - Parsed date or original string.
2174
2174
  */
2175
2175
  static _normalizeDateStringForInsert(value) {
2176
- return normalizeDateStringForWrite(value)
2176
+ return normalizeDateStringForWrite(value, {timeZone: this._timeZoneForDateWrite()})
2177
+ }
2178
+
2179
+ /**
2180
+ * Runs time zone for date writes.
2181
+ * @returns {string | undefined} - Active timezone identifier.
2182
+ */
2183
+ static _timeZoneForDateWrite() {
2184
+ const configuration = this._getConfiguration()
2185
+
2186
+ return configuration.getEnvironmentHandler().getTimeZone(configuration)
2177
2187
  }
2178
2188
 
2179
2189
  /**
@@ -3923,7 +3933,7 @@ class VelociousDatabaseRecord {
3923
3933
 
3924
3934
  const value = data[columnName]
3925
3935
 
3926
- data[columnName] = normalizeDateValueForWrite(value)
3936
+ data[columnName] = normalizeDateValueForWrite(value, {timeZone: this.getModelClass()._timeZoneForDateWrite()})
3927
3937
  }
3928
3938
  }
3929
3939
 
@@ -1,5 +1,7 @@
1
1
  // @ts-check
2
2
 
3
+ import {validateTimeZone} from "../time-zone.js"
4
+
3
5
  /**
4
6
  * CommandFileObjectType type.
5
7
  * @typedef {object} CommandFileObjectType
@@ -69,6 +71,26 @@ export default class VelociousEnvironmentHandlerBase {
69
71
  }
70
72
  }
71
73
 
74
+ /**
75
+ * Runs run with timezone.
76
+ * @param {string} timeZone - IANA timezone identifier.
77
+ * @param {() => Promise<?>} callback - Callback to run.
78
+ * @returns {Promise<?>} - Result of the callback.
79
+ */
80
+ async runWithTimezone(timeZone, callback) {
81
+ if (!this.configuration) throw new Error("Configuration hasn't been set")
82
+
83
+ const previousTimeZone = this.configuration._timeZone
84
+
85
+ this.configuration._timeZone = validateTimeZone(timeZone, "timeZone")
86
+
87
+ try {
88
+ return await callback()
89
+ } finally {
90
+ this.configuration._timeZone = previousTimeZone
91
+ }
92
+ }
93
+
72
94
  /**
73
95
  * Runs set timezone offset.
74
96
  * @param {number} _offsetMinutes - Offset in minutes (Date#getTimezoneOffset).
@@ -83,6 +105,17 @@ export default class VelociousEnvironmentHandlerBase {
83
105
  this.configuration._timezoneOffsetMinutes = _offsetMinutes
84
106
  }
85
107
 
108
+ /**
109
+ * Runs set timezone.
110
+ * @param {string} timeZone - IANA timezone identifier.
111
+ * @returns {void} - No return value.
112
+ */
113
+ setTimezone(timeZone) {
114
+ if (!this.configuration) throw new Error("Configuration hasn't been set")
115
+
116
+ this.configuration._timeZone = validateTimeZone(timeZone, "timeZone")
117
+ }
118
+
86
119
  /**
87
120
  * Runs get timezone offset minutes.
88
121
  * @param {import("../configuration.js").default | undefined} configuration - Configuration instance.
@@ -100,6 +133,19 @@ export default class VelociousEnvironmentHandlerBase {
100
133
  return /** @type {number} */ (activeConfiguration.getTimezoneOffsetMinutes())
101
134
  }
102
135
 
136
+ /**
137
+ * Runs get timezone.
138
+ * @param {import("../configuration.js").default | undefined} configuration - Configuration instance.
139
+ * @returns {string | undefined} - Timezone identifier.
140
+ */
141
+ getTimeZone(configuration) {
142
+ const activeConfiguration = configuration || this.configuration
143
+
144
+ if (!activeConfiguration) throw new Error("Configuration hasn't been set")
145
+
146
+ return activeConfiguration.getTimeZone()
147
+ }
148
+
103
149
  /**
104
150
  * Runs run with request timing.
105
151
  * @param {import("../http-server/client/request-timing.js").default | undefined} requestTiming - Request timing collector.
@@ -2,8 +2,11 @@ import BaseCommand from "../../../../../cli/base-command.js"
2
2
  import deburrColumnName from "../../../../../utils/deburr-column-name.js"
3
3
  import fileExists from "../../../../../utils/file-exists.js"
4
4
  import fs from "fs/promises"
5
+ import generatedFileBanner from "./generated-file-banner.js"
5
6
  import * as inflection from "inflection"
6
7
 
8
+ const BASE_MODELS_REGENERATE_COMMAND = "velocious generate:base-models"
9
+
7
10
  /**
8
11
  * Maps an effective column type to the JSDoc type used in generated base models.
9
12
  * @type {Record<string, string>}
@@ -106,7 +109,7 @@ export default class DbGenerateModel extends BaseCommand {
106
109
  sourceModelFilePath = "velocious/build/src/database/record/index.js"
107
110
  }
108
111
 
109
- let fileContent = ""
112
+ let fileContent = generatedFileBanner(BASE_MODELS_REGENERATE_COMMAND)
110
113
  let velociousPath
111
114
 
112
115
  if (devMode) {