@jskit-ai/http-web 0.1.91 → 0.1.93

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 (51) hide show
  1. package/package.json +6 -6
  2. package/patterns/crud-screen-set/PATTERN.md +11 -1
  3. package/src/client/bulkActions.js +13 -40
  4. package/src/client/components/CrudAddEditScreen.vue +1 -9
  5. package/src/client/components/CrudListBulkActionSurface.vue +2 -5
  6. package/src/client/components/CrudListFilterControls.vue +175 -0
  7. package/src/client/components/CrudListFilterSurface.vue +5 -248
  8. package/src/client/components/CrudListScreen.vue +1 -28
  9. package/src/client/components/CrudViewScreen.vue +1 -9
  10. package/src/client/composables/crud/crudJsonApiTransportSupport.js +20 -42
  11. package/src/client/composables/crud/crudLookupFieldLabelSupport.js +2 -13
  12. package/src/client/composables/crud/crudLookupFieldRuntime.js +2 -27
  13. package/src/client/composables/crud/crudSchemaFormHelpers.js +1 -7
  14. package/src/client/composables/internal/crudListFilterLookupSupport.js +2 -1
  15. package/src/client/composables/internal/useOperationScope.js +5 -7
  16. package/src/client/composables/records/useAddEdit.js +12 -11
  17. package/src/client/composables/records/useCrudAddEdit.js +74 -22
  18. package/src/client/composables/records/useCrudList.js +0 -6
  19. package/src/client/composables/records/useCrudView.js +0 -6
  20. package/src/client/composables/records/useList.js +2 -8
  21. package/src/client/composables/records/useView.js +3 -8
  22. package/src/client/composables/runtime/addEditUiRuntime.js +12 -60
  23. package/src/client/composables/runtime/listUiRuntime.js +11 -36
  24. package/src/client/composables/runtime/useFieldErrorBag.js +3 -3
  25. package/src/client/composables/runtime/viewUiRuntime.js +14 -62
  26. package/src/client/composables/support/errorMessageHelpers.js +6 -48
  27. package/src/client/composables/support/resourceLoadStateHelpers.js +67 -156
  28. package/src/client/composables/support/routeTemplateHelpers.js +57 -0
  29. package/src/client/composables/useAccess.js +12 -17
  30. package/src/client/composables/useCommand.js +0 -1
  31. package/src/client/composables/useCrudDeleteAction.js +7 -5
  32. package/src/client/composables/useCrudListBulkActions.js +9 -21
  33. package/src/client/composables/useCrudListFilterLookups.js +1 -3
  34. package/src/client/composables/useCrudListParentTitle.js +0 -2
  35. package/src/client/composables/useCrudListRowActions.js +8 -24
  36. package/src/client/composables/useCrudListScreen.js +4 -5
  37. package/src/client/composables/useCrudViewScreen.js +2 -3
  38. package/src/client/composables/useRealtimeQueryInvalidation.js +3 -10
  39. package/src/client/composables/useScopeRuntime.js +2 -5
  40. package/src/client/rowActions.js +13 -40
  41. package/src/client/support/crudListActionSupport.js +54 -0
  42. package/test/crudAddEditActions.test.js +59 -19
  43. package/test/crudListFilterSurface.test.js +7 -4
  44. package/test/listUiRuntime.test.js +3 -1
  45. package/test/requestTransportOptions.test.js +24 -0
  46. package/test/resourceLoadStateHelpers.test.js +32 -0
  47. package/test/useAccess.test.js +62 -0
  48. package/test/useCrudDeleteAction.test.js +57 -2
  49. package/test/useCrudListScreen.test.js +45 -0
  50. package/test/useListRouteInitialization.test.js +5 -44
  51. package/src/client/composables/runtime/operationAdapters.js +0 -34
@@ -93,14 +93,6 @@ const editLocation = computed(() => unref(props.screen?.editLocation) || null);
93
93
  </template>
94
94
 
95
95
  <style scoped>
