velocious 1.0.644 → 1.0.645
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/README.md +11 -9
- package/build/authorization/ability.js +36 -13
- package/build/background-jobs/adapter.js +9 -0
- package/build/background-jobs/main.js +35 -10
- package/build/background-jobs/store.js +217 -38
- package/build/background-jobs/types.js +15 -0
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +45 -9
- package/build/database/drivers/base-column.js +8 -0
- package/build/database/drivers/mssql/sql/update.js +1 -3
- package/build/database/drivers/mysql/sql/update.js +1 -3
- package/build/database/drivers/pgsql/column.js +22 -0
- package/build/database/drivers/pgsql/sql/update.js +1 -3
- package/build/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/build/database/drivers/sqlite/sql/update.js +1 -3
- package/build/database/query/index.js +2 -2
- package/build/database/query/model-class-query.js +26 -10
- package/build/database/query/preloader/has-many.js +4 -1
- package/build/database/query/query-data.js +2 -1
- package/build/database/query/update-base.js +14 -0
- package/build/database/query/with-count.js +3 -1
- package/build/database/record/acts-as-list.js +37 -13
- package/build/database/record/attachments/store.js +336 -26
- package/build/database/record/auditing.js +6 -3
- package/build/database/record/index.js +197 -42
- package/build/database/record/instance-relationships/belongs-to.js +2 -1
- package/build/database/record/validators/uniqueness.js +8 -5
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/build/frontend-model-controller.js +136 -53
- package/build/frontend-model-resource/base-resource.js +87 -37
- package/build/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/build/frontend-models/base.js +307 -59
- package/build/frontend-models/preloader.js +6 -3
- package/build/frontend-models/query.js +15 -6
- package/build/frontend-models/resource-definition.js +21 -2
- package/build/frontend-models/use-destroyed-event.js +1 -1
- package/build/frontend-models/use-model-class-event.js +2 -2
- package/build/frontend-models/use-updated-event.js +1 -1
- package/build/frontend-models/websocket-channel.js +256 -93
- package/build/frontend-models/websocket-publishers.js +218 -18
- package/build/http-server/client/websocket-session.js +13 -1
- package/build/http-server/websocket-channel.js +15 -2
- package/build/src/authorization/ability.d.ts +9 -3
- package/build/src/authorization/ability.d.ts.map +1 -1
- package/build/src/authorization/ability.js +33 -14
- package/build/src/background-jobs/adapter.d.ts +6 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +9 -1
- package/build/src/background-jobs/main.d.ts +6 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +34 -11
- package/build/src/background-jobs/store.d.ts +66 -12
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +191 -35
- package/build/src/background-jobs/types.d.ts +55 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +16 -1
- package/build/src/configuration-types.d.ts +3 -3
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +18 -8
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +38 -7
- package/build/src/database/drivers/base-column.d.ts +5 -0
- package/build/src/database/drivers/base-column.d.ts.map +1 -1
- package/build/src/database/drivers/base-column.js +8 -1
- package/build/src/database/drivers/mssql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/sql/update.js +2 -4
- package/build/src/database/drivers/mysql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/update.js +2 -4
- package/build/src/database/drivers/pgsql/column.d.ts +5 -0
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +19 -1
- package/build/src/database/drivers/pgsql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/sql/update.js +2 -4
- package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +6 -2
- package/build/src/database/drivers/sqlite/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/update.js +2 -4
- package/build/src/database/query/index.d.ts +4 -4
- package/build/src/database/query/index.d.ts.map +1 -1
- package/build/src/database/query/index.js +3 -3
- package/build/src/database/query/model-class-query.d.ts +8 -2
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +25 -11
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +4 -2
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +3 -2
- package/build/src/database/query/update-base.d.ts +7 -0
- package/build/src/database/query/update-base.d.ts.map +1 -1
- package/build/src/database/query/update-base.js +13 -1
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +3 -2
- package/build/src/database/record/acts-as-list.d.ts.map +1 -1
- package/build/src/database/record/acts-as-list.js +35 -14
- package/build/src/database/record/attachments/store.d.ts +67 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +301 -25
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +7 -4
- package/build/src/database/record/index.d.ts +78 -26
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +171 -42
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +3 -2
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +8 -6
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +4 -4
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -12
- package/build/src/frontend-model-controller.d.ts +29 -8
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +122 -51
- package/build/src/frontend-model-resource/base-resource.d.ts +51 -24
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +83 -37
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts +45 -26
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/velocious-attachment-resource.js +89 -31
- package/build/src/frontend-models/base.d.ts +53 -20
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +275 -59
- package/build/src/frontend-models/preloader.d.ts.map +1 -1
- package/build/src/frontend-models/preloader.js +6 -4
- package/build/src/frontend-models/query.d.ts +2 -2
- package/build/src/frontend-models/query.d.ts.map +1 -1
- package/build/src/frontend-models/query.js +14 -7
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -3
- package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
- package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-destroyed-event.js +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-model-class-event.js +3 -3
- package/build/src/frontend-models/use-updated-event.d.ts +1 -1
- package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-updated-event.js +2 -2
- package/build/src/frontend-models/websocket-channel.d.ts +60 -41
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +225 -85
- package/build/src/frontend-models/websocket-publishers.d.ts +9 -0
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +190 -19
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +11 -2
- package/build/src/http-server/websocket-channel.d.ts +20 -8
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +15 -3
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +4 -3
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +3 -2
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
- package/build/src/sync/sync-envelope-replay-service.js +6 -5
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +4 -3
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts +2 -2
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +3 -3
- package/build/src/testing/test-profiler.d.ts +4 -4
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +3 -3
- package/build/src/testing/test-runner.d.ts +271 -117
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +496 -427
- package/build/src/testing/test.d.ts +11 -72
- package/build/src/testing/test.d.ts.map +1 -1
- package/build/src/testing/test.js +131 -284
- package/build/src/testing/velocious-attempt-executor.d.ts +14 -29
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-attempt-executor.js +52 -46
- package/build/src/testing/velocious-runner-reporter.d.ts +29 -34
- package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -1
- package/build/src/testing/velocious-runner-reporter.js +145 -57
- package/build/src/testing/velocious-suite-hook-executor.d.ts +5 -23
- package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-suite-hook-executor.js +22 -47
- package/build/src/testing/velocious-test-arguments.d.ts +11 -6
- package/build/src/testing/velocious-test-arguments.d.ts.map +1 -1
- package/build/src/testing/velocious-test-arguments.js +18 -10
- package/build/src/utils/model-primary-key.d.ts +73 -0
- package/build/src/utils/model-primary-key.d.ts.map +1 -0
- package/build/src/utils/model-primary-key.js +144 -0
- package/build/sync/sync-api-client.js +3 -2
- package/build/sync/sync-client.js +2 -1
- package/build/sync/sync-envelope-replay-service.js +5 -4
- package/build/sync/sync-publisher.js +3 -2
- package/build/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/build/testing/test-profiler.js +2 -2
- package/build/testing/test-runner.js +493 -449
- package/build/testing/test.js +164 -306
- package/build/testing/velocious-attempt-executor.js +55 -45
- package/build/testing/velocious-runner-reporter.js +156 -58
- package/build/testing/velocious-suite-hook-executor.js +22 -50
- package/build/testing/velocious-test-arguments.js +19 -10
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/model-primary-key.js +171 -0
- package/package.json +5 -4
- package/src/authorization/ability.js +36 -13
- package/src/background-jobs/adapter.js +9 -0
- package/src/background-jobs/main.js +35 -10
- package/src/background-jobs/store.js +217 -38
- package/src/background-jobs/types.js +15 -0
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +45 -9
- package/src/database/drivers/base-column.js +8 -0
- package/src/database/drivers/mssql/sql/update.js +1 -3
- package/src/database/drivers/mysql/sql/update.js +1 -3
- package/src/database/drivers/pgsql/column.js +22 -0
- package/src/database/drivers/pgsql/sql/update.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/src/database/drivers/sqlite/sql/update.js +1 -3
- package/src/database/query/index.js +2 -2
- package/src/database/query/model-class-query.js +26 -10
- package/src/database/query/preloader/has-many.js +4 -1
- package/src/database/query/query-data.js +2 -1
- package/src/database/query/update-base.js +14 -0
- package/src/database/query/with-count.js +3 -1
- package/src/database/record/acts-as-list.js +37 -13
- package/src/database/record/attachments/store.js +336 -26
- package/src/database/record/auditing.js +6 -3
- package/src/database/record/index.js +197 -42
- package/src/database/record/instance-relationships/belongs-to.js +2 -1
- package/src/database/record/validators/uniqueness.js +8 -5
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/src/frontend-model-controller.js +136 -53
- package/src/frontend-model-resource/base-resource.js +87 -37
- package/src/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/src/frontend-models/base.js +307 -59
- package/src/frontend-models/preloader.js +6 -3
- package/src/frontend-models/query.js +15 -6
- package/src/frontend-models/resource-definition.js +21 -2
- package/src/frontend-models/use-destroyed-event.js +1 -1
- package/src/frontend-models/use-model-class-event.js +2 -2
- package/src/frontend-models/use-updated-event.js +1 -1
- package/src/frontend-models/websocket-channel.js +256 -93
- package/src/frontend-models/websocket-publishers.js +218 -18
- package/src/http-server/client/websocket-session.js +13 -1
- package/src/http-server/websocket-channel.js +15 -2
- package/src/sync/sync-api-client.js +3 -2
- package/src/sync/sync-client.js +2 -1
- package/src/sync/sync-envelope-replay-service.js +5 -4
- package/src/sync/sync-publisher.js +3 -2
- package/src/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/src/testing/test-profiler.js +2 -2
- package/src/testing/test-runner.js +493 -449
- package/src/testing/test.js +164 -306
- package/src/testing/velocious-attempt-executor.js +55 -45
- package/src/testing/velocious-runner-reporter.js +156 -58
- package/src/testing/velocious-suite-hook-executor.js +22 -50
- package/src/testing/velocious-test-arguments.js +19 -10
- package/src/utils/model-primary-key.js +171 -0
- package/build/src/testing/testing-package-adapter.d.ts +0 -9
- package/build/src/testing/testing-package-adapter.d.ts.map +0 -1
- package/build/src/testing/testing-package-adapter.js +0 -69
- package/build/testing/testing-package-adapter.js +0 -76
- package/src/testing/testing-package-adapter.js +0 -76
|
@@ -4,9 +4,16 @@ import AuthorizationBaseResource from "../authorization/base-resource.js"
|
|
|
4
4
|
import {frontendModelResourcesWithBuiltInsForBackendProject} from "./built-in-resources.js"
|
|
5
5
|
import {frontendModelResourceDefinitionIsClass} from "./resource-definition.js"
|
|
6
6
|
import {serializeFrontendModelTransportValue} from "./transport-serialization.js"
|
|
7
|
+
import {modelPrimaryKeyCacheKey, readModelPrimaryKeyValue} from "../utils/model-primary-key.js"
|
|
8
|
+
|
|
9
|
+
/** @typedef {{primaryKey: import("../utils/model-primary-key.js").ModelPrimaryKeyDefinition}} FrontendModelPublisherResource */
|
|
10
|
+
/** @typedef {Record<string, import("./query.js").FrontendModelTransportValue>} FrontendModelDestroyAuthorizationRecord */
|
|
11
|
+
/** @typedef {import("../database/record/index.js").default & {__frontendModelWebsocketAction?: "create" | "update", __frontendModelWebsocketDestroyAuthorizationRecord?: FrontendModelDestroyAuthorizationRecord, __frontendModelWebsocketPreviousIds?: Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>}} FrontendModelWebsocketRecord */
|
|
7
12
|
|
|
8
13
|
const modelClassesWithRegisteredHooks = new WeakSet()
|
|
9
14
|
const channelClassRegisteredConfigurations = new WeakSet()
|
|
15
|
+
/** @type {WeakMap<import("../configuration.js").default, WeakMap<typeof import("../database/record/index.js").default, Map<string, FrontendModelPublisherResource>>>} */
|
|
16
|
+
const publisherResourcesByConfiguration = new WeakMap()
|
|
10
17
|
|
|
11
18
|
/** Shared channel name for all frontend-model lifecycle subscriptions. */
|
|
12
19
|
export const FRONTEND_MODELS_CHANNEL_NAME = "frontend-models"
|
|
@@ -60,7 +67,7 @@ function frontendModelResourcesFromAbilityResourcesList(abilityResources) {
|
|
|
60
67
|
// throw `requires a static ModelClass` during ability-resource discovery.
|
|
61
68
|
if (!resourceClass.ModelClass) continue
|
|
62
69
|
|
|
63
|
-
const modelName = resourceClass.modelClass().getModelName()
|
|
70
|
+
const modelName = resourceClass.resourceConfig().modelName || resourceClass.modelClass().getModelName()
|
|
64
71
|
|
|
65
72
|
resources[modelName] = resourceClass
|
|
66
73
|
} else if (resourceClass.prototype instanceof AuthorizationBaseResource) {
|
|
@@ -113,14 +120,36 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
113
120
|
configuration.registerWebsocketChannel(FRONTEND_MODELS_CHANNEL_NAME, FrontendModelWebsocketChannel)
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
for (const resourceClass of Object.
|
|
123
|
+
for (const [modelName, resourceClass] of Object.entries(allFrontendModels)) {
|
|
117
124
|
// An abstract base resource (no static ModelClass — e.g. an app's shared
|
|
118
125
|
// `BaseResource` that other resources extend) backs no model, so there is
|
|
119
126
|
// nothing to publish realtime events for. Skip it instead of throwing.
|
|
120
127
|
if (!resourceClass.ModelClass) continue
|
|
121
128
|
|
|
122
129
|
const modelClass = resourceClass.modelClass()
|
|
123
|
-
const
|
|
130
|
+
const resourceConfiguration = resourceClass.resourceConfig()
|
|
131
|
+
const configuredPrimaryKey = resourceConfiguration.primaryKey
|
|
132
|
+
const modelPrimaryKey = modelClass.primaryKey()
|
|
133
|
+
const primaryKey = configuredPrimaryKey || (Array.isArray(modelPrimaryKey)
|
|
134
|
+
? modelPrimaryKey.map((columnName) => modelClass.resolveAttributeName(columnName) || columnName)
|
|
135
|
+
: modelClass.resolveAttributeName(modelPrimaryKey) || modelPrimaryKey)
|
|
136
|
+
let publisherResourcesByModelClass = publisherResourcesByConfiguration.get(configuration)
|
|
137
|
+
|
|
138
|
+
if (!publisherResourcesByModelClass) {
|
|
139
|
+
publisherResourcesByModelClass = new WeakMap()
|
|
140
|
+
publisherResourcesByConfiguration.set(configuration, publisherResourcesByModelClass)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let publisherResources = publisherResourcesByModelClass.get(modelClass)
|
|
144
|
+
|
|
145
|
+
if (!publisherResources) {
|
|
146
|
+
publisherResources = new Map()
|
|
147
|
+
publisherResourcesByModelClass.set(modelClass, publisherResources)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
publisherResources.set(modelName, {
|
|
151
|
+
primaryKey
|
|
152
|
+
})
|
|
124
153
|
|
|
125
154
|
// Register lifecycle hooks once per model class, not per configuration. A model class belongs to a
|
|
126
155
|
// single backend project/config in production, so per-config registration only differs in tests where
|
|
@@ -132,36 +161,203 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
132
161
|
modelClassesWithRegisteredHooks.add(modelClass)
|
|
133
162
|
|
|
134
163
|
modelClass.beforeCreate((model) => {
|
|
135
|
-
/** @type {
|
|
164
|
+
/** @type {FrontendModelWebsocketRecord} */ (model).__frontendModelWebsocketAction = "create"
|
|
136
165
|
})
|
|
137
166
|
|
|
138
|
-
modelClass.beforeUpdate((model) => {
|
|
139
|
-
/** @type {
|
|
167
|
+
modelClass.beforeUpdate(async (model) => {
|
|
168
|
+
const websocketModel = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
169
|
+
|
|
170
|
+
websocketModel.__frontendModelWebsocketAction = "update"
|
|
171
|
+
websocketModel.__frontendModelWebsocketPreviousIds = await frontendModelPreviousResourceIdentities(model)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
modelClass.beforeDestroy(async (model) => {
|
|
175
|
+
const websocketModel = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
176
|
+
const persistedModel = await model
|
|
177
|
+
.queryForModel(model.getModelClass())
|
|
178
|
+
.find(model._persistedPrimaryKeyValue())
|
|
179
|
+
|
|
180
|
+
if (!persistedModel) throw new Error(`Cannot capture websocket destroy authorization for missing ${model.getModelClass().name}`)
|
|
181
|
+
|
|
182
|
+
websocketModel.__frontendModelWebsocketPreviousIds = frontendModelResourceIdentities(persistedModel)
|
|
183
|
+
websocketModel.__frontendModelWebsocketDestroyAuthorizationRecord = frontendModelDestroyAuthorizationRecord(persistedModel)
|
|
140
184
|
})
|
|
141
185
|
|
|
142
186
|
modelClass.afterSave((model) => {
|
|
143
|
-
const modelWithWebsocketAction = /** @type {
|
|
187
|
+
const modelWithWebsocketAction = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
144
188
|
const action = modelWithWebsocketAction.__frontendModelWebsocketAction
|
|
145
189
|
|
|
146
190
|
if (action !== "create" && action !== "update") return
|
|
191
|
+
const previousIds = modelWithWebsocketAction.__frontendModelWebsocketPreviousIds
|
|
147
192
|
|
|
148
193
|
void model.connection().afterCommit(async () => {
|
|
149
|
-
|
|
150
|
-
action,
|
|
151
|
-
id: model.id(),
|
|
152
|
-
record: model.attributes()
|
|
153
|
-
})
|
|
194
|
+
broadcastFrontendModelEvents(model, action, previousIds)
|
|
154
195
|
})
|
|
155
196
|
delete modelWithWebsocketAction.__frontendModelWebsocketAction
|
|
197
|
+
delete modelWithWebsocketAction.__frontendModelWebsocketPreviousIds
|
|
156
198
|
})
|
|
157
199
|
|
|
158
200
|
modelClass.afterDestroy((model) => {
|
|
201
|
+
const websocketModel = /** @type {FrontendModelWebsocketRecord} */ (model)
|
|
202
|
+
const destroyAuthorizationRecord = websocketModel.__frontendModelWebsocketDestroyAuthorizationRecord
|
|
203
|
+
const previousIds = websocketModel.__frontendModelWebsocketPreviousIds
|
|
204
|
+
|
|
159
205
|
void model.connection().afterCommit(async () => {
|
|
160
|
-
|
|
161
|
-
action: "destroy",
|
|
162
|
-
id: model.id()
|
|
163
|
-
})
|
|
206
|
+
broadcastFrontendModelEvents(model, "destroy", previousIds, destroyAuthorizationRecord)
|
|
164
207
|
})
|
|
208
|
+
delete websocketModel.__frontendModelWebsocketDestroyAuthorizationRecord
|
|
209
|
+
delete websocketModel.__frontendModelWebsocketPreviousIds
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Returns every resource identity represented by the record before its pending changes or destruction.
|
|
216
|
+
* @param {import("../database/record/index.js").default} model - Backing model before update or destroy.
|
|
217
|
+
* @returns {Promise<Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>>} - Previous identities by resource name.
|
|
218
|
+
*/
|
|
219
|
+
async function frontendModelPreviousResourceIdentities(model) {
|
|
220
|
+
const publisherResources = publisherResourcesByConfiguration.get(model._getConfiguration())?.get(model.getModelClass())
|
|
221
|
+
/** @type {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} */
|
|
222
|
+
const previousIds = new Map()
|
|
223
|
+
|
|
224
|
+
if (!publisherResources) return previousIds
|
|
225
|
+
|
|
226
|
+
for (const [modelName, {primaryKey}] of publisherResources) {
|
|
227
|
+
const previousId = frontendModelResourceIdentity({model, previous: true, primaryKey})
|
|
228
|
+
|
|
229
|
+
if (previousId !== null) previousIds.set(modelName, previousId)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (previousIds.size === publisherResources.size) return previousIds
|
|
233
|
+
|
|
234
|
+
const persistedModel = await model
|
|
235
|
+
.queryForModel(model.getModelClass())
|
|
236
|
+
.find(model._persistedPrimaryKeyValue())
|
|
237
|
+
|
|
238
|
+
for (const [modelName, {primaryKey}] of publisherResources) {
|
|
239
|
+
if (previousIds.has(modelName)) continue
|
|
240
|
+
|
|
241
|
+
const persistedId = frontendModelResourceIdentity({model: persistedModel, primaryKey})
|
|
242
|
+
|
|
243
|
+
if (persistedId !== null) previousIds.set(modelName, persistedId)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return previousIds
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Returns every configured resource identity represented by a persisted backing record.
|
|
251
|
+
* @param {import("../database/record/index.js").default} model - Fully loaded persisted backing record.
|
|
252
|
+
* @returns {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} - Identities by resource name.
|
|
253
|
+
*/
|
|
254
|
+
function frontendModelResourceIdentities(model) {
|
|
255
|
+
const publisherResources = publisherResourcesByConfiguration.get(model._getConfiguration())?.get(model.getModelClass())
|
|
256
|
+
/** @type {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} */
|
|
257
|
+
const identities = new Map()
|
|
258
|
+
|
|
259
|
+
if (!publisherResources) return identities
|
|
260
|
+
|
|
261
|
+
for (const [modelName, {primaryKey}] of publisherResources) {
|
|
262
|
+
const id = frontendModelResourceIdentity({model, primaryKey})
|
|
263
|
+
|
|
264
|
+
if (id !== null) identities.set(modelName, id)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return identities
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Serializes the persisted record for server-side destroy authorization. Binary values
|
|
272
|
+
* use a dedicated byte-array marker because the shared transport serializer otherwise
|
|
273
|
+
* leaves Buffers to the JSON implementation used by the worker or Beacon transport.
|
|
274
|
+
* @param {import("../database/record/index.js").default} model - Fully loaded persisted backing record.
|
|
275
|
+
* @returns {FrontendModelDestroyAuthorizationRecord} - Column-keyed transport values.
|
|
276
|
+
*/
|
|
277
|
+
function frontendModelDestroyAuthorizationRecord(model) {
|
|
278
|
+
const serializationOptions = transportSerializationOptionsForConfiguration(model._getConfiguration())
|
|
279
|
+
/** @type {FrontendModelDestroyAuthorizationRecord} */
|
|
280
|
+
const authorizationRecord = {}
|
|
281
|
+
|
|
282
|
+
for (const [columnName, value] of Object.entries(model.rawAttributes())) {
|
|
283
|
+
authorizationRecord[columnName] = value instanceof Uint8Array
|
|
284
|
+
? {__velociousDestroyAuthorizationType: "binary", value: Array.from(value)}
|
|
285
|
+
: serializeFrontendModelTransportValue(value, serializationOptions)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (Object.keys(authorizationRecord).length === 0) {
|
|
289
|
+
throw new Error(`Cannot capture websocket destroy authorization without attributes for ${model.getModelClass().name}`)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return authorizationRecord
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Reads a resource identity only when every identity attribute was loaded on the backing record.
|
|
297
|
+
* @param {object} args - Identity arguments.
|
|
298
|
+
* @param {import("../database/record/index.js").default} args.model - Backing model.
|
|
299
|
+
* @param {boolean} [args.previous] - Read values from before pending changes.
|
|
300
|
+
* @param {import("../utils/model-primary-key.js").ModelPrimaryKeyDefinition} args.primaryKey - Resource identity definition.
|
|
301
|
+
* @returns {import("../utils/model-primary-key.js").ModelPrimaryKeyValue | null} - Complete identity or null when unavailable.
|
|
302
|
+
*/
|
|
303
|
+
function frontendModelResourceIdentity({model, previous = false, primaryKey}) {
|
|
304
|
+
const attributes = model.attributes()
|
|
305
|
+
const changes = model.changes()
|
|
306
|
+
/** @type {Record<string, import("../utils/model-primary-key.js").ModelPrimaryKeyScalar>} */
|
|
307
|
+
const identityAttributes = {}
|
|
308
|
+
const primaryKeyAttributes = Array.isArray(primaryKey) ? primaryKey : [primaryKey]
|
|
309
|
+
|
|
310
|
+
for (const attributeName of primaryKeyAttributes) {
|
|
311
|
+
const columnName = model.getModelClass().getColumnNameForAttributeName(attributeName)
|
|
312
|
+
let value
|
|
313
|
+
|
|
314
|
+
if (previous && Object.hasOwn(changes, columnName)) {
|
|
315
|
+
value = changes[columnName][0]
|
|
316
|
+
} else {
|
|
317
|
+
if (!Object.hasOwn(attributes, attributeName)) return null
|
|
318
|
+
|
|
319
|
+
value = attributes[attributeName]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (typeof value !== "string" && typeof value !== "number") return null
|
|
323
|
+
|
|
324
|
+
identityAttributes[attributeName] = value
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return readModelPrimaryKeyValue(primaryKey, (attributeName) => identityAttributes[attributeName])
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Fans one backing-record lifecycle event out through every configured frontend-resource identity.
|
|
332
|
+
* @param {import("../database/record/index.js").default} model - Backing model instance.
|
|
333
|
+
* @param {"create" | "update" | "destroy"} action - Lifecycle action.
|
|
334
|
+
* @param {Map<string, import("../utils/model-primary-key.js").ModelPrimaryKeyValue>} [previousIds] - Persisted identities captured before update or destroy.
|
|
335
|
+
* @param {FrontendModelDestroyAuthorizationRecord} [destroyAuthorizationRecord] - Server-only pre-delete row used to authorize a destroyed record.
|
|
336
|
+
* @returns {void}
|
|
337
|
+
*/
|
|
338
|
+
function broadcastFrontendModelEvents(model, action, previousIds, destroyAuthorizationRecord) {
|
|
339
|
+
const configuration = model._getConfiguration()
|
|
340
|
+
const publisherResources = publisherResourcesByConfiguration.get(configuration)?.get(model.getModelClass())
|
|
341
|
+
|
|
342
|
+
if (!publisherResources) return
|
|
343
|
+
|
|
344
|
+
for (const [modelName, {primaryKey}] of publisherResources) {
|
|
345
|
+
const previousId = previousIds?.get(modelName)
|
|
346
|
+
const currentId = frontendModelResourceIdentity({model, primaryKey})
|
|
347
|
+
const id = action === "destroy" ? previousId : currentId ?? previousId
|
|
348
|
+
|
|
349
|
+
if (id === null || id === undefined) continue
|
|
350
|
+
|
|
351
|
+
const identityChanged = action === "update"
|
|
352
|
+
&& currentId !== null
|
|
353
|
+
&& previousId !== undefined
|
|
354
|
+
&& modelPrimaryKeyCacheKey(primaryKey, previousId) !== modelPrimaryKeyCacheKey(primaryKey, id)
|
|
355
|
+
|
|
356
|
+
broadcastFrontendModelEvent(configuration, modelName, {
|
|
357
|
+
action,
|
|
358
|
+
id,
|
|
359
|
+
...(destroyAuthorizationRecord !== undefined ? {destroyAuthorizationRecord} : {}),
|
|
360
|
+
...(identityChanged ? {previousId} : {})
|
|
165
361
|
})
|
|
166
362
|
}
|
|
167
363
|
}
|
|
@@ -172,7 +368,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
172
368
|
* transport serializer so Date/undefined/etc. survive the JSON hop.
|
|
173
369
|
* @param {import("../configuration.js").default} configuration - Configuration instance.
|
|
174
370
|
* @param {string} modelName - Model class name.
|
|
175
|
-
* @param {{action: "create" | "update" | "destroy", id:
|
|
371
|
+
* @param {{action: "create" | "update" | "destroy", destroyAuthorizationRecord?: FrontendModelDestroyAuthorizationRecord, id: import("../utils/model-primary-key.js").ModelPrimaryKeyValue, previousId?: import("../utils/model-primary-key.js").ModelPrimaryKeyValue, record?: Record<string, import("./query.js").FrontendModelTransportValue>}} event - Lifecycle event.
|
|
176
372
|
* @returns {void}
|
|
177
373
|
*/
|
|
178
374
|
function broadcastFrontendModelEvent(configuration, modelName, event) {
|
|
@@ -180,8 +376,12 @@ function broadcastFrontendModelEvent(configuration, modelName, event) {
|
|
|
180
376
|
action: event.action,
|
|
181
377
|
id: event.id,
|
|
182
378
|
model: modelName,
|
|
379
|
+
...(event.previousId !== undefined ? {previousId: event.previousId} : {}),
|
|
183
380
|
...(event.record ? {record: serializeFrontendModelTransportValue(event.record, transportSerializationOptionsForConfiguration(configuration))} : {})
|
|
184
381
|
}
|
|
185
382
|
|
|
186
|
-
configuration.broadcastToChannel(FRONTEND_MODELS_CHANNEL_NAME, {
|
|
383
|
+
configuration.broadcastToChannel(FRONTEND_MODELS_CHANNEL_NAME, {
|
|
384
|
+
...(event.destroyAuthorizationRecord !== undefined ? {destroyAuthorizationRecord: event.destroyAuthorizationRecord} : {}),
|
|
385
|
+
model: modelName
|
|
386
|
+
}, body)
|
|
187
387
|
}
|
|
@@ -1880,7 +1880,19 @@ export default class VelociousHttpServerClientWebsocketSession {
|
|
|
1880
1880
|
for (const event of events) {
|
|
1881
1881
|
if (subscription.isClosed()) break
|
|
1882
1882
|
|
|
1883
|
-
|
|
1883
|
+
if (await subscription._requiresReplayGap(event.payload)) {
|
|
1884
|
+
this.sendJson({
|
|
1885
|
+
type: "channel-replay-gap",
|
|
1886
|
+
subscriptionId: subscription.subscriptionId,
|
|
1887
|
+
lastEventId
|
|
1888
|
+
})
|
|
1889
|
+
return
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
await subscription.deliverBroadcast(
|
|
1893
|
+
/** @type {import("../websocket-channel.js").WebsocketJsonValue} */ (event.payload),
|
|
1894
|
+
{eventId: event.id}
|
|
1895
|
+
)
|
|
1884
1896
|
}
|
|
1885
1897
|
}
|
|
1886
1898
|
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* WebsocketParams type.
|
|
9
9
|
* @typedef {Record<string, WebsocketJsonValue>} WebsocketParams
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Server-side metadata accompanying a matched broadcast.
|
|
13
|
+
* @typedef {{broadcastParams?: WebsocketParams, eventId?: string}} WebsocketBroadcastMetadata
|
|
14
|
+
*/
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* Base class for app-defined 1:N pub/sub channels.
|
|
@@ -90,6 +94,15 @@ export default class VelociousWebsocketChannel {
|
|
|
90
94
|
*/
|
|
91
95
|
matches(..._broadcastArgs) { return true }
|
|
92
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Whether replaying a persisted broadcast would require a client resync.
|
|
99
|
+
* Subclasses override this when replay storage deliberately omits metadata
|
|
100
|
+
* required to deliver an event safely.
|
|
101
|
+
* @param {WebsocketJsonValue} _body - Persisted broadcast payload.
|
|
102
|
+
* @returns {boolean | Promise<boolean>} - Whether the session must report a replay gap.
|
|
103
|
+
*/
|
|
104
|
+
_requiresReplayGap(_body) { return false }
|
|
105
|
+
|
|
93
106
|
/**
|
|
94
107
|
* Returns sanitized diagnostics for debug snapshots.
|
|
95
108
|
* Subclasses can override to expose non-sensitive routing details.
|
|
@@ -102,7 +115,7 @@ export default class VelociousWebsocketChannel {
|
|
|
102
115
|
* override when the outbound body must be tailored to subscription
|
|
103
116
|
* params before sending.
|
|
104
117
|
* @param {WebsocketJsonValue} body - Broadcast payload offered to this subscription.
|
|
105
|
-
* @param {
|
|
118
|
+
* @param {WebsocketBroadcastMetadata} [meta] - Optional server-side broadcast metadata.
|
|
106
119
|
* @returns {void | Promise<void>} - Completes after broadcast delivery.
|
|
107
120
|
*/
|
|
108
121
|
deliverBroadcast(body, meta) {
|
|
@@ -114,7 +127,7 @@ export default class VelociousWebsocketChannel {
|
|
|
114
127
|
* When `meta.eventId` is provided, the client receives it so it
|
|
115
128
|
* can track its checkpoint for `lastEventId` replay on reconnect.
|
|
116
129
|
* @param {WebsocketJsonValue} body - Channel payload to send to the subscribed client.
|
|
117
|
-
* @param {
|
|
130
|
+
* @param {WebsocketBroadcastMetadata} [meta] - Optional server-side broadcast metadata.
|
|
118
131
|
* @returns {void}
|
|
119
132
|
*/
|
|
120
133
|
sendMessage(body, meta) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {optionalBoolean, optionalInteger} from "typanic"
|
|
4
4
|
|
|
5
5
|
import recordChanges from "../database/record-changes.js"
|
|
6
|
+
import {scalarModelPrimaryKeyValue} from "../utils/model-primary-key.js"
|
|
6
7
|
import {applySyncReplayResultToLocalMutationLog} from "./conflict-strategy.js"
|
|
7
8
|
|
|
8
9
|
/** @typedef {import("./sync-api-client-types.js").SyncChangeApplyResult} SyncChangeApplyResult */
|
|
@@ -35,7 +36,7 @@ export default class SyncApiClient {
|
|
|
35
36
|
* @returns {Promise<import("./local-mutation-log.js").LocalMutationLogRecord>} Appended intent.
|
|
36
37
|
*/
|
|
37
38
|
static async queueConflictTrackedSync({baseVersion, conflictTracking, data, operation, resource, resourceType, syncType}) {
|
|
38
|
-
const resourceId = String(resource.id())
|
|
39
|
+
const resourceId = String(scalarModelPrimaryKeyValue(resource.id(), `Conflict-tracked sync for ${resourceType}`))
|
|
39
40
|
const records = await conflictTracking.mutationLog.records()
|
|
40
41
|
const predecessor = records
|
|
41
42
|
.filter((record) => record.mutation.model === resourceType && record.mutation.payload?.resourceId === resourceId)
|
|
@@ -675,7 +676,7 @@ export default class SyncApiClient {
|
|
|
675
676
|
* @returns {Promise<ReturnType<typeof JSON.parse>>} Local sync row.
|
|
676
677
|
*/
|
|
677
678
|
static async queueLocalSync(args) {
|
|
678
|
-
const resourceRecordId = args.resource.id()
|
|
679
|
+
const resourceRecordId = scalarModelPrimaryKeyValue(args.resource.id(), "Local sync queueing")
|
|
679
680
|
const modelClass = args.resource.constructor
|
|
680
681
|
|
|
681
682
|
if (typeof modelClass.getModelName !== "function") {
|
package/src/sync/sync-client.js
CHANGED
|
@@ -4,6 +4,7 @@ import Configuration from "../configuration.js"
|
|
|
4
4
|
import {isBooleanColumnType} from "../database/column-types.js"
|
|
5
5
|
import Logger from "../logger.js"
|
|
6
6
|
import {captureRemoteRequestContext, mergeRemoteRequestContext} from "../remote-request-context.js"
|
|
7
|
+
import {scalarModelPrimaryKey} from "../utils/model-primary-key.js"
|
|
7
8
|
import restArgsError from "../utils/rest-args-error.js"
|
|
8
9
|
import VelociousWebsocketClient from "../http-client/websocket-client.js"
|
|
9
10
|
|
|
@@ -1243,7 +1244,7 @@ function derivedSyncAttributes({modelClass, resourceType}) {
|
|
|
1243
1244
|
const localOnlyAttributes = []
|
|
1244
1245
|
|
|
1245
1246
|
if (modelClass.hasPrimaryKey()) {
|
|
1246
|
-
const primaryKeyColumn = modelClass.primaryKey()
|
|
1247
|
+
const primaryKeyColumn = scalarModelPrimaryKey(modelClass.primaryKey(), `Derived sync attributes for ${resourceType}`)
|
|
1247
1248
|
|
|
1248
1249
|
localOnlyAttributes.push(columnNameToAttributeName[primaryKeyColumn] || primaryKeyColumn)
|
|
1249
1250
|
}
|
|
@@ -11,6 +11,7 @@ import sha256Hex from "../utils/sha256-hex.js"
|
|
|
11
11
|
import {decodeReplayPersistedData, serializeReplayPersistedData} from "./sync-replay-persisted-data.js"
|
|
12
12
|
import {ValidationError} from "../database/record/index.js"
|
|
13
13
|
import VelociousError from "../velocious-error.js"
|
|
14
|
+
import {scalarModelPrimaryKey, scalarModelPrimaryKeyValue} from "../utils/model-primary-key.js"
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Resolved routed-resource registration for one replay resource type.
|
|
@@ -800,7 +801,7 @@ export default class SyncEnvelopeReplayService {
|
|
|
800
801
|
if (mutation.baseVersion === undefined || mutation.baseVersion === null) return null
|
|
801
802
|
|
|
802
803
|
const ModelClass = resource.modelClass()
|
|
803
|
-
const primaryKey = ModelClass.primaryKey()
|
|
804
|
+
const primaryKey = scalarModelPrimaryKey(ModelClass.primaryKey(), `Offline sync conflict handling for ${ModelClass.name}`)
|
|
804
805
|
const primaryKeyAttribute = ModelClass.resolveAttributeName(primaryKey)
|
|
805
806
|
const versionAttribute = this.conflictStrategy.versionAttribute
|
|
806
807
|
const versionAttributeName = ModelClass.resolveAttributeName(versionAttribute)
|
|
@@ -944,7 +945,7 @@ export default class SyncEnvelopeReplayService {
|
|
|
944
945
|
if (columnName) allowedKeys.add(columnName)
|
|
945
946
|
}
|
|
946
947
|
|
|
947
|
-
const primaryKey = ModelClass.primaryKey()
|
|
948
|
+
const primaryKey = scalarModelPrimaryKey(ModelClass.primaryKey(), `Offline sync attribute filtering for ${ModelClass.name}`)
|
|
948
949
|
const primaryKeyAttribute = ModelClass.getColumnNameToAttributeNameMap()[primaryKey]
|
|
949
950
|
|
|
950
951
|
/** @type {Record<string, ReturnType<typeof JSON.parse>>} */
|
|
@@ -981,7 +982,7 @@ export default class SyncEnvelopeReplayService {
|
|
|
981
982
|
*/
|
|
982
983
|
async createRoutedReplayRecord({attributes, mutation, resource}) {
|
|
983
984
|
const ModelClass = resource.modelClass()
|
|
984
|
-
const primaryKey = ModelClass.primaryKey()
|
|
985
|
+
const primaryKey = scalarModelPrimaryKey(ModelClass.primaryKey(), `Offline sync create for ${ModelClass.name}`)
|
|
985
986
|
const conflictingIds = await ModelClass.where({[primaryKey]: mutation.resourceId}).pluck(primaryKey)
|
|
986
987
|
|
|
987
988
|
if (conflictingIds.length > 0) {
|
|
@@ -1007,7 +1008,7 @@ export default class SyncEnvelopeReplayService {
|
|
|
1007
1008
|
if (ability) {
|
|
1008
1009
|
const memberIds = await ModelClass
|
|
1009
1010
|
.accessibleFor(resource.syncAbilityAction("create"), ability)
|
|
1010
|
-
.where({[primaryKey]: record.id()})
|
|
1011
|
+
.where({[primaryKey]: scalarModelPrimaryKeyValue(record.id(), `Offline sync create authorization for ${ModelClass.name}`)})
|
|
1011
1012
|
.pluck(primaryKey)
|
|
1012
1013
|
|
|
1013
1014
|
if (memberIds.length === 0) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import Configuration from "../configuration.js"
|
|
4
4
|
import Logger from "../logger.js"
|
|
5
|
+
import {scalarModelPrimaryKeyValue} from "../utils/model-primary-key.js"
|
|
5
6
|
import restArgsError from "../utils/rest-args-error.js"
|
|
6
7
|
|
|
7
8
|
import {declaredSyncScopeAttributes} from "./sync-scope-attributes.js"
|
|
@@ -190,7 +191,7 @@ export default class SyncPublisher {
|
|
|
190
191
|
if (isPublishingSuppressed(record)) return
|
|
191
192
|
|
|
192
193
|
const data = await resourceConfig.serialize(record)
|
|
193
|
-
const resourceId = String(record.id())
|
|
194
|
+
const resourceId = String(scalarModelPrimaryKeyValue(record.id(), `Sync publishing for ${resourceConfig.resourceType}`))
|
|
194
195
|
const syncType = operation === "destroy" ? "delete" : "update"
|
|
195
196
|
const scopeValues = await this.publishedScopeValues({record, resourceConfig})
|
|
196
197
|
/** @type {Record<string, ReturnType<typeof JSON.parse>>} */
|
|
@@ -259,7 +260,7 @@ export default class SyncPublisher {
|
|
|
259
260
|
} else if (scopePlanEntry.recordAttribute) {
|
|
260
261
|
rawValue = record.readAttribute(scopePlanEntry.recordAttribute)
|
|
261
262
|
} else {
|
|
262
|
-
rawValue = record.id()
|
|
263
|
+
rawValue = scalarModelPrimaryKeyValue(record.id(), `Sync scope publishing for ${resourceConfig.resourceType}`)
|
|
263
264
|
}
|
|
264
265
|
|
|
265
266
|
const value = rawValue === undefined || rawValue === null ? null : String(rawValue)
|
|
@@ -15,10 +15,10 @@ import wait from "awaitery/build/wait.js"
|
|
|
15
15
|
* @typedef {import("../frontend-models/base.js").FrontendModelResourceConfig} FrontendModelResourceConfig */
|
|
16
16
|
/**
|
|
17
17
|
* Defines this typedef.
|
|
18
|
-
* @typedef {{id: string, model: FrontendModelBase}} FrontendModelHookTestCreateUpdatePayload */
|
|
18
|
+
* @typedef {{id: string | import("../utils/model-primary-key.js").CompositeModelPrimaryKeyValue, model: FrontendModelBase}} FrontendModelHookTestCreateUpdatePayload */
|
|
19
19
|
/**
|
|
20
20
|
* Defines this typedef.
|
|
21
|
-
* @typedef {{id: string}} FrontendModelHookTestDestroyPayload */
|
|
21
|
+
* @typedef {{id: string | import("../utils/model-primary-key.js").CompositeModelPrimaryKeyValue}} FrontendModelHookTestDestroyPayload */
|
|
22
22
|
/**
|
|
23
23
|
* FakeSubscriptions type.
|
|
24
24
|
* @typedef {object} FakeSubscriptions
|
|
@@ -172,7 +172,7 @@ export default class TestProfiler {
|
|
|
172
172
|
this._spans = []
|
|
173
173
|
/** @type {Array<{id: string, parentId: string | undefined, file: string, line: number | undefined}>} */
|
|
174
174
|
this._scopes = []
|
|
175
|
-
/** @type {WeakMap<import("./test-runner.js").TestsArgument, string>} */
|
|
175
|
+
/** @type {WeakMap<import("./test-runner.js").TestsArgument | import("@velocious/testing/runner").SuiteDeclaration, string>} */
|
|
176
176
|
this._scopeIds = new WeakMap()
|
|
177
177
|
/** @type {Set<string>} */
|
|
178
178
|
this._customActivityNames = new Set()
|
|
@@ -243,7 +243,7 @@ export default class TestProfiler {
|
|
|
243
243
|
|
|
244
244
|
/**
|
|
245
245
|
* Registers and returns a deterministic scope identifier.
|
|
246
|
-
* @param {import("./test-runner.js").TestsArgument} scope - Scope object.
|
|
246
|
+
* @param {import("./test-runner.js").TestsArgument | import("@velocious/testing/runner").SuiteDeclaration} scope - Scope object.
|
|
247
247
|
* @param {object} args - Scope metadata.
|
|
248
248
|
* @param {string[]} args.descriptions - Scope description path.
|
|
249
249
|
* @param {string | undefined} args.filePath - Scope source path.
|