velocious 1.0.487 → 1.0.489

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 (37) hide show
  1. package/README.md +2 -0
  2. package/build/configuration-types.js +1 -0
  3. package/build/configuration.js +29 -0
  4. package/build/environment-handlers/node.js +22 -6
  5. package/build/src/configuration-types.d.ts +5 -0
  6. package/build/src/configuration-types.d.ts.map +1 -1
  7. package/build/src/configuration-types.js +2 -1
  8. package/build/src/configuration.d.ts +11 -0
  9. package/build/src/configuration.d.ts.map +1 -1
  10. package/build/src/configuration.js +27 -1
  11. package/build/src/environment-handlers/node.d.ts.map +1 -1
  12. package/build/src/environment-handlers/node.js +20 -8
  13. package/build/src/sync/sync-api-client-types.d.ts +182 -0
  14. package/build/src/sync/sync-api-client-types.d.ts.map +1 -0
  15. package/build/src/sync/sync-api-client-types.js +3 -0
  16. package/build/src/sync/sync-api-client.d.ts +188 -0
  17. package/build/src/sync/sync-api-client.d.ts.map +1 -0
  18. package/build/src/sync/sync-api-client.js +307 -0
  19. package/build/src/sync/sync-api-controller.d.ts +78 -0
  20. package/build/src/sync/sync-api-controller.d.ts.map +1 -0
  21. package/build/src/sync/sync-api-controller.js +142 -0
  22. package/build/src/sync/sync-model-change-feed-service.d.ts +131 -0
  23. package/build/src/sync/sync-model-change-feed-service.d.ts.map +1 -0
  24. package/build/src/sync/sync-model-change-feed-service.js +212 -0
  25. package/build/sync/sync-api-client-types.js +77 -0
  26. package/build/sync/sync-api-client.js +342 -0
  27. package/build/sync/sync-api-controller.js +161 -0
  28. package/build/sync/sync-model-change-feed-service.js +244 -0
  29. package/build/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +4 -4
  31. package/src/configuration-types.js +1 -0
  32. package/src/configuration.js +29 -0
  33. package/src/environment-handlers/node.js +22 -6
  34. package/src/sync/sync-api-client-types.js +77 -0
  35. package/src/sync/sync-api-client.js +342 -0
  36. package/src/sync/sync-api-controller.js +161 -0
  37. package/src/sync/sync-model-change-feed-service.js +244 -0