96
- .crud-screen {
97
- --crud-screen-state-padding: 2.5rem 1.25rem;
98
- }
99
-
100
- .crud-screen--operator {
101
- --crud-screen-state-padding: 2rem 1rem;
102
- }
103
-
104
96
  .crud-view-actions {
105
97
  display: flex;
106
98
  flex-wrap: wrap;
@@ -115,7 +107,7 @@ const editLocation = computed(() => unref(props.screen?.editLocation) || null);
115
107
  .crud-view-state {
116
108
  margin-inline: auto;
117
109
  max-width: 30rem;
118
- padding: var(--crud-screen-state-padding);
110
+ padding: 2rem 1rem;
119
111
  text-align: center;
120
112
  }
121
113
 
@@ -1,9 +1,12 @@
1
- import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import { normalizeCrudLookupNamespace } from "@jskit-ai/resource-crud-core/shared/crudLookup";
3
3
 
4
- function isRecord(value) {
5
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
6
- }
4
+ const CRUD_RESPONSE_KIND_BY_MODE = Object.freeze({
5
+ list: "collection",
6
+ view: "record",
7
+ delete: "record",
8
+ "add-edit": "record"
9
+ });
7
10
 
8
11
  function resolveSchemaFieldDefinitions(definition = null) {
9
12
  const schema = definition?.schema;
@@ -73,41 +76,20 @@ function inferCrudJsonApiTransport(resource = null, { mode = "", operationName =
73
76
  }
74
77
 
75
78
  const normalizedMode = normalizeText(mode).toLowerCase();
76
- const normalizedOperationName = normalizeText(operationName).toLowerCase();
77
- if (normalizedMode === "list") {
78
- return Object.freeze({
79
- kind: "jsonapi-resource",
80
- responseType: resourceType,
81
- responseKind: "collection"
82
- });
83
- }
84
-
85
- if (normalizedMode === "view") {
86
- return Object.freeze({
87
- kind: "jsonapi-resource",
88
- responseType: resourceType,
89
- responseKind: "record"
90
- });
91
- }
92
-
93
- if (normalizedMode === "delete") {
94
- return Object.freeze({
95
- kind: "jsonapi-resource",
96
- responseType: resourceType,
97
- responseKind: "record"
98
- });
99
- }
100
-
101
- if (normalizedMode === "add-edit") {
102
- return Object.freeze({
103
- kind: "jsonapi-resource",
104
- ...(normalizedOperationName ? { requestType: resourceType } : {}),
105
- responseType: resourceType,
106
- responseKind: "record"
107
- });
79
+ const responseKind = Object.hasOwn(CRUD_RESPONSE_KIND_BY_MODE, normalizedMode)
80
+ ? CRUD_RESPONSE_KIND_BY_MODE[normalizedMode]
81
+ : "";
82
+ if (!responseKind) {
83
+ return null;
108
84
  }
109
85
 
110
- return null;
86
+ const hasRequestType = normalizedMode === "add-edit" && Boolean(normalizeText(operationName));
87
+ return Object.freeze({
88
+ kind: "jsonapi-resource",
89
+ ...(hasRequestType ? { requestType: resourceType } : {}),
90
+ responseType: resourceType,
91
+ responseKind
92
+ });
111
93
  }
112
94
 
113
95
  function resolveCrudJsonApiTransport(transport = null, resource = null, options = {}) {
@@ -126,14 +108,10 @@ function resolveCrudJsonApiTransport(transport = null, resource = null, options
126
108
  const lookupContainerKey = normalizeText(resource?.contract?.lookup?.containerKey, {
127
109
  fallback: "lookups"
128
110
  });
129
- if (Object.keys(lookupFieldMap).length < 1 && !lookupContainerKey) {
130
- return baseTransport;
131
- }
132
-
133
111
  return Object.freeze({
134
112
  ...baseTransport,
135
113
  ...(Object.keys(lookupFieldMap).length > 0 ? { lookupFieldMap } : {}),
136
- ...(lookupContainerKey ? { lookupContainerKey } : {})
114
+ lookupContainerKey
137
115
  });
138
116
  }
139
117
 
@@ -1,4 +1,4 @@
1
- import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { hasValue, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import { normalizeCrudLookupContainerKey } from "@jskit-ai/resource-crud-core/shared/crudLookup";
3
3
  import { asPlainObject } from "../support/scopeHelpers.js";
4
4
 
@@ -12,17 +12,6 @@ const LOOKUP_LABEL_COMPOSITION_CANDIDATES = Object.freeze([
12
12
  ]);
13
13
  const DEFAULT_RECORD_TITLE = "-";
14
14
 
15
- function hasDisplayValue(value) {
16
- if (value == null) {
17
- return false;
18
- }
19
- if (typeof value === "string") {
20
- return normalizeText(value).length > 0;
21
- }
22
-
23
- return true;
24
- }
25
-
26
15
  function resolveComposedLabel(source = {}, candidates = LOOKUP_LABEL_COMPOSITION_CANDIDATES) {
27
16
  for (const candidate of candidates) {
28
17
  const parts = [];
@@ -137,7 +126,7 @@ function resolveLookupFieldDisplayValue(record = {}, field = {}, relationKind =
137
126
  }
138
127
 
139
128
  const lookupValue = lookupRecord[descriptor.relation.valueKey];
140
- if (hasDisplayValue(lookupValue)) {
129
+ if (hasValue(lookupValue)) {
141
130
  return lookupValue;
142
131
  }
143
132
 
@@ -21,14 +21,6 @@ function normalizeQueryKeyPrefix(value) {
21
21
  .filter(Boolean);
22
22
  }
23
23
 
24
- function isSameLookupValue(left, right) {
25
- if (left === right) {
26
- return true;
27
- }
28
-
29
- return String(left ?? "") === String(right ?? "");
30
- }
31
-
32
24
  function normalizeLookupValue(value) {
33
25
  if (value == null) {
34
26
  return "";
@@ -75,7 +67,6 @@ function createSelectedLookupItem(selectedValue, selectedRecord = {}, entry = {}
75
67
 
76
68
  function createCrudLookupFieldRuntime({
77
69
  formFields = [],
78
- adapter = null,
79
70
  recordIdParam = "recordId",
80
71
  queryKeyPrefix = [],
81
72
  placementSourcePrefix = "",
@@ -121,7 +112,6 @@ function createCrudLookupFieldRuntime({
121
112
  }
122
113
 
123
114
  const runtime = useList({
124
- adapter: adapter || undefined,
125
115
  ...(relationSurfaceId ? { surfaceId: relationSurfaceId } : {}),
126
116
  apiSuffix: apiPath,
127
117
  ...(transport ? { transport } : {}),
@@ -192,22 +182,7 @@ function createCrudLookupFieldRuntime({
192
182
  return items;
193
183
  }
194
184
 
195
- if (items.some((item) => item?.value === selectedItem.value)) {
196
- return items;
197
- }
198
-
199
- const matchingItem = items.find((item) => isSameLookupValue(item?.value, selectedItem.value));
200
- if (matchingItem) {
201
- return [
202
- {
203
- ...selectedItem,
204
- label: String(matchingItem.label ?? selectedItem.label ?? "")
205
- },
206
- ...items
207
- ];
208
- }
209
-
210
- return [selectedItem, ...items];
185
+ return items.some((item) => item.value === selectedItem.value) ? items : [selectedItem, ...items];
211
186
  }
212
187
 
213
188
  function resolveLookupLoading(fieldKey = "") {
@@ -217,7 +192,7 @@ function createCrudLookupFieldRuntime({
217
192
  return false;
218
193
  }
219
194
 
220
- return Boolean(entry.runtime.isInitialLoading || entry.runtime.isFetching || entry.runtime.isRefetching);
195
+ return Boolean(entry.runtime.isInitialLoading || entry.runtime.isFetching);
221
196
  }
222
197
 
223
198
  function resolveLookupSearch(fieldKey = "") {
@@ -443,13 +443,7 @@ function resolveCrudFieldErrors(fieldErrors = {}, fieldKey = "") {
443
443
  return [];
444
444
  }
445
445
 
446
- const source = asPlainObject(fieldErrors);
447
- const message = String(source[key] || "").trim();
448
-
449
- if (!message) {
450
- return resolveStableFieldErrorList(key, "");
451
- }
452
-
446
+ const message = String(asPlainObject(fieldErrors)[key] || "").trim();
453
447
  return resolveStableFieldErrorList(key, message);
454
448
  }
455
449
 
@@ -1,4 +1,5 @@
1
1
  import { normalizeText, normalizeUniqueTextList } from "@jskit-ai/kernel/shared/support/normalize";
2
+ import { CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY } from "@jskit-ai/resource-crud-core/shared/crudListFilters";
2
3
  import { resolveLookupItemLabel } from "../crud/crudLookupFieldLabelSupport.js";
3
4
  import { asPlainObject } from "../support/scopeHelpers.js";
4
5
 
@@ -58,7 +59,7 @@ function normalizeLookupRequestQueryParamsMap(value = {}) {
58
59
  }
59
60
 
60
61
  function resolveLookupSelectedValues(filter = {}, rawValue = undefined) {
61
- if (filter?.type === "recordIdMany") {
62
+ if (filter?.type === CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY) {
62
63
  return normalizeUniqueTextList(rawValue, {
63
64
  acceptSingle: true
64
65
  });
@@ -36,7 +36,6 @@ function useOperationScope({
36
36
  ownershipFilter = ROUTE_VISIBILITY_WORKSPACE,
37
37
  surfaceId = "",
38
38
  access = "auto",
39
- placementSource = "http-web.operation",
40
39
  apiSuffix = "",
41
40
  model,
42
41
  readEnabled = true,
@@ -49,8 +48,7 @@ function useOperationScope({
49
48
  ownershipFilter,
50
49
  surfaceId,
51
50
  accessMode: access,
52
- hasPermissionRequirements: hasAnyPermissions(normalizedPermissionSets),
53
- placementSource
51
+ hasPermissionRequirements: hasAnyPermissions(normalizedPermissionSets)
54
52
  });
55
53
  const normalizedOwnershipFilter = scopeRuntime.normalizedOwnershipFilter;
56
54
  const routeContext = scopeRuntime.routeContext;
@@ -106,9 +104,9 @@ function useOperationScope({
106
104
  return scopeRuntime.routeScopeError.value;
107
105
  }
108
106
 
109
- const bootstrapError = String(accessRuntime.bootstrapError.value || "").trim();
110
- if (bootstrapError) {
111
- return bootstrapError;
107
+ const permissionsError = String(accessRuntime.permissionsUnavailableError.value || "").trim();
108
+ if (permissionsError) {
109
+ return permissionsError;
112
110
  }
113
111
 
114
112
  if (baseError === undefined || baseError === null || baseError === "") {
@@ -120,7 +118,7 @@ function useOperationScope({
120
118
  }
121
119
 
122
120
  function isLoading(baseLoading = false) {
123
- return computed(() => Boolean(unref(baseLoading)) || accessRuntime.isBootstrapping.value);
121
+ return computed(() => Boolean(unref(baseLoading)) || accessRuntime.awaitingPlacementPermissions.value);
124
122
  }
125
123
 
126
124
  return Object.freeze({
@@ -3,7 +3,7 @@ import { useRoute } from "vue-router";
3
3
  import { ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/kernel/shared/support/visibility";
4
4
  import { useAddEditCore } from "../runtime/useAddEditCore.js";
5
5
  import { useEndpointResource } from "../runtime/useEndpointResource.js";
6
- import { resolveOperationAdapter } from "../runtime/operationAdapters.js";
6
+ import { useOperationScope } from "../internal/useOperationScope.js";
7
7
  import { createAddEditUiRuntime } from "../runtime/addEditUiRuntime.js";
8
8
  import { useUiFeedback } from "../runtime/useUiFeedback.js";
9
9
  import { useFieldErrorBag } from "../runtime/useFieldErrorBag.js";
@@ -35,7 +35,7 @@ function useAddEdit({
35
35
  requestRecoveryLabel = "Resource",
36
36
  placementSource = "http-web.add-edit",
37
37
  fallbackLoadError = "Unable to load resource.",
38
- fallbackSaveError = "Unable to save resource.",
38
+ fallbackSaveError = "",
39
39
  fieldErrorKeys = [],
40
40
  clearOnRouteChange = true,
41
41
  model,
@@ -54,8 +54,7 @@ function useAddEdit({
54
54
  listUrlTemplate = "",
55
55
  saveRecordIdSelector = null,
56
56
  messages = {},
57
- realtime = null,
58
- adapter = null
57
+ realtime = null
59
58
  } = {}) {
60
59
  const route = useRoute();
61
60
  const addEditUiRuntime = createAddEditUiRuntime({
@@ -71,14 +70,10 @@ function useAddEdit({
71
70
  });
72
71
  const normalizedApiUrlTemplate = String(apiUrlTemplate || "").trim();
73
72
  const effectiveApiSuffix = normalizedApiUrlTemplate ? addEditUiRuntime.apiSuffix : apiSuffix;
74
- const operationAdapter = resolveOperationAdapter(adapter, {
75
- context: "useAddEdit adapter"
76
- });
77
- const operationScope = operationAdapter.useOperationScope({
73
+ const operationScope = useOperationScope({
78
74
  ownershipFilter,
79
75
  surfaceId,
80
76
  access,
81
- placementSource,
82
77
  apiSuffix: effectiveApiSuffix,
83
78
  model,
84
79
  readEnabled,
@@ -90,7 +85,7 @@ function useAddEdit({
90
85
  realtime
91
86
  });
92
87
  const routeContext = operationScope.routeContext;
93
- const effectiveMessages = {
88
+ const resolvedMessages = {
94
89
  ...resolveResourceMessages(resource, {
95
90
  validation: "Fix invalid values and try again.",
96
91
  saveSuccess: "Saved.",
@@ -98,6 +93,12 @@ function useAddEdit({
98
93
  }),
99
94
  ...(messages && typeof messages === "object" ? messages : {})
100
95
  };
96
+ // One save-error string for the endpoint and its feedback: an explicit
97
+ // fallbackSaveError, otherwise the resolved resource/messages saveError.
98
+ const effectiveMessages = {
99
+ ...resolvedMessages,
100
+ saveError: String(fallbackSaveError || "").trim() || String(resolvedMessages.saveError || "").trim() || "Unable to save."
101
+ };
101
102
 
102
103
  const canView = operationScope.permissionGate("view");
103
104
  const canSave = operationScope.permissionGate("save");
@@ -129,7 +130,7 @@ function useAddEdit({
129
130
  requestRecovery,
130
131
  requestRecoveryLabel,
131
132
  fallbackLoadError,
132
- fallbackSaveError: String(fallbackSaveError || effectiveMessages.saveError || "Unable to save resource.")
133
+ fallbackSaveError: effectiveMessages.saveError
133
134
  });
134
135
 
135
136
  const feedback = useUiFeedback({
@@ -4,6 +4,7 @@ import { asPlainObject } from "../support/scopeHelpers.js";
4
4
  import { resolveCrudJsonApiTransport } from "../crud/crudJsonApiTransportSupport.js";
5
5
  import { resolveCrudHttpClient } from "../crud/crudHttpClientSupport.js";
6
6
  import { useAddEdit } from "./useAddEdit.js";
7
+ import { normalizeFieldErrorKeys } from "../runtime/useFieldErrorBag.js";
7
8
  import {
8
9
  resolveCrudBoundValues,
9
10
  } from "../crud/crudBindingSupport.js";
@@ -18,10 +19,68 @@ import {
18
19
  } from "../crud/crudSchemaFormHelpers.js";
19
20
  import { hasResolvedQueryData } from "../support/resourceLoadStateHelpers.js";
20
21
 
21
- function normalizeFieldErrorKeys(keys = []) {
22
- return Array.isArray(keys)
23
- ? keys.map((entry) => String(entry || "").trim()).filter(Boolean)
24
- : [];
22
+ // addEditOptions that pass unchanged to useAddEdit. client, transport, realtime,
23
+ // fieldErrorKeys, model and onSaveSuccess are read here first. Resource, input,
24
+ // payload building and model mapping have one home each: the top-level options.
25
+ const PASSED_ADD_EDIT_OPTIONS = Object.freeze([
26
+ "ownershipFilter",
27
+ "surfaceId",
28
+ "access",
29
+ "apiSuffix",
30
+ "queryKeyFactory",
31
+ "viewPermissions",
32
+ "savePermissions",
33
+ "readMethod",
34
+ "readEnabled",
35
+ "writeMethod",
36
+ "requestRecovery",
37
+ "requestRecoveryLabel",
38
+ "placementSource",
39
+ "fallbackLoadError",
40
+ "fallbackSaveError",
41
+ "clearOnRouteChange",
42
+ "buildSavePayload",
43
+ "validationFeedback",
44
+ "requestQueryParams",
45
+ "recordIdParam",
46
+ "routeParams",
47
+ "routeRecordId",
48
+ "apiUrlTemplate",
49
+ "viewUrlTemplate",
50
+ "listUrlTemplate",
51
+ "saveRecordIdSelector",
52
+ "messages"
53
+ ]);
54
+ const CONSUMED_ADD_EDIT_OPTIONS = Object.freeze([
55
+ "client",
56
+ "transport",
57
+ "realtime",
58
+ "fieldErrorKeys",
59
+ "model",
60
+ "onSaveSuccess"
61
+ ]);
62
+ const TOP_LEVEL_ADD_EDIT_OPTIONS = Object.freeze({
63
+ resource: "resource",
64
+ input: "input",
65
+ buildRawPayload: "buildPayload",
66
+ mapLoadedToModel: "mapPayloadToModel"
67
+ });
68
+
69
+ function resolvePassedAddEditOptions(addEditOptions = {}) {
70
+ const passed = {};
71
+ for (const [key, value] of Object.entries(addEditOptions)) {
72
+ if (Object.hasOwn(TOP_LEVEL_ADD_EDIT_OPTIONS, key)) {
73
+ throw new TypeError(
74
+ `useCrudAddEdit takes ${TOP_LEVEL_ADD_EDIT_OPTIONS[key]} as a top-level option, not addEditOptions.${key}.`
75
+ );
76
+ }
77
+ if (PASSED_ADD_EDIT_OPTIONS.includes(key)) {
78
+ passed[key] = value;
79
+ } else if (!CONSUMED_ADD_EDIT_OPTIONS.includes(key)) {
80
+ throw new TypeError(`useCrudAddEdit does not support addEditOptions.${key}.`);
81
+ }
82
+ }
83
+ return passed;
25
84
  }
26
85
 
27
86
  function normalizeSaveSuccessOptions(options = {}) {
@@ -57,10 +116,10 @@ function useCrudAddEdit({
57
116
  const route = useRoute();
58
117
  const normalizedFields = normalizeCrudFormFields(formFields);
59
118
  const normalizedAddEditOptions = asPlainObject(addEditOptions);
60
- const resolvedResource = normalizedAddEditOptions.resource || resource;
119
+ const passedAddEditOptions = resolvePassedAddEditOptions(normalizedAddEditOptions);
61
120
  const resolvedTransport = resolveCrudJsonApiTransport(
62
121
  normalizedAddEditOptions.transport,
63
- resolvedResource,
122
+ resource,
64
123
  {
65
124
  mode: "add-edit",
66
125
  operationName
@@ -68,8 +127,8 @@ function useCrudAddEdit({
68
127
  );
69
128
  const resolvedRealtime = resolveOperationRealtimeOptions({
70
129
  realtime: normalizedAddEditOptions.realtime,
71
- fallbackRealtime: resolvedResource?.operations?.[operationName]?.realtime ||
72
- resolvedResource?.operations?.list?.realtime ||
130
+ fallbackRealtime: resource?.operations?.[operationName]?.realtime ||
131
+ resource?.operations?.list?.realtime ||
73
132
  null
74
133
  });
75
134
  const saveSuccessOptions = normalizeSaveSuccessOptions(saveSuccess);
@@ -108,18 +167,13 @@ function useCrudAddEdit({
108
167
  deep: true
109
168
  }
110
169
  );
111
- const inputOverride = input || normalizedAddEditOptions.input || null;
112
- const buildPayloadOverride = typeof buildPayload === "function"
113
- ? buildPayload
114
- : (typeof normalizedAddEditOptions.buildRawPayload === "function" ? normalizedAddEditOptions.buildRawPayload : null);
115
- const mapPayloadToModelOverride = typeof mapPayloadToModel === "function"
116
- ? mapPayloadToModel
117
- : (typeof normalizedAddEditOptions.mapLoadedToModel === "function" ? normalizedAddEditOptions.mapLoadedToModel : null);
170
+ const buildPayloadOverride = typeof buildPayload === "function" ? buildPayload : null;
171
+ const mapPayloadToModelOverride = typeof mapPayloadToModel === "function" ? mapPayloadToModel : null;
118
172
  const onSaveSuccessOverride = typeof normalizedAddEditOptions.onSaveSuccess === "function"
119
173
  ? normalizedAddEditOptions.onSaveSuccess
120
174
  : null;
121
175
  const shouldApplyDefaultMapPayload = normalizedAddEditOptions.readEnabled !== false;
122
- const resolvedInput = inputOverride || resolvedResource?.operations?.[operationName]?.body || null;
176
+ const resolvedInput = input || resource?.operations?.[operationName]?.body || null;
123
177
 
124
178
  function resolveBuildRawPayload(model = {}, context = {}) {
125
179
  const payload = buildPayloadOverride
@@ -176,18 +230,16 @@ function useCrudAddEdit({
176
230
  return;
177
231
  }
178
232
 
179
- const listUrlTemplate = saveSuccessOptions.listUrlTemplate ||
180
- String(normalizedAddEditOptions.listUrlTemplate || "").trim();
181
- const listUrl = addEditRuntime?.resolveParams(listUrlTemplate);
233
+ const listUrl = addEditRuntime?.resolveParams(saveSuccessOptions.listUrlTemplate);
182
234
  if (listUrl) {
183
235
  await router.push(listUrl);
184
236
  }
185
237
  }
186
238
 
187
239
  const addEdit = useAddEdit({
188
- ...normalizedAddEditOptions,
189
- resource: resolvedResource,
190
- client: resolveCrudHttpClient(resolvedResource, {
240
+ ...passedAddEditOptions,
241
+ resource,
242
+ client: resolveCrudHttpClient(resource, {
191
243
  client: normalizedAddEditOptions.client
192
244
  }),
193
245
  transport: resolvedTransport,
@@ -2,10 +2,6 @@ import { computed, unref } from "vue";
2
2
  import { useRoute } from "vue-router";
3
3
  import { resolveCrudJsonApiTransport } from "../crud/crudJsonApiTransportSupport.js";
4
4
  import { resolveCrudHttpClient } from "../crud/crudHttpClientSupport.js";
5
- import {
6
- resolveLookupFieldDisplayValue,
7
- resolveRecordTitle
8
- } from "../crud/crudLookupFieldLabelSupport.js";
9
5
  import { resolveCrudBoundValues } from "../crud/crudBindingSupport.js";
10
6
  import { resolveCrudListParentDescriptor } from "../internal/crudListParentTitleSupport.js";
11
7
  import {
@@ -93,8 +89,6 @@ function useCrudList({
93
89
  }
94
90
  });
95
91
 
96
- records.resolveFieldDisplay = resolveLookupFieldDisplayValue;
97
- records.resolveRecordTitle = resolveRecordTitle;
98
92
  return records;
99
93
  }
100
94
 
@@ -4,10 +4,6 @@ import {
4
4
  resolveCrudJsonApiTransport
5
5
  } from "../crud/crudJsonApiTransportSupport.js";
6
6
  import { resolveCrudHttpClient } from "../crud/crudHttpClientSupport.js";
7
- import {
8
- resolveLookupFieldDisplayValue,
9
- resolveRecordTitle
10
- } from "../crud/crudLookupFieldLabelSupport.js";
11
7
  import { resolveCrudBoundValues } from "../crud/crudBindingSupport.js";
12
8
  import { asPlainObject } from "../support/scopeHelpers.js";
13
9
  import { useView } from "./useView.js";
@@ -39,8 +35,6 @@ function useCrudView({
39
35
  }),
40
36
  routeParams: boundRouteParams
41
37
  });
42
- view.resolveFieldDisplay = resolveLookupFieldDisplayValue;
43
- view.resolveRecordTitle = resolveRecordTitle;
44
38
  return view;
45
39
  }
46
40
 
@@ -3,7 +3,7 @@ import { useRouter } from "vue-router";
3
3
  import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
4
4
  import { ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/kernel/shared/support/visibility";
5
5
  import { useListCore } from "../runtime/useListCore.js";
6
- import { resolveOperationAdapter } from "../runtime/operationAdapters.js";
6
+ import { useOperationScope } from "../internal/useOperationScope.js";
7
7
  import { setupOperationErrorReporting } from "../runtime/operationUiHelpers.js";
8
8
  import { createListUiRuntime } from "../runtime/listUiRuntime.js";
9
9
  import { asPlainObject } from "../support/scopeHelpers.js";
@@ -53,7 +53,6 @@ function useList({
53
53
  requestRecovery,
54
54
  requestRecoveryLabel = "List",
55
55
  realtime = null,
56
- adapter = null,
57
56
  recordIdParam = "recordId",
58
57
  recordIdSelector = null,
59
58
  viewUrlTemplate = "",
@@ -104,14 +103,10 @@ function useList({
104
103
  searchDebounceTimer = null;
105
104
  });
106
105
 
107
- const operationAdapter = resolveOperationAdapter(adapter, {
108
- context: "useList adapter"
109
- });
110
- const operationScope = operationAdapter.useOperationScope({
106
+ const operationScope = useOperationScope({
111
107
  ownershipFilter,
112
108
  surfaceId,
113
109
  access,
114
- placementSource,
115
110
  apiSuffix,
116
111
  readEnabled,
117
112
  queryKeyFactory,
@@ -481,7 +476,6 @@ function useList({
481
476
  loadMore: list.loadMore,
482
477
  hasViewUrl: listUiRuntime.hasViewUrl,
483
478
  hasEditUrl: listUiRuntime.hasEditUrl,
484
- actionColumnCount: listUiRuntime.actionColumnCount,
485
479
  showListSkeleton: listUiRuntime.showListSkeleton,
486
480
  resolveRowKey: listUiRuntime.resolveRowKey,
487
481
  resolveParams: listUiRuntime.resolveParams,
@@ -3,7 +3,7 @@ import { useRoute } from "vue-router";
3
3
  import { ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/kernel/shared/support/visibility";
4
4
  import { useViewCore } from "../runtime/useViewCore.js";
5
5
  import { useEndpointResource } from "../runtime/useEndpointResource.js";
6
- import { resolveOperationAdapter } from "../runtime/operationAdapters.js";
6
+ import { useOperationScope } from "../internal/useOperationScope.js";
7
7
  import { setupOperationErrorReporting } from "../runtime/operationUiHelpers.js";
8
8
  import { createViewUiRuntime } from "../runtime/viewUiRuntime.js";
9
9
  import { createRequestQueryRuntime } from "../support/requestQueryRuntimeSupport.js";
@@ -39,8 +39,7 @@ function useView({
39
39
  listUrlTemplate = "",
40
40
  editUrlTemplate = "",
41
41
  includeRecordIdInQueryKey = false,
42
- realtime = undefined,
43
- adapter = null
42
+ realtime = undefined
44
43
  } = {}) {
45
44
  const route = useRoute();
46
45
  const viewUiRuntime = createViewUiRuntime({
@@ -55,14 +54,10 @@ function useView({
55
54
  });
56
55
  const normalizedApiUrlTemplate = String(apiUrlTemplate || "").trim();
57
56
  const effectiveApiSuffix = normalizedApiUrlTemplate ? viewUiRuntime.apiSuffix : apiSuffix;
58
- const operationAdapter = resolveOperationAdapter(adapter, {
59
- context: "useView adapter"
60
- });
61
- const operationScope = operationAdapter.useOperationScope({
57
+ const operationScope = useOperationScope({
62
58
  ownershipFilter,
63
59
  surfaceId,
64
60
  access,
65
- placementSource,
66
61
  apiSuffix: effectiveApiSuffix,
67
62
  readEnabled,
68
63
  queryKeyFactory,