n8n-nodes-lifespace 0.1.7 → 0.1.8
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 +51 -30
- package/dist/nodes/LifeSpace/LifeSpace.node.d.ts +1 -0
- package/dist/nodes/LifeSpace/LifeSpace.node.js +109 -64
- package/dist/nodes/LifeSpace/LifeSpace.node.js.map +1 -1
- package/dist/nodes/LifeSpaceAgentTool/LifeSpaceAgentTool.node.d.ts +6 -0
- package/dist/nodes/LifeSpaceAgentTool/LifeSpaceAgentTool.node.js +99 -0
- package/dist/nodes/LifeSpaceAgentTool/LifeSpaceAgentTool.node.js.map +1 -0
- package/dist/nodes/LifeSpaceAgentTool/lifespace.dark.svg +14 -0
- package/dist/nodes/LifeSpaceAgentTool/lifespace.svg +14 -0
- package/dist/nodes/LifeSpaceWorkflow/LifeSpaceWorkflow.node.d.ts +6 -0
- package/dist/nodes/LifeSpaceWorkflow/LifeSpaceWorkflow.node.js +139 -0
- package/dist/nodes/LifeSpaceWorkflow/LifeSpaceWorkflow.node.js.map +1 -0
- package/dist/nodes/LifeSpaceWorkflow/humanProjection.d.ts +10 -0
- package/dist/nodes/LifeSpaceWorkflow/humanProjection.js +167 -0
- package/dist/nodes/LifeSpaceWorkflow/humanProjection.js.map +1 -0
- package/dist/nodes/LifeSpaceWorkflow/lifespace.dark.svg +14 -0
- package/dist/nodes/LifeSpaceWorkflow/lifespace.svg +14 -0
- package/dist/nodes/agent/LifeSpaceAgentToolBase.d.ts +1 -0
- package/dist/nodes/agent/LifeSpaceAgentToolBase.js +6 -0
- package/dist/nodes/agent/LifeSpaceAgentToolBase.js.map +1 -0
- package/dist/nodes/agent/lifeSpaceGenericQueryTool.d.ts +24 -0
- package/dist/nodes/agent/lifeSpaceGenericQueryTool.js +197 -0
- package/dist/nodes/agent/lifeSpaceGenericQueryTool.js.map +1 -0
- package/dist/nodes/agent/lifeSpaceToolTypes.d.ts +10 -0
- package/dist/nodes/agent/lifeSpaceToolTypes.js +3 -0
- package/dist/nodes/agent/lifeSpaceToolTypes.js.map +1 -0
- package/dist/nodes/shared/lifeSpaceAdapterSemantics.d.ts +8 -0
- package/dist/nodes/shared/lifeSpaceAdapterSemantics.js +28 -0
- package/dist/nodes/shared/lifeSpaceAdapterSemantics.js.map +1 -0
- package/dist/nodes/shared/lifeSpaceLocalDateSemantics.d.ts +9 -0
- package/dist/nodes/shared/lifeSpaceLocalDateSemantics.js +23 -0
- package/dist/nodes/shared/lifeSpaceLocalDateSemantics.js.map +1 -0
- package/dist/nodes/shared/lifeSpaceQuerySemantics.d.ts +14 -0
- package/dist/nodes/shared/lifeSpaceQuerySemantics.js +120 -0
- package/dist/nodes/shared/lifeSpaceQuerySemantics.js.map +1 -0
- package/dist/package.json +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -66,7 +66,12 @@ Runtime Discovery determines which Spaces, Record Types, fields, queries, Action
|
|
|
66
66
|
|
|
67
67
|
## Generated Record UX
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
The package deliberately exposes two different projections over the same LifeSpace Runtime Discovery semantics:
|
|
70
|
+
|
|
71
|
+
- **LifeSpace** is the human-authored workflow node. Create/Update fields are generated through n8n Resource Mapper from the selected Record Type, so field type, required state, enum values, calendar dates and supported single relations determine the control automatically. List / Query exposes semantic predicates such as `Status — Is One Of` or `Due Date — After or Equal` instead of asking the workflow author to choose a filter type first. Multi-value relations keep their dedicated multi-select UX.
|
|
72
|
+
- **LifeSpace Agent Tool** is the native AI Tool surface. Its schema is generated for the model: generic queries expose semantic `field` / `operator` / `value` inputs, and the adapter compiles those inputs to the exact transport names published by LifeSpace. Capability Queries consume the grouped schema published by Runtime Discovery.
|
|
73
|
+
|
|
74
|
+
The human workflow node is not exposed through `usableAsTool`; this avoids maintaining two competing Agent Tool surfaces with different schema behavior.
|
|
70
75
|
|
|
71
76
|
The node displays the authorized human-readable `spaceName` when present while continuing to submit the stable `spc_*` ID.
|
|
72
77
|
|
|
@@ -74,16 +79,6 @@ Record Type is the LifeSpace `modelKey` (for example `task`). Design-time option
|
|
|
74
79
|
|
|
75
80
|
Calendar-backed models use canonical `capabilityBindings.calendar` field roles instead of Event-specific field names while the node is being configured. Date-only values are normalized to `YYYY-MM-DD`. Create/Update execution does not fetch fresh semantic Discovery solely to produce an adapter-local Calendar conflict error; the canonical mutation goes directly to LifeSpace Core, which remains authoritative for Calendar validation and current authorization.
|
|
76
81
|
|
|
77
|
-
## AI Agent Tool
|
|
78
|
-
|
|
79
|
-
`LifeSpace Tool` is the metadata-driven AI Agent sub-node. Configure one fixed Space, Record Type and semantic operation per Tool instance, then connect multiple instances to the same n8n AI Agent. The Tool name, default description and structured AI input schema are generated from LifeSpace Progressive Runtime Discovery; hand-written descriptions are optional.
|
|
80
|
-
|
|
81
|
-
- Query exposes published search/filter/comparison/local-date-window/sort/pagination semantics. Capability Query mode exposes only the capability-owned parameters and ordering that LifeSpace explicitly publishes; generic-facet composition stays narrowed until LifeSpace #228 defines it.
|
|
82
|
-
- Create/Update schemas come from writable Model fields. Required fields with LifeSpace defaults are optional AI inputs, and fields the AI omits are absent from the outgoing request rather than synthesized as empty/null placeholders.
|
|
83
|
-
- Delete and Action hide optimistic-concurrency `version` from the AI. The Adapter reads the current record version when required and Core remains the final authorization/concurrency/semantic authority.
|
|
84
|
-
- No Task/Event/Wish-specific Tool nodes or field maps are shipped. New models become available through Discovery without source changes.
|
|
85
|
-
- The ordinary `LifeSpace` workflow node remains `usableAsTool` for manually configured `$fromAI(...)` workflows, but `LifeSpace Tool` is the canonical path when the AI should receive the complete dynamic LifeSpace schema.
|
|
86
|
-
|
|
87
82
|
## LifeSpace contract compatibility
|
|
88
83
|
|
|
89
84
|
This package follows the current LifeSpace Core Kernel `0.35.0` contract family. It consumes Runtime/Discovery semantics only; Eventing configuration and webhook delivery semantics are owned independently by Integration/Eventing `0.1.0`.
|
|
@@ -140,16 +135,16 @@ Examples:
|
|
|
140
135
|
{{$vars.lifeSpaceRecordType}}
|
|
141
136
|
```
|
|
142
137
|
|
|
143
|
-
Discovery-backed selectors such as **Space**, **
|
|
138
|
+
Discovery-backed selectors such as **Space**, **Sort Field** and **Action** support the normal n8n pattern: choose a value from the list, or switch the parameter to an expression and provide the corresponding stable ID/key. **Record Type** stores the plain LifeSpace `modelKey`, so a LifeSpace Trigger can feed a Record node directly without a mapping step or an extra Discovery request.
|
|
144
139
|
|
|
145
|
-
The same applies to ordinary values such as Record ID, Search,
|
|
140
|
+
The same applies to ordinary values such as Record ID, Search, Return All, Limit, Sort Direction, Cursor, explicit Version, API Method, API Path and JSON Body.
|
|
146
141
|
|
|
147
142
|
Two boundaries are intentional:
|
|
148
143
|
|
|
149
144
|
- **Resource** and **Operation** are structural node controls and do not accept expressions because they determine which parameter schema and execution path the node has.
|
|
150
|
-
- **Fields** and **Action Input**
|
|
145
|
+
- **Fields**, **Filters**, Capability Query parameters and **Action Input** are generated from Runtime Discovery. Their mapper containers are structural, while generated values remain expression-capable.
|
|
151
146
|
|
|
152
|
-
|
|
147
|
+
In Standard Query, **Filters** is a Discovery-driven Resource Mapper. Adding a filter selects a published semantic predicate such as `Due Date — Before`; n8n then renders the value control from the underlying field type. **Sorts** remains an ordered structural list because sort priority is part of workflow structure rather than per-item data.
|
|
153
148
|
|
|
154
149
|
If **Record Type** itself varies per input item and those Record Types have different schemas, one discovery-generated mapper cannot safely represent every possible schema at design time. In that case, branch to separate LifeSpace nodes per schema or use **API Request** for a deliberately fully dynamic request.
|
|
155
150
|
|
|
@@ -188,7 +183,7 @@ LifeSpace server defaults are authoritative. A field that is `required` but has
|
|
|
188
183
|
|
|
189
184
|
For example, lifecycle state such as Task status can remain server/Action-owned instead of being manually entered by the workflow author.
|
|
190
185
|
|
|
191
|
-
When Runtime Discovery advertises relation semantics, supported `person
|
|
186
|
+
When Runtime Discovery advertises relation semantics, supported single-value `person` / `record` fields are rendered as selectors from the current authorized `{ id, label }` target projection. Multi-value relations retain the dedicated multi-select control rather than falling back to n8n Resource Mapper's generic JSON array editor. The displayed value is the canonical LifeSpace reference label, while the workflow payload still stores and submits the stable target ID.
|
|
192
187
|
|
|
193
188
|
Older compatible Discovery responses without relation lookup metadata retain the raw-ID field behavior. The adapter never guesses record labels from conventional fields such as `name`, `title` or `summary`.
|
|
194
189
|
|
|
@@ -208,19 +203,18 @@ If a workflow intentionally needs to bind a known version, add **Concurrency Opt
|
|
|
208
203
|
|
|
209
204
|
### List / Query
|
|
210
205
|
|
|
211
|
-
|
|
206
|
+
**Query Type** separates ordinary record query UX from capability-owned query semantics:
|
|
207
|
+
|
|
208
|
+
- **Records** is the normal query surface and exposes optional **Search**, Discovery-driven semantic **Filters**, optional **Advanced Local Date Windows**, generic **Sorts**, **Return All** and **Limit**;
|
|
209
|
+
- each Filter entry represents an allowed field/operator pair published by LifeSpace, for example `Status — Is One Of`, `Due Date — Before` or `Created At — After or Equal`; the adapter compiles the selected semantic predicate to the exact transport parameter published by Discovery;
|
|
210
|
+
- **Capability Query** is offered only when the selected Record Type publishes `query.capabilityQueries`; its selector, parameters and ordering are loaded from Runtime Discovery;
|
|
211
|
+
- **Return All** follows `nextCursor` automatically, while **Limit** bounds a single-page query.
|
|
212
212
|
|
|
213
|
-
-
|
|
214
|
-
- one or more typed **Filters**;
|
|
215
|
-
- Discovery-driven explicit **Number Comparison** and **Date / Time Comparison** rows using the exact LifeSpace-published operator transport;
|
|
216
|
-
- generic **Local Date Windows** for datetime fields advertised by Time Semantics, including envelope `createdAt` / `updatedAt`, with local dates + IANA timezone sent unchanged to Core;
|
|
217
|
-
- optional grouped **Semantic Query** input generated from `query.capabilityQueries` (for example `calendar.window`) plus its published semantic ordering;
|
|
218
|
-
- **Return All** to follow `nextCursor` automatically;
|
|
219
|
-
- **Limit** when Return All is disabled.
|
|
213
|
+
Advanced Local Date Windows are shown separately because a local calendar window is a structured field/start/end/timezone input rather than a scalar predicate. The adapter sends the published date-window parameter names and IANA timezone unchanged; Core owns timezone/DST conversion. Existing persisted legacy `exact/from/to` filters preserve the inclusive `To` behavior.
|
|
220
214
|
|
|
221
|
-
|
|
215
|
+
Generic Search/Filters/Sorts are intentionally hidden in Capability Query mode until LifeSpace publishes machine-readable facet-composability metadata. This prevents the adapter from guessing which generic parameters may be mixed with a grouped capability query.
|
|
222
216
|
|
|
223
|
-
Use **Sorts → Add Sort** to add zero or more sort criteria in priority order. Sortable model fields use authoritative `title` metadata from Runtime Discovery, while envelope fields such as `createdAt` / `updatedAt` are offered only when Discovery advertises them. Multiple criteria are sent as ordered repeated `sort=field:direction` query parameters.
|
|
217
|
+
Use **Sorts → Add Sort** in Records mode to add zero or more sort criteria in priority order. Sortable model fields use authoritative `title` metadata from Runtime Discovery, while envelope fields such as `createdAt` / `updatedAt` are offered only when Discovery advertises them. Multiple criteria are sent as ordered repeated `sort=field:direction` query parameters.
|
|
224
218
|
|
|
225
219
|
Advanced **Options** contain manual **Cursor** as an escape hatch.
|
|
226
220
|
|
|
@@ -248,6 +242,30 @@ Paths are relative to the configured API Base URL, for example:
|
|
|
248
242
|
|
|
249
243
|
Use normal Record operations when possible because they benefit from Runtime Discovery metadata and n8n-specific UX.
|
|
250
244
|
|
|
245
|
+
## Use the LifeSpace Agent Tool
|
|
246
|
+
|
|
247
|
+
Use **LifeSpace Agent Tool** when connecting LifeSpace to an n8n AI Agent. This is a separate native AiTool surface rather than the human workflow node running through `usableAsTool`.
|
|
248
|
+
|
|
249
|
+
Configure the Tool's structural scope — Space, Record Type and operation — in the node. The Tool then derives its model-facing name, description and input schema from Runtime Discovery, so multiple LifeSpace Tools can distinguish their configured purpose without requiring handwritten descriptions.
|
|
250
|
+
|
|
251
|
+
For Generic Query, the model receives semantic inputs such as:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"search": "food",
|
|
256
|
+
"filters": [
|
|
257
|
+
{ "field": "status", "operator": "in", "value": ["todo"] },
|
|
258
|
+
{ "field": "dueDate", "operator": "gte", "value": "2026-09-13" }
|
|
259
|
+
],
|
|
260
|
+
"sort": [{ "field": "dueDate", "direction": "asc" }],
|
|
261
|
+
"limit": 20
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The adapter validates those field/operator pairs against Discovery and compiles them to the exact LifeSpace query transport. The model does not need to know transport names such as `dueDate.gte`. Local date windows are likewise semantic inputs and Core remains responsible for timezone/DST conversion.
|
|
266
|
+
|
|
267
|
+
Create/Update schemas are generated from writable model fields. Optional fields that the model does not provide are omitted rather than synthesized as empty values. Actions and Capability Queries remain metadata-driven, and adding a future Record Type does not require a model-specific `create_*` or `query_*` node implementation.
|
|
268
|
+
|
|
251
269
|
## Use the LifeSpace Trigger
|
|
252
270
|
|
|
253
271
|
The **LifeSpace Trigger** receives signed LifeSpace Domain Event webhooks.
|
|
@@ -283,11 +301,12 @@ Webhook Endpoint / Event Subscription creation is intentionally not performed by
|
|
|
283
301
|
|
|
284
302
|
- **LifeSpace API** credential for API authentication and Runtime Discovery;
|
|
285
303
|
- **LifeSpace Webhook Signing** credential for endpoint-scoped inbound HMAC verification;
|
|
286
|
-
- **LifeSpace** node with
|
|
304
|
+
- **LifeSpace** human workflow node with Discovery-driven Record operations plus advanced API Request;
|
|
305
|
+
- **LifeSpace Agent Tool** native AiTool with Discovery-driven semantic schemas;
|
|
287
306
|
- **LifeSpace Trigger** with signed multi-Record-Type Domain Event filtering;
|
|
288
|
-
-
|
|
307
|
+
- shared thin adapter projection from LifeSpace Runtime Discovery into human n8n controls and model-facing Tool schemas.
|
|
289
308
|
|
|
290
|
-
LifeSpace remains authoritative for validation, authorization, defaults, Mutation Authority, Action semantics, relation semantics and event contracts. Runtime Discovery and Relation Target Lookup are current capability/reference projections, not execution-authorization proofs.
|
|
309
|
+
LifeSpace remains authoritative for validation, authorization, defaults, Mutation Authority, Action semantics, query/time semantics, relation semantics and event contracts. Runtime Discovery and Relation Target Lookup are current capability/reference projections, not execution-authorization proofs.
|
|
291
310
|
|
|
292
311
|
## Architecture boundary
|
|
293
312
|
|
|
@@ -295,6 +314,8 @@ LifeSpace is the source of truth for Identity, Authority, Shared Reality semanti
|
|
|
295
314
|
|
|
296
315
|
LifeSpace does not depend on n8n. n8n is one optional orchestration/integration runtime consuming LifeSpace APIs and events.
|
|
297
316
|
|
|
317
|
+
The human Workflow node and native Agent Tool are separate presentation projections over the same LifeSpace semantics. Shared code in this package only translates Discovery into n8n-specific UI/schema/transport; it does not redefine domain rules.
|
|
318
|
+
|
|
298
319
|
The adapter does not use privileged model-admin endpoints for ordinary workflow discovery and does not copy Model Definitions into this repository.
|
|
299
320
|
|
|
300
321
|
## Development
|
|
@@ -337,4 +358,4 @@ The adapter now consumes generic `person`, `person_list`, `record` and `record_l
|
|
|
337
358
|
|
|
338
359
|
## License
|
|
339
360
|
|
|
340
|
-
MIT
|
|
361
|
+
MIT
|
|
@@ -20,6 +20,7 @@ export declare class LifeSpace implements INodeType {
|
|
|
20
20
|
getTemporalComparisonFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
21
21
|
getComparisonOperatorsForCurrentField(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
22
22
|
getLocalDateWindowFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
23
|
+
getQueryModes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
23
24
|
getCapabilityQueries(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
24
25
|
getSemanticSorts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
25
26
|
getPersonFilterableFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
@@ -171,26 +171,40 @@ function normalizeActionInput(context, itemIndex, value, fields) {
|
|
|
171
171
|
return result;
|
|
172
172
|
}
|
|
173
173
|
function queryParameters(context, itemIndex, limit, cursorOverride) {
|
|
174
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
|
|
174
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
|
|
175
175
|
const qs = {};
|
|
176
|
-
const
|
|
176
|
+
const configuredQueryMode = String((_a = context.getNodeParameter('queryMode', itemIndex, '')) !== null && _a !== void 0 ? _a : '').trim();
|
|
177
|
+
const legacyCapabilityQueryKey = String((_b = context.getNodeParameter('semanticQueryKey', itemIndex, '')) !== null && _b !== void 0 ? _b : '').trim();
|
|
178
|
+
const rawCapabilityQueryInput = mappedValue(context, itemIndex, 'semanticQueryInput');
|
|
179
|
+
const rawCapabilitySort = String((_c = context.getNodeParameter('semanticSort', itemIndex, '')) !== null && _c !== void 0 ? _c : '').trim();
|
|
180
|
+
const hasLegacyCapabilityConfiguration = legacyCapabilityQueryKey !== ''
|
|
181
|
+
|| Object.keys(rawCapabilityQueryInput).length > 0
|
|
182
|
+
|| rawCapabilitySort !== '';
|
|
183
|
+
const queryMode = configuredQueryMode || (hasLegacyCapabilityConfiguration ? 'capability' : 'standard');
|
|
184
|
+
const useStandardQuery = queryMode !== 'capability';
|
|
185
|
+
const useCapabilityQuery = queryMode === 'capability';
|
|
186
|
+
const search = useStandardQuery ? String(context.getNodeParameter('search', itemIndex, '')).trim() : '';
|
|
177
187
|
const options = context.getNodeParameter('options', itemIndex, {});
|
|
178
|
-
const configuredSorts =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
const
|
|
188
|
+
const configuredSorts = useStandardQuery
|
|
189
|
+
? context.getNodeParameter('sorts.sort', itemIndex, [])
|
|
190
|
+
: [];
|
|
191
|
+
const semanticSort = useCapabilityQuery ? rawCapabilitySort : '';
|
|
192
|
+
const legacySortField = useStandardQuery ? String((_d = options.sortField) !== null && _d !== void 0 ? _d : '').trim() : '';
|
|
193
|
+
const legacySortDirection = String((_e = options.sortDirection) !== null && _e !== void 0 ? _e : 'desc').trim();
|
|
194
|
+
const configuredCursor = String((_f = options.cursor) !== null && _f !== void 0 ? _f : '').trim();
|
|
183
195
|
const cursor = cursorOverride !== null && cursorOverride !== void 0 ? cursorOverride : configuredCursor;
|
|
184
|
-
const filters =
|
|
196
|
+
const filters = useStandardQuery
|
|
197
|
+
? context.getNodeParameter('filters.filter', itemIndex, [])
|
|
198
|
+
: [];
|
|
185
199
|
if (search)
|
|
186
200
|
qs.q = search;
|
|
187
201
|
const orderedSorts = [];
|
|
188
202
|
const usedSortFields = new Set();
|
|
189
203
|
for (const sort of configuredSorts) {
|
|
190
|
-
const field = String((
|
|
204
|
+
const field = String((_g = sort.field) !== null && _g !== void 0 ? _g : '').trim();
|
|
191
205
|
if (!field)
|
|
192
206
|
continue;
|
|
193
|
-
const direction = String((
|
|
207
|
+
const direction = String((_h = sort.direction) !== null && _h !== void 0 ? _h : 'asc').trim();
|
|
194
208
|
if (direction !== 'asc' && direction !== 'desc') {
|
|
195
209
|
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Sort direction for ${field} must be asc or desc`, { itemIndex });
|
|
196
210
|
}
|
|
@@ -227,37 +241,39 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
|
|
|
227
241
|
setQueryParameter(key, value);
|
|
228
242
|
};
|
|
229
243
|
for (const filter of filters) {
|
|
230
|
-
const field = String((
|
|
231
|
-
const value = String((
|
|
232
|
-
const operator = (
|
|
244
|
+
const field = String((_j = filter.field) !== null && _j !== void 0 ? _j : '').trim();
|
|
245
|
+
const value = String((_k = filter.value) !== null && _k !== void 0 ? _k : '');
|
|
246
|
+
const operator = (_l = filter.operator) !== null && _l !== void 0 ? _l : 'exact';
|
|
233
247
|
if (field && value !== '')
|
|
234
248
|
setFilter(field, operator, value);
|
|
235
249
|
}
|
|
236
|
-
const typedFilters =
|
|
237
|
-
|
|
238
|
-
|
|
250
|
+
const typedFilters = useStandardQuery
|
|
251
|
+
? context.getNodeParameter('filters', itemIndex, {})
|
|
252
|
+
: {};
|
|
253
|
+
for (const row of ((_m = typedFilters.text) !== null && _m !== void 0 ? _m : [])) {
|
|
254
|
+
const field = String((_o = row.field) !== null && _o !== void 0 ? _o : '').trim();
|
|
239
255
|
if (field && row.value !== undefined && row.value !== '')
|
|
240
256
|
setFilter(field, 'exact', String(row.value));
|
|
241
257
|
}
|
|
242
|
-
for (const row of ((
|
|
243
|
-
const field = String((
|
|
258
|
+
for (const row of ((_p = typedFilters.enum) !== null && _p !== void 0 ? _p : [])) {
|
|
259
|
+
const field = String((_q = row.field) !== null && _q !== void 0 ? _q : '').trim();
|
|
244
260
|
const values = Array.isArray(row.values) ? row.values.map(String).filter(Boolean) : [];
|
|
245
261
|
if (field && values.length)
|
|
246
262
|
setFilter(field, 'exact', values.join(','));
|
|
247
263
|
}
|
|
248
|
-
for (const row of ((
|
|
249
|
-
const field = String((
|
|
264
|
+
for (const row of ((_r = typedFilters.boolean) !== null && _r !== void 0 ? _r : [])) {
|
|
265
|
+
const field = String((_s = row.field) !== null && _s !== void 0 ? _s : '').trim();
|
|
250
266
|
if (field)
|
|
251
267
|
setFilter(field, 'exact', Boolean(row.value));
|
|
252
268
|
}
|
|
253
|
-
for (const row of ((
|
|
254
|
-
const field = String((
|
|
269
|
+
for (const row of ((_t = typedFilters.number) !== null && _t !== void 0 ? _t : [])) {
|
|
270
|
+
const field = String((_u = row.field) !== null && _u !== void 0 ? _u : '').trim();
|
|
255
271
|
const value = Number(row.value);
|
|
256
272
|
if (field && Number.isFinite(value))
|
|
257
|
-
setFilter(field, (
|
|
273
|
+
setFilter(field, (_v = row.operator) !== null && _v !== void 0 ? _v : 'exact', value);
|
|
258
274
|
}
|
|
259
|
-
for (const row of ((
|
|
260
|
-
const encoded = String((
|
|
275
|
+
for (const row of ((_w = typedFilters.temporal) !== null && _w !== void 0 ? _w : [])) {
|
|
276
|
+
const encoded = String((_x = row.field) !== null && _x !== void 0 ? _x : '').trim();
|
|
261
277
|
if (!encoded || row.value === undefined || row.value === '')
|
|
262
278
|
continue;
|
|
263
279
|
const separator = encoded.indexOf(':');
|
|
@@ -266,24 +282,24 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
|
|
|
266
282
|
const raw = String(row.value);
|
|
267
283
|
const value = fieldType === 'date' ? dateOnlyValue(context, itemIndex, raw, field) : raw;
|
|
268
284
|
if (value !== null)
|
|
269
|
-
setFilter(field, (
|
|
285
|
+
setFilter(field, (_y = row.operator) !== null && _y !== void 0 ? _y : 'exact', value);
|
|
270
286
|
}
|
|
271
|
-
for (const row of ((
|
|
272
|
-
const field = String((
|
|
273
|
-
const target = String((
|
|
287
|
+
for (const row of ((_z = typedFilters.person) !== null && _z !== void 0 ? _z : [])) {
|
|
288
|
+
const field = String((_0 = row.field) !== null && _0 !== void 0 ? _0 : '').trim();
|
|
289
|
+
const target = String((_1 = row.target) !== null && _1 !== void 0 ? _1 : '').trim();
|
|
274
290
|
if (field && target)
|
|
275
291
|
setFilter(field, 'exact', target);
|
|
276
292
|
}
|
|
277
|
-
for (const row of ((
|
|
293
|
+
for (const row of ((_2 = typedFilters.numberComparison) !== null && _2 !== void 0 ? _2 : [])) {
|
|
278
294
|
const field = comparisonFieldParts(row.field).field;
|
|
279
|
-
const parameter = String((
|
|
295
|
+
const parameter = String((_3 = row.parameter) !== null && _3 !== void 0 ? _3 : '').trim();
|
|
280
296
|
const value = Number(row.value);
|
|
281
297
|
if (field && parameter && Number.isFinite(value))
|
|
282
298
|
setQueryParameter(parameter, value);
|
|
283
299
|
}
|
|
284
|
-
for (const row of ((
|
|
300
|
+
for (const row of ((_4 = typedFilters.temporalComparison) !== null && _4 !== void 0 ? _4 : [])) {
|
|
285
301
|
const { field, valueType } = comparisonFieldParts(row.field);
|
|
286
|
-
const parameter = String((
|
|
302
|
+
const parameter = String((_5 = row.parameter) !== null && _5 !== void 0 ? _5 : '').trim();
|
|
287
303
|
if (!field || !parameter || row.value === undefined || row.value === '')
|
|
288
304
|
continue;
|
|
289
305
|
const raw = String(row.value);
|
|
@@ -291,18 +307,23 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
|
|
|
291
307
|
if (value !== null)
|
|
292
308
|
setQueryParameter(parameter, value);
|
|
293
309
|
}
|
|
294
|
-
const localDateWindows =
|
|
310
|
+
const localDateWindows = useStandardQuery
|
|
311
|
+
? [
|
|
312
|
+
...((_6 = typedFilters.localDateWindow) !== null && _6 !== void 0 ? _6 : []),
|
|
313
|
+
...context.getNodeParameter('localDateWindows.window', itemIndex, []),
|
|
314
|
+
]
|
|
315
|
+
: [];
|
|
295
316
|
for (const row of localDateWindows) {
|
|
296
317
|
const selector = decodeLocalDateWindowSelector(row.field);
|
|
297
318
|
if (!selector) {
|
|
298
|
-
if (String((
|
|
319
|
+
if (String((_7 = row.field) !== null && _7 !== void 0 ? _7 : '').trim()) {
|
|
299
320
|
throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Local Date Window selector is invalid. Re-select the field from Runtime Discovery.', { itemIndex });
|
|
300
321
|
}
|
|
301
322
|
continue;
|
|
302
323
|
}
|
|
303
324
|
const dateStart = dateOnlyValue(context, itemIndex, row.dateStart, selector.field);
|
|
304
325
|
const dateEndExclusive = dateOnlyValue(context, itemIndex, row.dateEndExclusive, selector.field);
|
|
305
|
-
const timezone = String((
|
|
326
|
+
const timezone = String((_8 = row.timezone) !== null && _8 !== void 0 ? _8 : '').trim();
|
|
306
327
|
if (dateStart !== null)
|
|
307
328
|
setQueryParameter(selector.dateStartParameter, dateStart);
|
|
308
329
|
if (dateEndExclusive !== null)
|
|
@@ -310,12 +331,12 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
|
|
|
310
331
|
if (timezone)
|
|
311
332
|
setQueryParameter(selector.timezoneParameter, timezone);
|
|
312
333
|
}
|
|
313
|
-
const semanticQueryInput =
|
|
334
|
+
const semanticQueryInput = useCapabilityQuery ? rawCapabilityQueryInput : {};
|
|
314
335
|
for (const [parameter, value] of Object.entries(semanticQueryInput)) {
|
|
315
336
|
if (value === null || value === undefined || value === '')
|
|
316
337
|
continue;
|
|
317
338
|
if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
|
|
318
|
-
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `
|
|
339
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Capability Query parameter ${parameter} must be a scalar value`, { itemIndex });
|
|
319
340
|
}
|
|
320
341
|
setQueryParameter(parameter, value);
|
|
321
342
|
}
|
|
@@ -647,14 +668,21 @@ class LifeSpace {
|
|
|
647
668
|
] }],
|
|
648
669
|
},
|
|
649
670
|
{
|
|
650
|
-
displayName: '
|
|
671
|
+
displayName: 'Query Mode Name or ID', name: 'queryMode', type: 'options', noDataExpression: true,
|
|
672
|
+
typeOptions: { loadOptionsMethod: 'getQueryModes', loadOptionsDependsOn: ['spaceId', 'recordType'] },
|
|
673
|
+
options: [], default: 'standard',
|
|
651
674
|
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
675
|
+
description: 'Standard Query is always available. Capability Query is offered only when the selected Record Type publishes a grouped capability query through Runtime Discovery. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
displayName: 'Search', name: 'search', type: 'string', default: '',
|
|
679
|
+
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['standard'] } },
|
|
652
680
|
description: 'Full-text search across fields declared searchable by LifeSpace. Leave empty to disable search.',
|
|
653
681
|
},
|
|
654
682
|
{
|
|
655
683
|
displayName: 'Filters', name: 'filters', type: 'fixedCollection', default: {},
|
|
656
684
|
placeholder: 'Add Filter', typeOptions: { multipleValues: true },
|
|
657
|
-
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
685
|
+
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['standard'] } },
|
|
658
686
|
options: [
|
|
659
687
|
{ displayName: 'Text Filter', name: 'text', values: [
|
|
660
688
|
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getTextFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
@@ -688,6 +716,12 @@ class LifeSpace {
|
|
|
688
716
|
{ displayName: 'Operator Name or ID', name: 'parameter', type: 'options', description: 'Uses the exact explicit comparison transport advertised by LifeSpace. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.', typeOptions: { loadOptionsMethod: 'getComparisonOperatorsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] }, options: [], default: '', required: true },
|
|
689
717
|
{ displayName: 'Value', name: 'value', type: 'dateTime', default: '', required: true },
|
|
690
718
|
] },
|
|
719
|
+
{ displayName: 'Local Date Window', name: 'localDateWindow', values: [
|
|
720
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getLocalDateWindowFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true, description: 'Choose a datetime field whose published Time Semantics includes a local-date-window transport. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.' },
|
|
721
|
+
{ displayName: 'Start Date', name: 'dateStart', type: 'dateTime', default: '', required: true, description: 'Inclusive local calendar start date. The adapter sends YYYY-MM-DD and does not calculate UTC boundaries.' },
|
|
722
|
+
{ displayName: 'End Date (Exclusive)', name: 'dateEndExclusive', type: 'dateTime', default: '', required: true, description: 'Exclusive local calendar end date' },
|
|
723
|
+
{ displayName: 'Viewing Timezone', name: 'timezone', type: 'string', default: '', required: true, placeholder: 'Europe/Amsterdam', description: 'IANA timezone passed unchanged to LifeSpace Core' },
|
|
724
|
+
] },
|
|
691
725
|
{ displayName: 'Person Filter', name: 'person', values: [
|
|
692
726
|
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getPersonFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
693
727
|
{ displayName: 'Person Name or ID', name: 'target', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] }, options: [], default: '', required: true },
|
|
@@ -700,43 +734,33 @@ class LifeSpace {
|
|
|
700
734
|
],
|
|
701
735
|
},
|
|
702
736
|
{
|
|
703
|
-
displayName: '
|
|
704
|
-
placeholder: 'Add Local Date Window', typeOptions: { multipleValues: true },
|
|
705
|
-
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
706
|
-
options: [{ displayName: 'Window', name: 'window', values: [
|
|
707
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getLocalDateWindowFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true, description: 'Only datetime fields whose published comparison semantics include a local-date-window transport are offered. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.' },
|
|
708
|
-
{ displayName: 'Start Date', name: 'dateStart', type: 'dateTime', default: '', required: true, description: 'Inclusive local calendar start date. The adapter submits YYYY-MM-DD and does not calculate UTC boundaries.' },
|
|
709
|
-
{ displayName: 'End Date (Exclusive)', name: 'dateEndExclusive', type: 'dateTime', default: '', required: true, description: 'Exclusive local calendar end date' },
|
|
710
|
-
{ displayName: 'Viewing Timezone', name: 'timezone', type: 'string', default: '', required: true, placeholder: 'Europe/Amsterdam', description: 'IANA timezone passed unchanged to LifeSpace Core' },
|
|
711
|
-
] }],
|
|
712
|
-
},
|
|
713
|
-
{
|
|
714
|
-
displayName: 'Semantic Query Name or ID', name: 'semanticQueryKey', type: 'options',
|
|
737
|
+
displayName: 'Capability Query Name or ID', name: 'semanticQueryKey', type: 'options', noDataExpression: true,
|
|
715
738
|
typeOptions: { loadOptionsMethod: 'getCapabilityQueries', loadOptionsDependsOn: ['spaceId', 'recordType'] },
|
|
716
|
-
options: [], default: '',
|
|
717
|
-
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
718
|
-
description: '
|
|
739
|
+
options: [], default: '', required: true,
|
|
740
|
+
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['capability'] } },
|
|
741
|
+
description: 'Choose a grouped capability query published by the selected LifeSpace Record Type. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
719
742
|
},
|
|
720
743
|
{
|
|
721
|
-
displayName: '
|
|
744
|
+
displayName: 'Capability Query Parameters', name: 'semanticQueryInput', type: 'resourceMapper',
|
|
722
745
|
default: { mappingMode: 'defineBelow', value: null }, noDataExpression: true,
|
|
723
746
|
typeOptions: {
|
|
724
747
|
loadOptionsDependsOn: ['spaceId', 'recordType', 'semanticQueryKey'],
|
|
725
748
|
resourceMapper: {
|
|
726
749
|
resourceMapperMethod: 'getSemanticQueryInputFields', mode: 'add',
|
|
727
|
-
fieldWords: { singular: 'parameter', plural: 'parameters' },
|
|
728
|
-
|
|
750
|
+
fieldWords: { singular: 'query parameter', plural: 'query parameters' },
|
|
751
|
+
addAllFields: true, supportAutoMap: false,
|
|
752
|
+
noFieldsError: 'Choose a Capability Query to load its LifeSpace-published parameters.',
|
|
729
753
|
},
|
|
730
754
|
},
|
|
731
|
-
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
732
|
-
description: 'Parameters are projected directly from query.capabilityQueries.
|
|
755
|
+
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['capability'] } },
|
|
756
|
+
description: 'Parameters are projected directly from query.capabilityQueries. Local dates/timezone are sent to Core unchanged; Core owns overlap and DST semantics.',
|
|
733
757
|
},
|
|
734
758
|
{
|
|
735
|
-
displayName: '
|
|
759
|
+
displayName: 'Capability Sort Name or ID', name: 'semanticSort', type: 'options',
|
|
736
760
|
typeOptions: { loadOptionsMethod: 'getSemanticSorts', loadOptionsDependsOn: ['spaceId', 'recordType', 'semanticQueryKey'] },
|
|
737
761
|
options: [], default: '',
|
|
738
|
-
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
739
|
-
description: 'Optional capability-specific
|
|
762
|
+
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['capability'] } },
|
|
763
|
+
description: 'Optional capability-specific ordering published with the selected Capability Query. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
740
764
|
},
|
|
741
765
|
{
|
|
742
766
|
displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false,
|
|
@@ -752,7 +776,7 @@ class LifeSpace {
|
|
|
752
776
|
{
|
|
753
777
|
displayName: 'Sorts', name: 'sorts', type: 'fixedCollection', default: {},
|
|
754
778
|
placeholder: 'Add Sort', typeOptions: { multipleValues: true },
|
|
755
|
-
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
779
|
+
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['standard'] } },
|
|
756
780
|
options: [{ displayName: 'Sort', name: 'sort', values: [
|
|
757
781
|
{ displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getSortableFields' }, options: [], default: '', required: true, description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>' },
|
|
758
782
|
{ displayName: 'Direction', name: 'direction', type: 'options', options: [{ name: 'Ascending', value: 'asc' }, { name: 'Descending', value: 'desc' }], default: 'asc' },
|
|
@@ -927,6 +951,27 @@ class LifeSpace {
|
|
|
927
951
|
};
|
|
928
952
|
});
|
|
929
953
|
},
|
|
954
|
+
async getQueryModes() {
|
|
955
|
+
var _a;
|
|
956
|
+
const modes = [{
|
|
957
|
+
name: 'Standard Query',
|
|
958
|
+
value: 'standard',
|
|
959
|
+
description: 'Search, filters, comparisons, local-date windows, and generic sorting',
|
|
960
|
+
}];
|
|
961
|
+
try {
|
|
962
|
+
const selected = await optionModel(this);
|
|
963
|
+
if (((_a = selected === null || selected === void 0 ? void 0 : selected.model.query.capabilityQueries) !== null && _a !== void 0 ? _a : []).length > 0) {
|
|
964
|
+
modes.push({
|
|
965
|
+
name: 'Capability Query',
|
|
966
|
+
value: 'capability',
|
|
967
|
+
description: 'Use one grouped query contract published by a LifeSpace Capability',
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
catch {
|
|
972
|
+
}
|
|
973
|
+
return modes;
|
|
974
|
+
},
|
|
930
975
|
async getCapabilityQueries() {
|
|
931
976
|
var _a;
|
|
932
977
|
const selected = await optionModel(this);
|