@@ -0,0 +1,244 @@
1
+ // @ts-check
2
+
3
+ import VelociousError from "../velocious-error.js"
4
+
5
+ /**
6
+ * Generic cursor-paginated change feed over an app-owned sync/change model.
7
+ *
8
+ * Apps provide the model and optional scoping/serialization hooks. Velocious owns
9
+ * cursor parsing, snapshot high-water resolution, stable ordering, page limits,
10
+ * and response shape for `/velocious/sync/changes` style endpoints.
11
+ */
12
+ export default class SyncModelChangeFeedService {
13
+ /**
14
+ * Creates a generic sync-model change-feed service.
15
+ * @param {object} args - Service arguments.
16
+ * @param {typeof import("../database/record/index.js").default} args.modelClass - Sync/change model class.
17
+ * @param {Record<string, unknown>} args.params - Request params.
18
+ * @param {number} [args.defaultLimit] - Default page size.
19
+ * @param {number} [args.maxLimit] - Maximum page size.
20
+ * @param {(record: ?) => Record<string, unknown>} [args.serializeRecord] - Record serializer.
21
+ * @param {function({query: import("../database/query/model-class-query.js").default}): void} [args.scopeQuery] - Applies app-owned visibility scope.
22
+ */
23
+ constructor({defaultLimit = 1000, maxLimit = 1000, modelClass, params, scopeQuery, serializeRecord}) {
24
+ this.defaultLimit = defaultLimit
25
+ this.maxLimit = maxLimit
26
+ this.modelClass = modelClass
27
+ this.params = params
28
+ this.scopeQuery = scopeQuery || null
29
+ this.serializeRecord = serializeRecord || ((record) => this.defaultSerializeRecord(record))
30
+ }
31
+
32
+ /**
33
+ * Builds a stable change-feed page.
34
+ * @returns {Promise<{status: string, nextCursor: {id: string, serverSequence: number, updatedAt: string} | null, syncs: Array<Record<string, unknown>>, upToCursor: {id: string, serverSequence: number, updatedAt: string} | null}>} Change-feed page result.
35
+ */
36
+ async changes() {
37
+ const limit = this.normalizedLimit(this.params.limit)
38
+ const upToCursor = await this.resolveUpToCursor()
39
+
40
+ if (!upToCursor) return {status: "success", nextCursor: null, syncs: [], upToCursor: null}
41
+
42
+ const query = this.pageQuery({limit, upToCursor})
43
+ const records = await query.toArray()
44
+ const nextCursor = records.length > 0 ? this.cursorForRecord(records[records.length - 1]) : upToCursor
45
+
46
+ return {
47
+ status: "success",
48
+ nextCursor,
49
+ syncs: records.map((record) => this.serializeRecord(record)),
50
+ upToCursor
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Normalizes the requested page limit.
56
+ * @param {unknown} value - Raw limit param.
57
+ * @returns {number} Normalized page limit.
58
+ */
59
+ normalizedLimit(value) {
60
+ if (value === undefined || value === null || value === "") return this.defaultLimit
61
+
62
+ const limit = Number(value)
63
+
64
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
65
+ throw VelociousError.safe("Sync changes limit must be a positive integer.", {code: "sync-invalid-changes-limit"})
66
+ }
67
+
68
+ return Math.min(limit, this.maxLimit)
69
+ }
70
+
71
+ /**
72
+ * Parses an optional positive integer cursor field.
73
+ * @param {unknown} value - Raw integer param.
74
+ * @param {string} name - Param name for error messages.
75
+ * @returns {number | null} Positive integer value, or null when omitted.
76
+ */
77
+ optionalPositiveIntegerParam(value, name) {
78
+ if (value === undefined || value === null || value === "") return null
79
+
80
+ const integer = Number(value)
81
+
82
+ if (!Number.isSafeInteger(integer) || integer <= 0) {
83
+ throw VelociousError.safe(`${name} must be a positive integer.`, {code: "sync-invalid-changes-cursor"})
84
+ }
85
+
86
+ return integer
87
+ }
88
+
89
+ /**
90
+ * Resolves the high-water cursor that bounds the current feed page.
91
+ * @returns {Promise<{id: string, serverSequence: number, updatedAt: string} | null>} Snapshot upper-bound cursor.
92
+ */
93
+ async resolveUpToCursor() {
94
+ const upToServerSequence = this.optionalPositiveIntegerParam(this.params.upToServerSequence, "upToServerSequence")
95
+
96
+ if (upToServerSequence !== null && typeof this.params.upToUpdatedAt === "string" && typeof this.params.upToId === "string") {
97
+ return {id: this.params.upToId, serverSequence: upToServerSequence, updatedAt: this.params.upToUpdatedAt}
98
+ }
99
+
100
+ if (typeof this.params.upToUpdatedAt === "string" && typeof this.params.upToId === "string") {
101
+ const upToRecord = await this.modelClass.findBy({id: this.params.upToId})
102
+
103
+ if (upToRecord) return this.cursorForRecord(upToRecord)
104
+ }
105
+
106
+ const query = this.scopedQuery()
107
+ const table = query.driver.quoteTable(this.modelClass.tableName())
108
+ const serverSequenceColumn = query.driver.quoteColumn("server_sequence")
109
+ const latestRecords = await query
110
+ .order(`${table}.${serverSequenceColumn} DESC`)
111
+ .limit(1)
112
+ .toArray()
113
+
114
+ if (latestRecords.length === 0) return null
115
+
116
+ return this.cursorForRecord(latestRecords[0])
117
+ }
118
+
119
+ /**
120
+ * Builds the ordered and cursor-filtered page query.
121
+ * @param {{limit: number, upToCursor: {id: string, serverSequence: number, updatedAt: string}}} args - Page query args.
122
+ * @returns {import("../database/query/model-class-query.js").default} Page query.
123
+ */
124
+ pageQuery({limit, upToCursor}) {
125
+ const query = this.scopedQuery()
126
+ const driver = query.driver
127
+ const table = driver.quoteTable(this.modelClass.tableName())
128
+ const serverSequenceColumn = `${table}.${driver.quoteColumn("server_sequence")}`
129
+ const updatedAtColumn = `${table}.${driver.quoteColumn("updated_at")}`
130
+ const idColumn = `${table}.${driver.quoteColumn("id")}`
131
+
132
+ query
133
+ .where(`${serverSequenceColumn} <= ${driver.quote(upToCursor.serverSequence)}`)
134
+ .order(`${serverSequenceColumn} ASC`)
135
+ .limit(limit)
136
+
137
+ const afterServerSequence = this.optionalPositiveIntegerParam(this.params.afterServerSequence, "afterServerSequence")
138
+
139
+ if (afterServerSequence !== null) {
140
+ query.where(`${serverSequenceColumn} > ${driver.quote(afterServerSequence)}`)
141
+ } else if (typeof this.params.afterUpdatedAt === "string" && this.params.afterUpdatedAt !== "") {
142
+ const isPagingExistingSnapshot = typeof this.params.upToUpdatedAt === "string" && this.params.upToUpdatedAt !== "" && typeof this.params.upToId === "string" && this.params.upToId !== ""
143
+
144
+ if (isPagingExistingSnapshot && typeof this.params.afterId === "string" && this.params.afterId !== "") {
145
+ query.where(`(${updatedAtColumn} > ${driver.quote(this.params.afterUpdatedAt)} OR (${updatedAtColumn} = ${driver.quote(this.params.afterUpdatedAt)} AND ${idColumn} > ${driver.quote(this.params.afterId)}))`)
146
+ } else {
147
+ query.where(`${updatedAtColumn} >= ${driver.quote(this.params.afterUpdatedAt)}`)
148
+ }
149
+ }
150
+
151
+ return query
152
+ }
153
+
154
+ /**
155
+ * Builds a base query with app-owned scope applied.
156
+ * @returns {import("../database/query/model-class-query.js").default} Scoped base query.
157
+ */
158
+ scopedQuery() {
159
+ const query = this.modelClass.where({})
160
+
161
+ if (this.scopeQuery) this.scopeQuery({query})
162
+
163
+ return query
164
+ }
165
+
166
+ /**
167
+ * Serializes a record into a transport cursor.
168
+ * @param {?} record - Sync/change record.
169
+ * @returns {{id: string, serverSequence: number, updatedAt: string}} Cursor for row.
170
+ */
171
+ cursorForRecord(record) {
172
+ return {id: String(this.recordValue(record, "id")), serverSequence: Number(this.recordValue(record, "serverSequence")), updatedAt: this.isoDate(this.recordValue(record, "updatedAt"))}
173
+ }
174
+
175
+ /**
176
+ * Serializes a record using the standard sync envelope shape.
177
+ * @param {?} record - Sync/change record.
178
+ * @returns {Record<string, unknown>} Default serialized row.
179
+ */
180
+ defaultSerializeRecord(record) {
181
+ return {
182
+ data: this.recordData(record),
183
+ eventId: this.recordValue(record, "eventId"),
184
+ id: this.recordValue(record, "id"),
185
+ resourceId: this.recordValue(record, "resourceId"),
186
+ resourceType: this.recordValue(record, "resourceType"),
187
+ serverSequence: this.recordValue(record, "serverSequence"),
188
+ syncType: this.recordValue(record, "syncType"),
189
+ updatedAt: this.isoDate(this.recordValue(record, "updatedAt"))
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Reads and parses the record data payload.
195
+ * @param {?} record - Sync/change record.
196
+ * @returns {unknown} Parsed data value.
197
+ */
198
+ recordData(record) {
199
+ const data = this.recordValue(record, "data")
200
+
201
+ if (data === "" || data === null || data === undefined) return null
202
+ if (typeof data !== "string") return data
203
+
204
+ return JSON.parse(data)
205
+ }
206
+
207
+ /**
208
+ * Reads a value from either a record accessor method or plain property.
209
+ * @param {?} record - Sync/change record.
210
+ * @param {string} name - Camel-cased value/method name.
211
+ * @returns {unknown} Record value.
212
+ */
213
+ recordValue(record, name) {
214
+ if (!record || typeof record !== "object") {
215
+ throw new Error("Sync changes row must be an object.")
216
+ }
217
+
218
+ const recordObject = /** @type {Record<string, ?>} */ (record)
219
+ const method = recordObject[name]
220
+ const value = typeof method === "function" ? method.call(record) : method
221
+
222
+ if (value === undefined) {
223
+ throw new Error(`Sync changes row is missing ${name}.`)
224
+ }
225
+
226
+ return value
227
+ }
228
+
229
+ /**
230
+ * Converts a date-like value to an ISO string.
231
+ * @param {Date | string | null | undefined | unknown} value - Date value.
232
+ * @returns {string} ISO date.
233
+ */
234
+ isoDate(value) {
235
+ const date = value instanceof Date ? value : new Date(typeof value === "string" || typeof value === "number" ? value : 0)
236
+
237
+ if (Number.isNaN(date.getTime())) {
238
+ throw VelociousError.safe("Sync changes row has an invalid updated_at value.", {code: "sync-invalid-changes-row"})
239
+ }
240
+
241
+ return date.toISOString()
242
+ }
243
+ }
244
+
@@ -1 +1 @@
1
- {"root":["../index.js","../bin/velocious.js","../src/application.js","../src/configuration-resolver.js","../src/configuration-types.js","../src/configuration.js","../src/controller.js","../src/current-configuration.js","../src/current.js","../src/error-logger.js","../src/frontend-model-controller.js","../src/initializer.js","../src/logger.js","../src/mailer.js","../src/record-payload-values.js","../src/time-zone.js","../src/velocious-error.js","../src/authorization/ability.js","../src/authorization/base-resource.js","../src/background-jobs/client.js","../src/background-jobs/cron-expression.js","../src/background-jobs/forked-runner-child.js","../src/background-jobs/job-record.js","../src/background-jobs/job-registry.js","../src/background-jobs/job-runner.js","../src/background-jobs/job.js","../src/background-jobs/json-socket.js","../src/background-jobs/main.js","../src/background-jobs/normalize-error.js","../src/background-jobs/scheduler.js","../src/background-jobs/socket-request.js","../src/background-jobs/status-reporter.js","../src/background-jobs/store.js","../src/background-jobs/types.js","../src/background-jobs/worker.js","../src/background-jobs/web/authorization.js","../src/background-jobs/web/controller.js","../src/background-jobs/web/index.js","../src/background-jobs/web/path-matcher.js","../src/background-jobs/web/registry.js","../src/beacon/client.js","../src/beacon/in-process-broker.js","../src/beacon/in-process-client.js","../src/beacon/server.js","../src/beacon/types.js","../src/cli/base-command.js","../src/cli/browser-cli.js","../src/cli/index.js","../src/cli/tenant-database-command-helper.js","../src/cli/use-browser-cli.js","../src/cli/commands/background-jobs-main.js","../src/cli/commands/background-jobs-runner.js","../src/cli/commands/background-jobs-worker.js","../src/cli/commands/beacon.js","../src/cli/commands/console.js","../src/cli/commands/init.js","../src/cli/commands/routes.js","../src/cli/commands/run-script.js","../src/cli/commands/runner.js","../src/cli/commands/server.js","../src/cli/commands/test.js","../src/cli/commands/db/base-command.js","../src/cli/commands/db/create.js","../src/cli/commands/db/drop.js","../src/cli/commands/db/migrate.js","../src/cli/commands/db/reset.js","../src/cli/commands/db/rollback.js","../src/cli/commands/db/seed.js","../src/cli/commands/db/schema/dump.js","../src/cli/commands/db/schema/load.js","../src/cli/commands/db/tenants/check.js","../src/cli/commands/db/tenants/create.js","../src/cli/commands/db/tenants/drop.js","../src/cli/commands/db/tenants/migrate.js","../src/cli/commands/destroy/migration.js","../src/cli/commands/generate/base-models.js","../src/cli/commands/generate/frontend-models.js","../src/cli/commands/generate/migration.js","../src/cli/commands/generate/model.js","../src/cli/commands/lint/relationships.js","../src/database/annotations-async-hooks.js","../src/database/annotations.js","../src/database/datetime-storage.js","../src/database/handler.js","../src/database/initializer-from-require-context.js","../src/database/migrations-ledger.js","../src/database/migrator.js","../src/database/use-database.js","../src/database/drivers/base-column.js","../src/database/drivers/base-columns-index.js","../src/database/drivers/base-foreign-key.js","../src/database/drivers/base-table.js","../src/database/drivers/base.js","../src/database/drivers/mssql/column.js","../src/database/drivers/mssql/columns-index.js","../src/database/drivers/mssql/connect-connection.js","../src/database/drivers/mssql/foreign-key.js","../src/database/drivers/mssql/index.js","../src/database/drivers/mssql/options.js","../src/database/drivers/mssql/query-parser.js","../src/database/drivers/mssql/structure-sql.js","../src/database/drivers/mssql/table.js","../src/database/drivers/mssql/sql/alter-table.js","../src/database/drivers/mssql/sql/create-database.js","../src/database/drivers/mssql/sql/create-index.js","../src/database/drivers/mssql/sql/create-table.js","../src/database/drivers/mssql/sql/delete.js","../src/database/drivers/mssql/sql/drop-database.js","../src/database/drivers/mssql/sql/drop-table.js","../src/database/drivers/mssql/sql/insert.js","../src/database/drivers/mssql/sql/remove-index.js","../src/database/drivers/mssql/sql/update.js","../src/database/drivers/mssql/sql/upsert.js","../src/database/drivers/mysql/column.js","../src/database/drivers/mysql/columns-index.js","../src/database/drivers/mysql/foreign-key.js","../src/database/drivers/mysql/index.js","../src/database/drivers/mysql/options.js","../src/database/drivers/mysql/query-parser.js","../src/database/drivers/mysql/query.js","../src/database/drivers/mysql/structure-sql.js","../src/database/drivers/mysql/table.js","../src/database/drivers/mysql/sql/alter-table.js","../src/database/drivers/mysql/sql/create-database.js","../src/database/drivers/mysql/sql/create-index.js","../src/database/drivers/mysql/sql/create-table.js","../src/database/drivers/mysql/sql/delete.js","../src/database/drivers/mysql/sql/drop-database.js","../src/database/drivers/mysql/sql/drop-table.js","../src/database/drivers/mysql/sql/insert.js","../src/database/drivers/mysql/sql/remove-index.js","../src/database/drivers/mysql/sql/update.js","../src/database/drivers/mysql/sql/upsert.js","../src/database/drivers/pgsql/column.js","../src/database/drivers/pgsql/columns-index.js","../src/database/drivers/pgsql/foreign-key.js","../src/database/drivers/pgsql/index.js","../src/database/drivers/pgsql/options.js","../src/database/drivers/pgsql/query-parser.js","../src/database/drivers/pgsql/structure-sql.js","../src/database/drivers/pgsql/table.js","../src/database/drivers/pgsql/sql/alter-table.js","../src/database/drivers/pgsql/sql/create-database.js","../src/database/drivers/pgsql/sql/create-index.js","../src/database/drivers/pgsql/sql/create-table.js","../src/database/drivers/pgsql/sql/delete.js","../src/database/drivers/pgsql/sql/drop-database.js","../src/database/drivers/pgsql/sql/drop-table.js","../src/database/drivers/pgsql/sql/insert.js","../src/database/drivers/pgsql/sql/remove-index.js","../src/database/drivers/pgsql/sql/update.js","../src/database/drivers/pgsql/sql/upsert.js","../src/database/drivers/sqlite/base.js","../src/database/drivers/sqlite/column.js","../src/database/drivers/sqlite/columns-index.js","../src/database/drivers/sqlite/connection-sql-js.js","../src/database/drivers/sqlite/foreign-key.js","../src/database/drivers/sqlite/index.js","../src/database/drivers/sqlite/index.native.js","../src/database/drivers/sqlite/index.web.js","../src/database/drivers/sqlite/options.js","../src/database/drivers/sqlite/query-parser.js","../src/database/drivers/sqlite/query.js","../src/database/drivers/sqlite/query.native.js","../src/database/drivers/sqlite/query.web.js","../src/database/drivers/sqlite/structure-sql.js","../src/database/drivers/sqlite/table-rebuilder.js","../src/database/drivers/sqlite/table.js","../src/database/drivers/sqlite/web-persistence.js","../src/database/drivers/sqlite/sql/alter-table.js","../src/database/drivers/sqlite/sql/create-index.js","../src/database/drivers/sqlite/sql/create-table.js","../src/database/drivers/sqlite/sql/delete.js","../src/database/drivers/sqlite/sql/drop-table.js","../src/database/drivers/sqlite/sql/insert.js","../src/database/drivers/sqlite/sql/remove-index.js","../src/database/drivers/sqlite/sql/update.js","../src/database/drivers/sqlite/sql/upsert.js","../src/database/drivers/structure-sql/utils.js","../src/database/migration/index.js","../src/database/migrator/files-finder.js","../src/database/migrator/types.js","../src/database/pool/async-tracked-multi-connection.js","../src/database/pool/base-methods-forward.js","../src/database/pool/base.js","../src/database/pool/single-multi-use.js","../src/database/query/alter-table-base.js","../src/database/query/base.js","../src/database/query/create-database-base.js","../src/database/query/create-index-base.js","../src/database/query/create-table-base.js","../src/database/query/delete-base.js","../src/database/query/drop-database-base.js","../src/database/query/drop-table-base.js","../src/database/query/from-base.js","../src/database/query/from-plain.js","../src/database/query/from-table.js","../src/database/query/index.js","../src/database/query/insert-base.js","../src/database/query/join-base.js","../src/database/query/join-object.js","../src/database/query/join-plain.js","../src/database/query/join-tracker.js","../src/database/query/model-class-query.js","../src/database/query/order-base.js","../src/database/query/order-column.js","../src/database/query/order-plain.js","../src/database/query/preloader.js","../src/database/query/query-data.js","../src/database/query/remove-index-base.js","../src/database/query/select-base.js","../src/database/query/select-plain.js","../src/database/query/select-table-and-column.js","../src/database/query/update-base.js","../src/database/query/upsert-base.js","../src/database/query/where-base.js","../src/database/query/where-combinator.js","../src/database/query/where-hash.js","../src/database/query/where-model-class-hash.js","../src/database/query/where-not.js","../src/database/query/where-plain.js","../src/database/query/with-count.js","../src/database/query/preloader/belongs-to.js","../src/database/query/preloader/ensure-model-class-initialized.js","../src/database/query/preloader/has-many.js","../src/database/query/preloader/has-one.js","../src/database/query/preloader/selection.js","../src/database/query-parser/base-query-parser.js","../src/database/query-parser/from-parser.js","../src/database/query-parser/group-parser.js","../src/database/query-parser/joins-parser.js","../src/database/query-parser/limit-parser.js","../src/database/query-parser/options.js","../src/database/query-parser/order-parser.js","../src/database/query-parser/select-parser.js","../src/database/query-parser/where-parser.js","../src/database/record/acts-as-list.js","../src/database/record/auditing.js","../src/database/record/index.js","../src/database/record/record-not-found-error.js","../src/database/record/state-machine.js","../src/database/record/user-module.js","../src/database/record/attachments/attachment-record.js","../src/database/record/attachments/download.js","../src/database/record/attachments/handle.js","../src/database/record/attachments/normalize-input.js","../src/database/record/attachments/store.js","../src/database/record/attachments/storage-drivers/filesystem.js","../src/database/record/attachments/storage-drivers/native.js","../src/database/record/attachments/storage-drivers/s3.js","../src/database/record/instance-relationships/base.js","../src/database/record/instance-relationships/belongs-to.js","../src/database/record/instance-relationships/has-many.js","../src/database/record/instance-relationships/has-one.js","../src/database/record/relationships/base.js","../src/database/record/relationships/belongs-to.js","../src/database/record/relationships/has-many.js","../src/database/record/relationships/has-one.js","../src/database/record/validators/base.js","../src/database/record/validators/format.js","../src/database/record/validators/presence.js","../src/database/record/validators/uniqueness.js","../src/database/table-data/index.js","../src/database/table-data/table-column.js","../src/database/table-data/table-foreign-key.js","../src/database/table-data/table-index.js","../src/database/table-data/table-reference.js","../src/database/tenants/data-copier.js","../src/database/tenants/schema-cloner.js","../src/database/tenants/tenant-table-plan.js","../src/environment-handlers/base.js","../src/environment-handlers/browser.js","../src/environment-handlers/node.js","../src/environment-handlers/node/cli/commands/background-jobs-main.js","../src/environment-handlers/node/cli/commands/background-jobs-runner.js","../src/environment-handlers/node/cli/commands/background-jobs-worker.js","../src/environment-handlers/node/cli/commands/beacon.js","../src/environment-handlers/node/cli/commands/cli-command-context.js","../src/environment-handlers/node/cli/commands/console.js","../src/environment-handlers/node/cli/commands/init.js","../src/environment-handlers/node/cli/commands/routes.js","../src/environment-handlers/node/cli/commands/run-script.js","../src/environment-handlers/node/cli/commands/runner.js","../src/environment-handlers/node/cli/commands/server.js","../src/environment-handlers/node/cli/commands/test.js","../src/environment-handlers/node/cli/commands/db/seed.js","../src/environment-handlers/node/cli/commands/db/schema/dump.js","../src/environment-handlers/node/cli/commands/db/schema/load.js","../src/environment-handlers/node/cli/commands/destroy/migration.js","../src/environment-handlers/node/cli/commands/generate/base-models.js","../src/environment-handlers/node/cli/commands/generate/frontend-models.js","../src/environment-handlers/node/cli/commands/generate/generated-file-banner.js","../src/environment-handlers/node/cli/commands/generate/migration.js","../src/environment-handlers/node/cli/commands/generate/model.js","../src/environment-handlers/node/cli/commands/lint/relationships.js","../src/error-reporting/request-details.js","../src/frontend-model-resource/base-resource.js","../src/frontend-model-resource/velocious-attachment-resource.js","../src/frontend-models/base.js","../src/frontend-models/built-in-resources.js","../src/frontend-models/clear-pending-debounced-callback.js","../src/frontend-models/event-hook-models.js","../src/frontend-models/model-registry.js","../src/frontend-models/outgoing-event-buffer.js","../src/frontend-models/preloader.js","../src/frontend-models/query.js","../src/frontend-models/resource-config-validation.js","../src/frontend-models/resource-definition.js","../src/frontend-models/transport-serialization.js","../src/frontend-models/use-created-event.js","../src/frontend-models/use-destroyed-event.js","../src/frontend-models/use-model-class-event.js","../src/frontend-models/use-updated-event.js","../src/frontend-models/websocket-channel.js","../src/frontend-models/websocket-publishers.js","../src/http-client/header.js","../src/http-client/index.js","../src/http-client/request.js","../src/http-client/response.js","../src/http-client/websocket-client.js","../src/http-server/cookie.js","../src/http-server/development-reloader.js","../src/http-server/index.js","../src/http-server/remote-address.js","../src/http-server/server-client.js","../src/http-server/server-lock.js","../src/http-server/websocket-channel-subscribers.js","../src/http-server/websocket-channel.js","../src/http-server/websocket-connection.js","../src/http-server/websocket-event-log-store.js","../src/http-server/websocket-events-host.js","../src/http-server/websocket-events.js","../src/http-server/client/index.js","../src/http-server/client/params-to-object.js","../src/http-server/client/request-parser.js","../src/http-server/client/request-runner.js","../src/http-server/client/request-timing.js","../src/http-server/client/request.js","../src/http-server/client/response.js","../src/http-server/client/websocket-request.js","../src/http-server/client/websocket-session.js","../src/http-server/client/request-buffer/form-data-part.js","../src/http-server/client/request-buffer/header.js","../src/http-server/client/request-buffer/index.js","../src/http-server/client/uploaded-file/memory-uploaded-file.js","../src/http-server/client/uploaded-file/temporary-uploaded-file.js","../src/http-server/client/uploaded-file/uploaded-file.js","../src/http-server/worker-handler/channel-subscriber-dispatch.js","../src/http-server/worker-handler/in-process.js","../src/http-server/worker-handler/index.js","../src/http-server/worker-handler/worker-script.js","../src/http-server/worker-handler/worker-thread.js","../src/jobs/mail-delivery.js","../src/logger/base-logger.js","../src/logger/console-logger.js","../src/logger/file-logger.js","../src/logger/outputs/array-output.js","../src/logger/outputs/console-output.js","../src/logger/outputs/file-output.js","../src/logger/outputs/stdout-output.js","../src/mailer/base.js","../src/mailer/delivery.js","../src/mailer/index.js","../src/mailer/backends/smtp.js","../src/packages/velocious-package.js","../src/plugins/sqljs-wasm-route-controller.js","../src/plugins/sqljs-wasm-route.js","../src/routes/app-routes.js","../src/routes/base-route.js","../src/routes/basic-route.js","../src/routes/get-route.js","../src/routes/index.js","../src/routes/namespace-route.js","../src/routes/plugin-routes.js","../src/routes/post-route.js","../src/routes/resolver.js","../src/routes/resource-route.js","../src/routes/root-route.js","../src/routes/built-in/debug/controller.js","../src/routes/built-in/errors/controller.js","../src/routes/hooks/frontend-model-command-route-hook.js","../src/sync/conflict-strategy.js","../src/sync/device-identity.js","../src/sync/local-mutation-log.js","../src/sync/offline-grant.js","../src/sync/peer-mutation-bundle.js","../src/sync/server-change-feed.js","../src/sync/sync-envelope-replay-service.js","../src/tenants/tenant-iterator.js","../src/tenants/tenant.js","../src/testing/base-expect.js","../src/testing/browser-frontend-model-event-hook-scenarios.js","../src/testing/browser-test-app.js","../src/testing/expect-to-change.js","../src/testing/expect-utils.js","../src/testing/expect.js","../src/testing/request-client.js","../src/testing/test-files-finder.js","../src/testing/test-filter-parser.js","../src/testing/test-runner.js","../src/testing/test-suite-splitter.js","../src/testing/test.js","../src/types/external-modules.d.ts","../src/utils/backtrace-cleaner-node.js","../src/utils/backtrace-cleaner.js","../src/utils/deburr-column-name.js","../src/utils/event-emitter.js","../src/utils/file-exists.js","../src/utils/format-value.js","../src/utils/is-date.js","../src/utils/model-scope.js","../src/utils/nest-callbacks.js","../src/utils/plain-object.js","../src/utils/ransack.js","../src/utils/rest-args-error.js","../src/utils/singularize-model-name.js","../src/utils/split-sql-statements.js","../src/utils/to-import-specifier.js","../src/utils/with-tracked-stack-async-hooks.js","../src/utils/with-tracked-stack.js"],"version":"6.0.3"}
1
+ {"root":["../index.js","../bin/velocious.js","../src/application.js","../src/configuration-resolver.js","../src/configuration-types.js","../src/configuration.js","../src/controller.js","../src/current-configuration.js","../src/current.js","../src/error-logger.js","../src/frontend-model-controller.js","../src/initializer.js","../src/logger.js","../src/mailer.js","../src/record-payload-values.js","../src/time-zone.js","../src/velocious-error.js","../src/authorization/ability.js","../src/authorization/base-resource.js","../src/background-jobs/client.js","../src/background-jobs/cron-expression.js","../src/background-jobs/forked-runner-child.js","../src/background-jobs/job-record.js","../src/background-jobs/job-registry.js","../src/background-jobs/job-runner.js","../src/background-jobs/job.js","../src/background-jobs/json-socket.js","../src/background-jobs/main.js","../src/background-jobs/normalize-error.js","../src/background-jobs/scheduler.js","../src/background-jobs/socket-request.js","../src/background-jobs/status-reporter.js","../src/background-jobs/store.js","../src/background-jobs/types.js","../src/background-jobs/worker.js","../src/background-jobs/web/authorization.js","../src/background-jobs/web/controller.js","../src/background-jobs/web/index.js","../src/background-jobs/web/path-matcher.js","../src/background-jobs/web/registry.js","../src/beacon/client.js","../src/beacon/in-process-broker.js","../src/beacon/in-process-client.js","../src/beacon/server.js","../src/beacon/types.js","../src/cli/base-command.js","../src/cli/browser-cli.js","../src/cli/index.js","../src/cli/tenant-database-command-helper.js","../src/cli/use-browser-cli.js","../src/cli/commands/background-jobs-main.js","../src/cli/commands/background-jobs-runner.js","../src/cli/commands/background-jobs-worker.js","../src/cli/commands/beacon.js","../src/cli/commands/console.js","../src/cli/commands/init.js","../src/cli/commands/routes.js","../src/cli/commands/run-script.js","../src/cli/commands/runner.js","../src/cli/commands/server.js","../src/cli/commands/test.js","../src/cli/commands/db/base-command.js","../src/cli/commands/db/create.js","../src/cli/commands/db/drop.js","../src/cli/commands/db/migrate.js","../src/cli/commands/db/reset.js","../src/cli/commands/db/rollback.js","../src/cli/commands/db/seed.js","../src/cli/commands/db/schema/dump.js","../src/cli/commands/db/schema/load.js","../src/cli/commands/db/tenants/check.js","../src/cli/commands/db/tenants/create.js","../src/cli/commands/db/tenants/drop.js","../src/cli/commands/db/tenants/migrate.js","../src/cli/commands/destroy/migration.js","../src/cli/commands/generate/base-models.js","../src/cli/commands/generate/frontend-models.js","../src/cli/commands/generate/migration.js","../src/cli/commands/generate/model.js","../src/cli/commands/lint/relationships.js","../src/database/annotations-async-hooks.js","../src/database/annotations.js","../src/database/datetime-storage.js","../src/database/handler.js","../src/database/initializer-from-require-context.js","../src/database/migrations-ledger.js","../src/database/migrator.js","../src/database/use-database.js","../src/database/drivers/base-column.js","../src/database/drivers/base-columns-index.js","../src/database/drivers/base-foreign-key.js","../src/database/drivers/base-table.js","../src/database/drivers/base.js","../src/database/drivers/mssql/column.js","../src/database/drivers/mssql/columns-index.js","../src/database/drivers/mssql/connect-connection.js","../src/database/drivers/mssql/foreign-key.js","../src/database/drivers/mssql/index.js","../src/database/drivers/mssql/options.js","../src/database/drivers/mssql/query-parser.js","../src/database/drivers/mssql/structure-sql.js","../src/database/drivers/mssql/table.js","../src/database/drivers/mssql/sql/alter-table.js","../src/database/drivers/mssql/sql/create-database.js","../src/database/drivers/mssql/sql/create-index.js","../src/database/drivers/mssql/sql/create-table.js","../src/database/drivers/mssql/sql/delete.js","../src/database/drivers/mssql/sql/drop-database.js","../src/database/drivers/mssql/sql/drop-table.js","../src/database/drivers/mssql/sql/insert.js","../src/database/drivers/mssql/sql/remove-index.js","../src/database/drivers/mssql/sql/update.js","../src/database/drivers/mssql/sql/upsert.js","../src/database/drivers/mysql/column.js","../src/database/drivers/mysql/columns-index.js","../src/database/drivers/mysql/foreign-key.js","../src/database/drivers/mysql/index.js","../src/database/drivers/mysql/options.js","../src/database/drivers/mysql/query-parser.js","../src/database/drivers/mysql/query.js","../src/database/drivers/mysql/structure-sql.js","../src/database/drivers/mysql/table.js","../src/database/drivers/mysql/sql/alter-table.js","../src/database/drivers/mysql/sql/create-database.js","../src/database/drivers/mysql/sql/create-index.js","../src/database/drivers/mysql/sql/create-table.js","../src/database/drivers/mysql/sql/delete.js","../src/database/drivers/mysql/sql/drop-database.js","../src/database/drivers/mysql/sql/drop-table.js","../src/database/drivers/mysql/sql/insert.js","../src/database/drivers/mysql/sql/remove-index.js","../src/database/drivers/mysql/sql/update.js","../src/database/drivers/mysql/sql/upsert.js","../src/database/drivers/pgsql/column.js","../src/database/drivers/pgsql/columns-index.js","../src/database/drivers/pgsql/foreign-key.js","../src/database/drivers/pgsql/index.js","../src/database/drivers/pgsql/options.js","../src/database/drivers/pgsql/query-parser.js","../src/database/drivers/pgsql/structure-sql.js","../src/database/drivers/pgsql/table.js","../src/database/drivers/pgsql/sql/alter-table.js","../src/database/drivers/pgsql/sql/create-database.js","../src/database/drivers/pgsql/sql/create-index.js","../src/database/drivers/pgsql/sql/create-table.js","../src/database/drivers/pgsql/sql/delete.js","../src/database/drivers/pgsql/sql/drop-database.js","../src/database/drivers/pgsql/sql/drop-table.js","../src/database/drivers/pgsql/sql/insert.js","../src/database/drivers/pgsql/sql/remove-index.js","../src/database/drivers/pgsql/sql/update.js","../src/database/drivers/pgsql/sql/upsert.js","../src/database/drivers/sqlite/base.js","../src/database/drivers/sqlite/column.js","../src/database/drivers/sqlite/columns-index.js","../src/database/drivers/sqlite/connection-sql-js.js","../src/database/drivers/sqlite/foreign-key.js","../src/database/drivers/sqlite/index.js","../src/database/drivers/sqlite/index.native.js","../src/database/drivers/sqlite/index.web.js","../src/database/drivers/sqlite/options.js","../src/database/drivers/sqlite/query-parser.js","../src/database/drivers/sqlite/query.js","../src/database/drivers/sqlite/query.native.js","../src/database/drivers/sqlite/query.web.js","../src/database/drivers/sqlite/structure-sql.js","../src/database/drivers/sqlite/table-rebuilder.js","../src/database/drivers/sqlite/table.js","../src/database/drivers/sqlite/web-persistence.js","../src/database/drivers/sqlite/sql/alter-table.js","../src/database/drivers/sqlite/sql/create-index.js","../src/database/drivers/sqlite/sql/create-table.js","../src/database/drivers/sqlite/sql/delete.js","../src/database/drivers/sqlite/sql/drop-table.js","../src/database/drivers/sqlite/sql/insert.js","../src/database/drivers/sqlite/sql/remove-index.js","../src/database/drivers/sqlite/sql/update.js","../src/database/drivers/sqlite/sql/upsert.js","../src/database/drivers/structure-sql/utils.js","../src/database/migration/index.js","../src/database/migrator/files-finder.js","../src/database/migrator/types.js","../src/database/pool/async-tracked-multi-connection.js","../src/database/pool/base-methods-forward.js","../src/database/pool/base.js","../src/database/pool/single-multi-use.js","../src/database/query/alter-table-base.js","../src/database/query/base.js","../src/database/query/create-database-base.js","../src/database/query/create-index-base.js","../src/database/query/create-table-base.js","../src/database/query/delete-base.js","../src/database/query/drop-database-base.js","../src/database/query/drop-table-base.js","../src/database/query/from-base.js","../src/database/query/from-plain.js","../src/database/query/from-table.js","../src/database/query/index.js","../src/database/query/insert-base.js","../src/database/query/join-base.js","../src/database/query/join-object.js","../src/database/query/join-plain.js","../src/database/query/join-tracker.js","../src/database/query/model-class-query.js","../src/database/query/order-base.js","../src/database/query/order-column.js","../src/database/query/order-plain.js","../src/database/query/preloader.js","../src/database/query/query-data.js","../src/database/query/remove-index-base.js","../src/database/query/select-base.js","../src/database/query/select-plain.js","../src/database/query/select-table-and-column.js","../src/database/query/update-base.js","../src/database/query/upsert-base.js","../src/database/query/where-base.js","../src/database/query/where-combinator.js","../src/database/query/where-hash.js","../src/database/query/where-model-class-hash.js","../src/database/query/where-not.js","../src/database/query/where-plain.js","../src/database/query/with-count.js","../src/database/query/preloader/belongs-to.js","../src/database/query/preloader/ensure-model-class-initialized.js","../src/database/query/preloader/has-many.js","../src/database/query/preloader/has-one.js","../src/database/query/preloader/selection.js","../src/database/query-parser/base-query-parser.js","../src/database/query-parser/from-parser.js","../src/database/query-parser/group-parser.js","../src/database/query-parser/joins-parser.js","../src/database/query-parser/limit-parser.js","../src/database/query-parser/options.js","../src/database/query-parser/order-parser.js","../src/database/query-parser/select-parser.js","../src/database/query-parser/where-parser.js","../src/database/record/acts-as-list.js","../src/database/record/auditing.js","../src/database/record/index.js","../src/database/record/record-not-found-error.js","../src/database/record/state-machine.js","../src/database/record/user-module.js","../src/database/record/attachments/attachment-record.js","../src/database/record/attachments/download.js","../src/database/record/attachments/handle.js","../src/database/record/attachments/normalize-input.js","../src/database/record/attachments/store.js","../src/database/record/attachments/storage-drivers/filesystem.js","../src/database/record/attachments/storage-drivers/native.js","../src/database/record/attachments/storage-drivers/s3.js","../src/database/record/instance-relationships/base.js","../src/database/record/instance-relationships/belongs-to.js","../src/database/record/instance-relationships/has-many.js","../src/database/record/instance-relationships/has-one.js","../src/database/record/relationships/base.js","../src/database/record/relationships/belongs-to.js","../src/database/record/relationships/has-many.js","../src/database/record/relationships/has-one.js","../src/database/record/validators/base.js","../src/database/record/validators/format.js","../src/database/record/validators/presence.js","../src/database/record/validators/uniqueness.js","../src/database/table-data/index.js","../src/database/table-data/table-column.js","../src/database/table-data/table-foreign-key.js","../src/database/table-data/table-index.js","../src/database/table-data/table-reference.js","../src/database/tenants/data-copier.js","../src/database/tenants/schema-cloner.js","../src/database/tenants/tenant-table-plan.js","../src/environment-handlers/base.js","../src/environment-handlers/browser.js","../src/environment-handlers/node.js","../src/environment-handlers/node/cli/commands/background-jobs-main.js","../src/environment-handlers/node/cli/commands/background-jobs-runner.js","../src/environment-handlers/node/cli/commands/background-jobs-worker.js","../src/environment-handlers/node/cli/commands/beacon.js","../src/environment-handlers/node/cli/commands/cli-command-context.js","../src/environment-handlers/node/cli/commands/console.js","../src/environment-handlers/node/cli/commands/init.js","../src/environment-handlers/node/cli/commands/routes.js","../src/environment-handlers/node/cli/commands/run-script.js","../src/environment-handlers/node/cli/commands/runner.js","../src/environment-handlers/node/cli/commands/server.js","../src/environment-handlers/node/cli/commands/test.js","../src/environment-handlers/node/cli/commands/db/seed.js","../src/environment-handlers/node/cli/commands/db/schema/dump.js","../src/environment-handlers/node/cli/commands/db/schema/load.js","../src/environment-handlers/node/cli/commands/destroy/migration.js","../src/environment-handlers/node/cli/commands/generate/base-models.js","../src/environment-handlers/node/cli/commands/generate/frontend-models.js","../src/environment-handlers/node/cli/commands/generate/generated-file-banner.js","../src/environment-handlers/node/cli/commands/generate/migration.js","../src/environment-handlers/node/cli/commands/generate/model.js","../src/environment-handlers/node/cli/commands/lint/relationships.js","../src/error-reporting/request-details.js","../src/frontend-model-resource/base-resource.js","../src/frontend-model-resource/velocious-attachment-resource.js","../src/frontend-models/base.js","../src/frontend-models/built-in-resources.js","../src/frontend-models/clear-pending-debounced-callback.js","../src/frontend-models/event-hook-models.js","../src/frontend-models/model-registry.js","../src/frontend-models/outgoing-event-buffer.js","../src/frontend-models/preloader.js","../src/frontend-models/query.js","../src/frontend-models/resource-config-validation.js","../src/frontend-models/resource-definition.js","../src/frontend-models/transport-serialization.js","../src/frontend-models/use-created-event.js","../src/frontend-models/use-destroyed-event.js","../src/frontend-models/use-model-class-event.js","../src/frontend-models/use-updated-event.js","../src/frontend-models/websocket-channel.js","../src/frontend-models/websocket-publishers.js","../src/http-client/header.js","../src/http-client/index.js","../src/http-client/request.js","../src/http-client/response.js","../src/http-client/websocket-client.js","../src/http-server/cookie.js","../src/http-server/development-reloader.js","../src/http-server/index.js","../src/http-server/remote-address.js","../src/http-server/server-client.js","../src/http-server/server-lock.js","../src/http-server/websocket-channel-subscribers.js","../src/http-server/websocket-channel.js","../src/http-server/websocket-connection.js","../src/http-server/websocket-event-log-store.js","../src/http-server/websocket-events-host.js","../src/http-server/websocket-events.js","../src/http-server/client/index.js","../src/http-server/client/params-to-object.js","../src/http-server/client/request-parser.js","../src/http-server/client/request-runner.js","../src/http-server/client/request-timing.js","../src/http-server/client/request.js","../src/http-server/client/response.js","../src/http-server/client/websocket-request.js","../src/http-server/client/websocket-session.js","../src/http-server/client/request-buffer/form-data-part.js","../src/http-server/client/request-buffer/header.js","../src/http-server/client/request-buffer/index.js","../src/http-server/client/uploaded-file/memory-uploaded-file.js","../src/http-server/client/uploaded-file/temporary-uploaded-file.js","../src/http-server/client/uploaded-file/uploaded-file.js","../src/http-server/worker-handler/channel-subscriber-dispatch.js","../src/http-server/worker-handler/in-process.js","../src/http-server/worker-handler/index.js","../src/http-server/worker-handler/worker-script.js","../src/http-server/worker-handler/worker-thread.js","../src/jobs/mail-delivery.js","../src/logger/base-logger.js","../src/logger/console-logger.js","../src/logger/file-logger.js","../src/logger/outputs/array-output.js","../src/logger/outputs/console-output.js","../src/logger/outputs/file-output.js","../src/logger/outputs/stdout-output.js","../src/mailer/base.js","../src/mailer/delivery.js","../src/mailer/index.js","../src/mailer/backends/smtp.js","../src/packages/velocious-package.js","../src/plugins/sqljs-wasm-route-controller.js","../src/plugins/sqljs-wasm-route.js","../src/routes/app-routes.js","../src/routes/base-route.js","../src/routes/basic-route.js","../src/routes/get-route.js","../src/routes/index.js","../src/routes/namespace-route.js","../src/routes/plugin-routes.js","../src/routes/post-route.js","../src/routes/resolver.js","../src/routes/resource-route.js","../src/routes/root-route.js","../src/routes/built-in/debug/controller.js","../src/routes/built-in/errors/controller.js","../src/routes/hooks/frontend-model-command-route-hook.js","../src/sync/conflict-strategy.js","../src/sync/device-identity.js","../src/sync/local-mutation-log.js","../src/sync/offline-grant.js","../src/sync/peer-mutation-bundle.js","../src/sync/server-change-feed.js","../src/sync/sync-api-client-types.js","../src/sync/sync-api-client.js","../src/sync/sync-api-controller.js","../src/sync/sync-envelope-replay-service.js","../src/sync/sync-model-change-feed-service.js","../src/tenants/tenant-iterator.js","../src/tenants/tenant.js","../src/testing/base-expect.js","../src/testing/browser-frontend-model-event-hook-scenarios.js","../src/testing/browser-test-app.js","../src/testing/expect-to-change.js","../src/testing/expect-utils.js","../src/testing/expect.js","../src/testing/request-client.js","../src/testing/test-files-finder.js","../src/testing/test-filter-parser.js","../src/testing/test-runner.js","../src/testing/test-suite-splitter.js","../src/testing/test.js","../src/types/external-modules.d.ts","../src/utils/backtrace-cleaner-node.js","../src/utils/backtrace-cleaner.js","../src/utils/deburr-column-name.js","../src/utils/event-emitter.js","../src/utils/file-exists.js","../src/utils/format-value.js","../src/utils/is-date.js","../src/utils/model-scope.js","../src/utils/nest-callbacks.js","../src/utils/plain-object.js","../src/utils/ransack.js","../src/utils/rest-args-error.js","../src/utils/singularize-model-name.js","../src/utils/split-sql-statements.js","../src/utils/to-import-specifier.js","../src/utils/with-tracked-stack-async-hooks.js","../src/utils/with-tracked-stack.js"],"version":"6.0.3"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "build/bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.487",
6
+ "version": "1.0.489",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -69,7 +69,7 @@
69
69
  "pure-uuid": "^2.0.0",
70
70
  "require-context": "^1.1.0",
71
71
  "set-state-compare": "^1.0.61",
72
- "snapreq": "^0.0.8",
72
+ "snapreq": "^0.0.9",
73
73
  "sql-escape-string": "^1.1.0",
74
74
  "sql.js": "^1.12.0",
75
75
  "strftime": "^0.10.2",
@@ -103,7 +103,7 @@
103
103
  "eslint-plugin-jsdoc": "^63.0.0",
104
104
  "eslint-plugin-jsdoc-inline-type-casts": "https://github.com/kaspernj/eslint-plugin-jsdoc-inline-type-casts/archive/refs/heads/master.tar.gz",
105
105
  "eslint-plugin-jsdoc-tag-lines": "^1.0.1",
106
- "fallow": "2.102.0",
106
+ "fallow": "2.104.0",
107
107
  "globals": "^17.0.0",
108
108
  "mssql": "^12.0.0",
109
109
  "mysql": "^2.18.1",
@@ -115,7 +115,7 @@
115
115
  "sqlite": "^5.1.1",
116
116
  "sqlite3": "^6.0.1",
117
117
  "system-testing": "^1.0.108",
118
- "tedious": "^19.1.3",
118
+ "tedious": "^20.0.0",
119
119
  "typescript": "^6.0.2",
120
120
  "uniqunize": "^1.0.3"
121
121
  }
@@ -421,6 +421,7 @@
421
421
  * @property {string} [frontendModelsOutputPath] - Optional output project path where `src/frontend-models` should be generated.
422
422
  * @property {string} [resourcesPath] - Optional override for the resources directory to auto-discover; defaults to `<path>/src/resources`. Set internally for package entries.
423
423
  * @property {Record<string, FrontendModelResourceDefinition>} [frontendModels] - Auto-discovered frontend model definitions keyed by model class name. Set internally by the environment handler — do not set manually.
424
+ * @property {AbilityResourceClassType[]} [abilityResources] - Auto-discovered ability resource classes (frontend-model and authorization) from this project's resources directory. Set internally by the environment handler — do not set manually.
424
425
  */
425
426
 
426
427
  /**
@@ -875,6 +875,34 @@ export default class VelociousConfiguration {
875
875
  */
876
876
  setAbilityResources(resources) { this._abilityResources = resources }
877
877
 
878
+ /**
879
+ * Merges resource classes discovered from the app and every registered package
880
+ * into the ability-resources list. `autoDiscoverResources` populates each backend
881
+ * project's `frontendModels` (including package projects), so this makes a
882
+ * package-contributed model's abilities reach subscription and per-record
883
+ * authorization automatically — consuming apps do not have to hand-register
884
+ * package resources. Already-present classes (e.g. an app's explicitly-set
885
+ * resources) are left untouched.
886
+ * @returns {void} - No return value.
887
+ */
888
+ _mergeDiscoveredAbilityResources() {
889
+ const merged = [...this._abilityResources]
890
+ const seen = new Set(merged)
891
+
892
+ for (const backendProject of this._backendProjects) {
893
+ if (!backendProject.abilityResources) continue
894
+
895
+ for (const ResourceClass of backendProject.abilityResources) {
896
+ if (seen.has(ResourceClass)) continue
897
+
898
+ seen.add(ResourceClass)
899
+ merged.push(ResourceClass)
900
+ }
901
+ }
902
+
903
+ this._abilityResources = merged
904
+ }
905
+
878
906
  /**
879
907
  * Runs get ability resolver.
880
908
  * @returns {import("./configuration-types.js").AbilityResolverType | undefined} - Ability resolver.
@@ -1723,6 +1751,7 @@ export default class VelociousConfiguration {
1723
1751
 
1724
1752
  await this.initializeModels({type})
1725
1753
  await this.getEnvironmentHandler().autoDiscoverResources(this)
1754
+ this._mergeDiscoveredAbilityResources()
1726
1755
  this._validateResourceRelationshipsOnModels()
1727
1756
 
1728
1757
  if (this._initializers) {
@@ -100,10 +100,11 @@ export default class VelociousEnvironmentHandlerNode extends Base{
100
100
  */
101
101
  async autoDiscoverResources(configuration) {
102
102
  const {frontendModelResourceDefinitionIsClass} = await import("../frontend-models/resource-definition.js")
103
+ const {default: AuthorizationBaseResource} = await import("../authorization/base-resource.js")
103
104
  const backendProjects = configuration.getBackendProjects()
104
105
 
105
106
  for (const backendProject of backendProjects) {
106
- if (backendProject.frontendModels) continue
107
+ if (backendProject.abilityResources) continue
107
108
 
108
109
  const resourcesDir = backendProject.resourcesPath || path.join(backendProject.path, "src", "resources")
109
110
  let files
@@ -115,9 +116,13 @@ export default class VelociousEnvironmentHandlerNode extends Base{
115
116
  }
116
117
 
117
118
  /**
118
- * Discovered.
119
+ * Discovered frontend-model resources keyed by model name.
119
120
  * @type {Record<string, ?>} */
120
121
  const discovered = {}
122
+ /**
123
+ * Every discovered ability resource class (frontend-model and authorization).
124
+ * @type {Array<?>} */
125
+ const abilityResourceClasses = []
121
126
 
122
127
  for (const file of files) {
123
128
  if (!file.endsWith(".js") && !file.endsWith(".mjs")) continue
@@ -127,10 +132,17 @@ export default class VelociousEnvironmentHandlerNode extends Base{
127
132
  const imported = await import(filePath)
128
133
  const ResourceClass = imported.default
129
134
 
135
+ // Any authorization resource (frontend-model resources also extend it) that
136
+ // declares a `ModelClass`; skip abstract/common base resources without one.
137
+ const isAbilityResource = typeof ResourceClass === "function"
138
+ && (ResourceClass === AuthorizationBaseResource || ResourceClass.prototype instanceof AuthorizationBaseResource)
139
+
140
+ if (!isAbilityResource || !ResourceClass.ModelClass) continue
141
+
142
+ abilityResourceClasses.push(ResourceClass)
143
+
144
+ // Only frontend-model resources drive routing/generation/publisher discovery.
130
145
  if (!frontendModelResourceDefinitionIsClass(ResourceClass)) continue
131
- // Skip abstract/common base resources that declare no `ModelClass` — they are
132
- // not models, so they must not be recorded as discovered frontend models.
133
- if (!ResourceClass.ModelClass) continue
134
146
 
135
147
  const baseName = file.replace(/\.(js|mjs)$/, "")
136
148
  const modelName = baseName.replace(/-resource$/, "")
@@ -141,9 +153,13 @@ export default class VelociousEnvironmentHandlerNode extends Base{
141
153
  discovered[modelName] = ResourceClass
142
154
  }
143
155
 
144
- if (Object.keys(discovered).length > 0) {
156
+ if (!backendProject.frontendModels && Object.keys(discovered).length > 0) {
145
157
  backendProject.frontendModels = discovered
146
158
  }
159
+
160
+ if (abilityResourceClasses.length > 0) {
161
+ backendProject.abilityResources = abilityResourceClasses
162
+ }
147
163
  }
148
164
  }
149
165
 
@@ -0,0 +1,77 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @module sync-api-client-types
5
+ */
6
+
7
+ /** @typedef {{id: string | null, serverSequence: number | null, updatedAt: string} | null} SyncCursor */
8
+
9
+ /**
10
+ * @typedef {object} SyncChangeEnvelope
11
+ * @property {() => unknown} data - Sync data payload.
12
+ * @property {() => unknown} id - Sync row identifier.
13
+ * @property {() => unknown} resourceId - Resource identifier.
14
+ * @property {() => string | null} resourceType - Resource type name.
15
+ * @property {() => string} syncType - Sync operation type.
16
+ */
17
+
18
+ /**
19
+ * @typedef {object} SyncChangeApplyResult
20
+ * @property {boolean} changed - Whether the local store changed.
21
+ * @property {string | null} [resourceType] - Applied resource type override.
22
+ */
23
+
24
+ /**
25
+ * @typedef {object} SyncResourceConfig
26
+ * @property {(args: {attributes: Record<string, unknown>, data: Record<string, unknown>, record: ?, sync: SyncChangeEnvelope}) => Promise<boolean | void> | boolean | void} [afterApply] - Optional post-save hook.
27
+ * @property {(args: {data: Record<string, unknown>, record: ?, sync: SyncChangeEnvelope}) => Promise<Record<string, unknown>> | Record<string, unknown>} attributes - Allowed attributes builder.
28
+ * @property {boolean} enabled - Whether this resource is sync-enabled.
29
+ * @property {(args: {data: Record<string, unknown>, resourceId: unknown, sync: SyncChangeEnvelope}) => Promise<?> | ?} [findRecord] - Optional upsert finder.
30
+ * @property {(args: {resourceId: unknown, sync: SyncChangeEnvelope}) => Promise<?> | ?} [findRecordForDelete] - Optional destroy finder.
31
+ * @property {?} modelClass - Velocious model class.
32
+ */
33
+
34
+ /**
35
+ * @typedef {object} SyncChangesRequest
36
+ * @property {string} authenticationToken - Auth token.
37
+ * @property {string | null} [afterId] - Last seen row id.
38
+ * @property {number} [afterServerSequence] - Last seen server sequence.
39
+ * @property {string} [afterUpdatedAt] - Last seen timestamp.
40
+ * @property {number} limit - Page size.
41
+ * @property {string | null} [upToId] - Snapshot upper-bound row id.
42
+ * @property {number} [upToServerSequence] - Snapshot upper-bound server sequence.
43
+ * @property {string} [upToUpdatedAt] - Snapshot upper-bound timestamp.
44
+ */
45
+
46
+ /**
47
+ * @typedef {object} SyncChangesResponse
48
+ * @property {string} [errorMessage] - Error message.
49
+ * @property {SyncCursor | Record<string, ?>} [nextCursor] - Next cursor.
50
+ * @property {string} [status] - Response status.
51
+ * @property {Array<unknown>} [syncs] - Sync rows.
52
+ * @property {SyncCursor | Record<string, ?>} [upToCursor] - Snapshot upper-bound cursor.
53
+ */
54
+
55
+ /**
56
+ * @typedef {object} SyncChangesResult
57
+ * @property {boolean} changed - Whether any local record changed.
58
+ * @property {number} pages - Applied page count.
59
+ * @property {Record<string, boolean>} resourceChanged - Changed flags by resource type.
60
+ * @property {Record<string, number>} resourceCounts - Applied counts by resource type.
61
+ * @property {number} syncedCount - Applied row count.
62
+ */
63
+
64
+ /**
65
+ * @typedef {object} SyncReplayItem
66
+ * @property {string | number} id - Sync id.
67
+ * @property {string} syncState - Replay state.
68
+ */
69
+
70
+ /**
71
+ * @typedef {object} SyncReplayResponse
72
+ * @property {string} [errorMessage] - Error message.
73
+ * @property {string} [status] - Response status.
74
+ * @property {Array<SyncReplayItem>} [syncs] - Replay results.
75
+ */
76
+
77
+ export {}