pgkiln 0.80.0 → 0.82.0

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 (85) hide show
  1. package/db/migrations/146_database_model_providers.sql +53 -0
  2. package/db/migrations/147_condition_types.sql +80 -0
  3. package/db/migrations/148_validation_types.sql +44 -0
  4. package/db/migrations/149_dynamic_action_events.sql +45 -0
  5. package/db/migrations/150_process_types.sql +23 -0
  6. package/db/migrations/151_item_types.sql +16 -0
  7. package/db/migrations/152_workflow_diagram_region.sql +10 -0
  8. package/docs/apex-feature-parity.md +12 -10
  9. package/docs/guide/02-concepts.md +3 -3
  10. package/docs/guide/03-builder.md +13 -3
  11. package/docs/guide/04-pages-and-regions.md +17 -3
  12. package/docs/guide/05-items.md +5 -0
  13. package/docs/guide/06-processing.md +104 -17
  14. package/docs/guide/07-dynamic-actions.md +16 -1
  15. package/docs/guide/11-from-apex.md +7 -0
  16. package/examples/hr/hr_79_condition_types.sql +17 -0
  17. package/package.json +1 -1
  18. package/public/app.css +12 -0
  19. package/public/app.js +157 -9
  20. package/src/ai/embeddings.ts +20 -7
  21. package/src/appfiles.ts +4 -4
  22. package/src/builder/advisor.ts +43 -7
  23. package/src/builder/code-editor.ts +14 -10
  24. package/src/builder/components.ts +51 -21
  25. package/src/builder/conditions.ts +64 -0
  26. package/src/builder/db-users.ts +124 -118
  27. package/src/builder/designer.ts +9 -1
  28. package/src/builder/home.ts +42 -40
  29. package/src/builder/i18n/de.ts +464 -0
  30. package/src/builder/i18n/en.ts +464 -0
  31. package/src/builder/i18n/es.ts +464 -0
  32. package/src/builder/i18n/fr.ts +464 -0
  33. package/src/builder/i18n/it.ts +464 -0
  34. package/src/builder/i18n/ja.ts +464 -0
  35. package/src/builder/i18n/ko.ts +464 -0
  36. package/src/builder/i18n/nl.ts +464 -0
  37. package/src/builder/i18n/pt.ts +464 -0
  38. package/src/builder/i18n/zh-cn.ts +464 -0
  39. package/src/builder/i18n/zh-tw.ts +464 -0
  40. package/src/builder/instance.ts +27 -23
  41. package/src/builder/ldap.ts +50 -47
  42. package/src/builder/mail-queue.ts +50 -45
  43. package/src/builder/network-access.ts +46 -43
  44. package/src/builder/region-help.ts +3 -1
  45. package/src/builder/region-settings.ts +30 -5
  46. package/src/builder/vector-column.ts +3 -3
  47. package/src/builder/vector-providers.ts +16 -5
  48. package/src/i18n/ar.ts +5 -0
  49. package/src/i18n/cs.ts +5 -0
  50. package/src/i18n/da.ts +5 -0
  51. package/src/i18n/de.ts +5 -0
  52. package/src/i18n/el.ts +5 -0
  53. package/src/i18n/es.ts +5 -0
  54. package/src/i18n/fi.ts +5 -0
  55. package/src/i18n/fr.ts +5 -0
  56. package/src/i18n/he.ts +5 -0
  57. package/src/i18n/it.ts +5 -0
  58. package/src/i18n/ja.ts +5 -0
  59. package/src/i18n/ko.ts +5 -0
  60. package/src/i18n/nb.ts +5 -0
  61. package/src/i18n/pl.ts +5 -0
  62. package/src/i18n/pt.ts +5 -0
  63. package/src/i18n/ru.ts +5 -0
  64. package/src/i18n/sv.ts +5 -0
  65. package/src/i18n/tr.ts +5 -0
  66. package/src/i18n/uk.ts +5 -0
  67. package/src/i18n/zh.ts +5 -0
  68. package/src/i18n.ts +10 -0
  69. package/src/metadata.ts +72 -9
  70. package/src/runtime/authz.ts +9 -5
  71. package/src/runtime/context.ts +4 -0
  72. package/src/runtime/da-styles.ts +37 -0
  73. package/src/runtime/documents.ts +3 -2
  74. package/src/runtime/engine.ts +95 -13
  75. package/src/runtime/items.ts +33 -0
  76. package/src/runtime/logic.ts +79 -8
  77. package/src/runtime/maps.ts +50 -4
  78. package/src/runtime/process-types.ts +139 -0
  79. package/src/runtime/processes.ts +2 -0
  80. package/src/runtime/regions.ts +4 -0
  81. package/src/runtime/render.ts +13 -2
  82. package/src/runtime/routes.ts +2 -2
  83. package/src/runtime/workflow-diagram.ts +45 -0
  84. package/src/vector.ts +3 -3
  85. package/src/workflow.ts +10 -2
@@ -1,3 +1,4 @@
1
+ import { shapeStyle } from '../runtime/maps.ts';
1
2
  import type { FastifyInstance } from 'fastify';
2
3
  import { aiFilterSettingsForm, assistantSettingsForm } from './assistant.ts';
3
4
  import { designSql } from './websources.ts';
@@ -398,6 +399,25 @@ export function mergeWorkflowsSettings(config: Config, b: Body): Config {
398
399
  return out;
399
400
  }
400
401
 
402
+ // (159, CD-6) a map layer's kind and the style of a lines / polygons / extruded layer
403
+ const LAYER_KINDS = new Set(['heat', 'lines', 'polygons', 'extruded']);
404
+ function mergeStyle(b: Body, p: string): Config | undefined {
405
+ const s = shapeStyle({ fill_color: b[`${p}_fill_color`]?.trim() || undefined, stroke_color: b[`${p}_stroke_color`]?.trim() || undefined,
406
+ stroke_width: b[`${p}_stroke_width`]?.trim() ? Number(b[`${p}_stroke_width`]) : undefined, opacity: b[`${p}_opacity`]?.trim() ? Number(b[`${p}_opacity`]) : undefined });
407
+ return Object.keys(s).length ? (s as Config) : undefined;
408
+ }
409
+ function styleFields(id: (n: string) => string, p: string, st: Config | undefined) {
410
+ const s = (st ?? {}) as Config;
411
+ return html`<div class="field"><label class="label" for="${id(`${p}_fill_color`)}">Fill colour (lines / polygons)</label>
412
+ <input id="${id(`${p}_fill_color`)}" name="${p}_fill_color" value="${s.fill_color ?? ''}" placeholder="#2e5c8a" pattern="#[0-9a-fA-F]{6}"></div>
413
+ <div class="field"><label class="label" for="${id(`${p}_stroke_color`)}">Line colour</label>
414
+ <input id="${id(`${p}_stroke_color`)}" name="${p}_stroke_color" value="${s.stroke_color ?? ''}" placeholder="#1b2230" pattern="#[0-9a-fA-F]{6}"></div>
415
+ <div class="field"><label class="label" for="${id(`${p}_stroke_width`)}">Line width</label>
416
+ <input id="${id(`${p}_stroke_width`)}" name="${p}_stroke_width" type="number" min="0" max="20" step="0.5" value="${s.stroke_width ?? ''}"></div>
417
+ <div class="field"><label class="label" for="${id(`${p}_opacity`)}">Opacity (0–1)</label>
418
+ <input id="${id(`${p}_opacity`)}" name="${p}_opacity" type="number" min="0" max="1" step="0.05" value="${s.opacity ?? ''}"></div>`;
419
+ }
420
+
401
421
  export function mergeMapSettings(config: Config, b: Body, a: Allowed): Config {
402
422
  const out = { ...config };
403
423
  const set = setter(out);
@@ -406,8 +426,9 @@ export function mergeMapSettings(config: Config, b: Body, a: Allowed): Config {
406
426
  set('zoom', b.zoom && Number.isInteger(zoom) && zoom >= 1 && zoom <= 19 ? zoom : undefined);
407
427
  set('empty', b.empty?.trim() || undefined);
408
428
  set('link', mergeLink(b, a.pages));
409
- set('layer', b.layer === 'heat' ? 'heat' : undefined);
410
- set('cluster', b.cluster === 'true' && b.layer !== 'heat' && b.load !== 'tiles' ? true : undefined);
429
+ set('layer', LAYER_KINDS.has(b.layer ?? '') ? b.layer : undefined);
430
+ set('cluster', b.cluster === 'true' && !b.layer && b.load !== 'tiles' ? true : undefined);
431
+ set('style', mergeStyle(b, 'style'));
411
432
  set('name', b.name?.trim().slice(0, 60) || undefined);
412
433
  set('visible_area', b.load === 'area' ? true : undefined);
413
434
  set('tiles', b.load === 'tiles' ? true : undefined);
@@ -422,8 +443,10 @@ export function mergeMapSettings(config: Config, b: Body, a: Allowed): Config {
422
443
  if (!source) continue;
423
444
  const layer: Config = { name: b[`layer${i}_name`]?.trim().slice(0, 60) || `Layer ${layers.length + 2}`, source };
424
445
  const load = b[`layer${i}_load`];
425
- if (b[`layer${i}_layer`] === 'heat') layer.layer = 'heat';
446
+ if (LAYER_KINDS.has(b[`layer${i}_layer`] ?? '')) layer.layer = b[`layer${i}_layer`];
426
447
  else if (b[`layer${i}_cluster`] === 'true' && load !== 'tiles') layer.cluster = true;
448
+ const style = mergeStyle(b, `layer${i}`);
449
+ if (style) layer.style = style;
427
450
  if (b[`layer${i}_hidden`] === 'true') layer.hidden = true;
428
451
  if (load === 'area') layer.visible_area = true;
429
452
  else if (load === 'tiles') layer.tiles = true;
@@ -572,7 +595,8 @@ async function mapLayersFieldsets(cfg: Config, appId: number, allPages: { page_n
572
595
  <div class="field"><label class="label" for="${id(`${p}_name`)}">Name (legend)</label>
573
596
  <input id="${id(`${p}_name`)}" name="${p}_name" value="${l.name ?? ''}" maxlength="60"></div>
574
597
  <div class="field"><label class="label" for="${id(`${p}_layer`)}">Show places as</label>
575
- <select id="${id(`${p}_layer`)}" name="${p}_layer">${opt('', 'Markers', l.layer)}${opt('heat', 'Heat map', l.layer)}</select></div>
598
+ <select id="${id(`${p}_layer`)}" name="${p}_layer">${opt('', 'Markers', l.layer)}${opt('heat', 'Heat map', l.layer)}${opt('lines', 'Lines', l.layer)}${opt('polygons', 'Polygons', l.layer)}${opt('extruded', 'Extruded polygons (shaded by height)', l.layer)}</select></div>
599
+ ${styleFields(id, p, l.style)}
576
600
  ${loadField(id, `${p}_load`, l)}
577
601
  ${check(`${p}_cluster`, 'Group close markers', l.cluster === true)}
578
602
  ${check(`${p}_hidden`, 'Off at first', l.hidden === true)}
@@ -822,7 +846,8 @@ export async function regionSettingsForm(pageId: number, appId: number, r: Regio
822
846
  <p class="muted u-mt0">Each row is a marker at <code>lat</code>, <code>lng</code> (or <code>location</code> as "lat,lng"), with <code>title</code> and <code>body</code> in its popup; a <code>geojson</code> column draws lines and areas (with PostGIS installed, a <code>geometry</code> or <code>geography</code> column in WGS 84 works too). A heat map weighs each place by a <code>weight</code> column.</p>
823
847
  <fieldset class="prop-group"><legend>Appearance</legend><div class="form-grid">
824
848
  <div class="field"><label class="label" for="${id('layer')}">Show places as</label>
825
- <select id="${id('layer')}" name="layer">${opt('', 'Markers', cfg.layer)}${opt('heat', 'Heat map', cfg.layer)}</select></div>
849
+ <select id="${id('layer')}" name="layer">${opt('', 'Markers', cfg.layer)}${opt('heat', 'Heat map', cfg.layer)}${opt('lines', 'Lines', cfg.layer)}${opt('polygons', 'Polygons', cfg.layer)}${opt('extruded', 'Extruded polygons (shaded by height)', cfg.layer)}</select></div>
850
+ ${styleFields(id, 'style', cfg.style)}
826
851
  <div class="field"><label class="label" for="${id('name')}">Layer name (legend)</label>
827
852
  <input id="${id('name')}" name="name" value="${cfg.name ?? ''}" maxlength="60" placeholder="the region title"></div>
828
853
  <div class="field"><label class="label" for="${id('height')}">Height</label>
@@ -1,6 +1,6 @@
1
1
  import type { FastifyInstance, FastifyReply } from 'fastify';
2
2
  import pg from 'pg';
3
- import { HNSW_MAX_DIMENSIONS, vectorProviderProblems, type VectorProvider } from '../ai/embeddings.ts';
3
+ import { embedsInDatabase, HNSW_MAX_DIMENSIONS, vectorProviderProblems, type VectorProvider } from '../ai/embeddings.ts';
4
4
  import { joinStatements, recordChanges } from '../changelog.ts';
5
5
  import { owner } from '../db.ts';
6
6
  import { html, type Raw } from '../html.ts';
@@ -102,7 +102,7 @@ async function page(req: Req, reply: FastifyReply) {
102
102
  const base = t.name.replace(/[^a-z0-9_]+/gi, '_').toLowerCase().slice(0, 40);
103
103
  const index = isPost ? String(b.index ?? '').trim() : `${base}_${column || 'embedding'}_idx`;
104
104
  const distance: Distance = isDistance(b.distance) ? b.distance : 'cosine';
105
- const mode: 'set' | 'queue' = pick?.p.type === 'function' && (isPost ? b.keep === 'set' : true) ? 'set' : 'queue';
105
+ const mode: 'set' | 'queue' = pick && embedsInDatabase(pick.p) && (isPost ? b.keep === 'set' : true) ? 'set' : 'queue';
106
106
  const picked = new Set<string>();
107
107
  if (isPost) texty.forEach((c, i) => b[`c.${i}.use`] === 'on' && b[`c.${i}.name`] === c.name && picked.add(c.name));
108
108
  else texty.forEach((c) => picked.add(c.name));
@@ -189,7 +189,7 @@ async function page(req: Req, reply: FastifyReply) {
189
189
  : html`<form method="post" action="${BASE}/sql/objects/vector" class="fts-form">${csrf(s)}
190
190
  <input type="hidden" name="o" value="${table}">
191
191
  <div class="form-grid">
192
- <div class="field"><label class="label" for="vec-provider">${tx('vec.provider')}</label><select id="vec-provider" name="provider">${choices.map((c) => html`<option value="${c.key}"${sel(c.key, pick?.key ?? '')}>${c.alias} · ${c.p.name} (${c.p.type === 'ai_service' ? c.p.model : c.p.function}, ${c.p.dimensions})</option>`)}</select>
192
+ <div class="field"><label class="label" for="vec-provider">${tx('vec.provider')}</label><select id="vec-provider" name="provider">${choices.map((c) => html`<option value="${c.key}"${sel(c.key, pick?.key ?? '')}>${c.alias} · ${c.p.name} (${c.p.type === 'function' ? c.p.function : c.p.model}, ${c.p.dimensions})</option>`)}</select>
193
193
  <small class="help">${tx('vec.provider_help')}</small></div>
194
194
  <div class="field"><label class="label" for="vec-keep">${tx('vec.keep')}</label><select id="vec-keep" name="keep"><option value="set"${sel(mode, 'set')}>${tx('vec.mode_set')}</option><option value="queue"${sel(mode, 'queue')}>${tx('vec.mode_queue')}</option></select>
195
195
  <small class="help">${tx('vec.keep_help')}</small></div>
@@ -1,5 +1,5 @@
1
1
  import type { FastifyInstance, FastifyReply } from 'fastify';
2
- import { embedTexts, vectorProviderProblems, DEFAULT_BATCH, DEFAULT_PER_MINUTE, HNSW_MAX_DIMENSIONS, type VectorProvider } from '../ai/embeddings.ts';
2
+ import { embedTexts, MODEL_ENGINES, vectorProviderProblems, DEFAULT_BATCH, DEFAULT_PER_MINUTE, HNSW_MAX_DIMENSIONS, type VectorProvider } from '../ai/embeddings.ts';
3
3
  import { providerLabel } from '../ai/service.ts';
4
4
  import { isAiError } from '../ai/types.ts';
5
5
  import { owner } from '../db.ts';
@@ -54,6 +54,8 @@ async function servicesOf(appId: number) {
54
54
 
55
55
  async function providerForm(a: any, s: Session, x: Partial<VectorProvider>, original: string, problems: string[]): Promise<Raw> {
56
56
  const services = await servicesOf(a.id);
57
+ // (151) a model in the database: is PostgresML there?
58
+ const pgml = !!(await (await devDbForApp(a.id)).query(`select to_regprocedure('pgml.embed(text, text, jsonb)') is not null as ok`)).rows[0]?.ok;
57
59
  const svc: [string, string][] = [['', '-'], ...services.map((v): [string, string] => [v.name, `${v.name} (${providerLabel(v.provider)})`])];
58
60
  if (x.service && !services.some((v) => v.name === x.service)) svc.push([x.service, `${x.service} (not available to this application)`]);
59
61
  const configs = original
@@ -63,9 +65,9 @@ async function providerForm(a: any, s: Session, x: Partial<VectorProvider>, orig
63
65
  return html`${problems.length ? html`<div class="alert alert-error" role="alert">${problems.join(' ')}</div>` : ''}
64
66
  <form method="post" action="${BASE}/apps/${a.id}/vector-providers/provider">${csrf(s)}<input type="hidden" name="original" value="${original}">
65
67
  <fieldset class="prop-group"><legend>Vector provider</legend><div class="form-grid">
66
- ${input('name', 'Name (static ID)', x.name ?? '', { required: true, placeholder: 'e.g. DOC_EMBEDDINGS', help: 'Upper case letters, digits and _. Search configurations name it; in SQL: meta.embed(text, \'DOC_EMBEDDINGS\') (function providers).' })}
68
+ ${input('name', 'Name (static ID)', x.name ?? '', { required: true, placeholder: 'e.g. DOC_EMBEDDINGS', help: 'Upper case letters, digits and _. Search configurations name it; in SQL: meta.embed(text, \'DOC_EMBEDDINGS\') (function and database model providers).' })}
67
69
  ${input('description', 'Description', x.description ?? '')}
68
- ${select('type', 'Type', x.type ?? 'ai_service', [['ai_service', 'AI service with an embedding model'], ['function', 'SQL or PL/pgSQL function']], 'Where the embeddings come from.')}
70
+ ${select('type', 'Type', x.type ?? 'ai_service', [['ai_service', 'AI service with an embedding model'], ['database_model', 'Model in the database (PostgresML)'], ['function', 'SQL or PL/pgSQL function']], 'Where the embeddings come from.')}
69
71
  ${input('dimensions', 'Dimensions', x.dimensions ?? '', { type: 'number', required: true, placeholder: 'e.g. 1536', help: `The length of each vector: text-embedding-3-small 1536 (or fewer: OpenAI shortens them), text-embedding-3-large 3072, nomic-embed-text 768, gemini-embedding-001 3072. A vector column is vector(n) with this n; an HNSW index takes up to ${HNSW_MAX_DIMENSIONS}.` })}
70
72
  </div></fieldset>
71
73
  <fieldset class="prop-group"><legend>AI service</legend><div class="form-grid">
@@ -75,6 +77,11 @@ async function providerForm(a: any, s: Session, x: Partial<VectorProvider>, orig
75
77
  ${input('batch_size', 'Texts per request', x.batch_size ?? '', { type: 'number', placeholder: String(DEFAULT_BATCH), help: 'When the server embeds queued rows (vector columns).' })}
76
78
  ${input('per_minute', 'Requests per minute', x.per_minute ?? '', { type: 'number', placeholder: String(DEFAULT_PER_MINUTE), help: 'At most this many background requests per minute (per server), under the provider\'s rate limit.' })}
77
79
  </div></fieldset>
80
+ <fieldset class="prop-group"><legend>Model in the database</legend><div class="form-grid">
81
+ ${pgml ? '' : html`<p class="muted" data-wide>PostgresML (<code>pgml</code>) is not installed in this database: install it on the database server, then <code>create extension pgml;</code>. It runs Hugging Face models (ONNX among them) inside PostgreSQL.</p>`}
82
+ ${select('engine', 'Engine', x.engine ?? 'pgml', MODEL_ENGINES.map((e): [string, string] => [e, e === 'pgml' ? 'PostgresML: pgml.embed(model, text)' : e]), 'The extension that runs the model. Nothing leaves the database; grant usage on its schema (pgml) to the application\'s role.')}
83
+ ${input('db_model', 'Model', x.type === 'database_model' ? (x.model ?? '') : '', { placeholder: 'e.g. intfloat/e5-small-v2', help: 'A model the engine loads (the first call downloads it): intfloat/e5-small-v2 (384), Alibaba-NLP/gte-base-en-v1.5 (768), mixedbread-ai/mxbai-embed-large-v1 (1024) …' })}
84
+ </div></fieldset>
78
85
  <fieldset class="prop-group"><legend>Function</legend><div class="form-grid">
79
86
  ${input('function', 'Function', x.function ?? '', { placeholder: 'e.g. sales.embed', help: 'schema.name of a function (text) returning vector, real[] or float8[]. meta.embed calls it as the role that runs the query (its grants apply): grant execute on it to the application\'s role.' })}
80
87
  </div></fieldset>
@@ -114,6 +121,10 @@ function providerFrom(b: Body, errors: string[]): VectorProvider {
114
121
  if (pm !== undefined) x.per_minute = pm;
115
122
  }
116
123
  if (type === 'function') x.function = one(b.function).trim();
124
+ if (type === 'database_model') {
125
+ x.engine = one(b.engine).trim() as VectorProvider['engine'];
126
+ x.model = one(b.db_model).trim();
127
+ }
117
128
  return x;
118
129
  }
119
130
 
@@ -153,7 +164,7 @@ export async function vectorProviderRoutes(app: FastifyInstance) {
153
164
  ${pgvectorNote(st, await isAdmin(s.username), s)}
154
165
  ${region('Providers', html`<div class="table-wrap"><table class="report report-reflow"><thead><tr><th>Name</th><th>Type</th><th>Model or function</th><th class="num">Dimensions</th></tr></thead>
155
166
  <tbody>${list.length ? list.map((p) => html`<tr><td data-label="Name"><a href="${BASE}/apps/${a.id}/vector-providers/provider?name=${encodeURIComponent(p.name)}">${p.name}</a>${p.description ? html` <span class="muted">${p.description}</span>` : ''}</td>
156
- <td data-label="Type">${p.type === 'ai_service' ? html`AI service ${p.service}` : 'function'}</td><td data-label="Model or function"><code>${p.type === 'ai_service' ? p.model : p.function}</code></td>
167
+ <td data-label="Type">${p.type === 'ai_service' ? html`AI service ${p.service}` : p.type === 'database_model' ? `in the database (${p.engine})` : 'function'}</td><td data-label="Model or function"><code>${p.type === 'function' ? p.function : p.model}</code></td>
157
168
  <td class="num" data-label="Dimensions">${p.dimensions}</td></tr>`)
158
169
  : html`<tr><td colspan="4" class="empty">No vector providers yet.</td></tr>`}</tbody></table></div>
159
170
  <p><a class="btn" href="${BASE}/apps/${a.id}/vector-providers/provider">${icon('plus')} Add vector provider</a></p>`)}`;
@@ -256,7 +267,7 @@ export async function vectorProviderRoutes(app: FastifyInstance) {
256
267
  ${region('Providers', html`<div class="table-wrap"><table class="report report-reflow"><thead><tr><th>Application</th><th>Name</th><th>Type</th><th>Model or function</th><th class="num">Dimensions</th></tr></thead>
257
268
  <tbody>${rows.length ? rows.map(({ app: x, p }) => html`<tr><td data-label="Application">${x.name}</td>
258
269
  <td data-label="Name"><a href="${BASE}/apps/${x.id}/vector-providers/provider?name=${encodeURIComponent(p.name)}">${p.name}</a></td>
259
- <td data-label="Type">${p.type === 'ai_service' ? html`AI service ${p.service}` : 'function'}</td><td data-label="Model or function"><code>${p.type === 'ai_service' ? p.model : p.function}</code></td>
270
+ <td data-label="Type">${p.type === 'ai_service' ? html`AI service ${p.service}` : p.type === 'database_model' ? `in the database (${p.engine})` : 'function'}</td><td data-label="Model or function"><code>${p.type === 'function' ? p.function : p.model}</code></td>
260
271
  <td class="num" data-label="Dimensions">${p.dimensions}</td></tr>`)
261
272
  : html`<tr><td colspan="5" class="empty">No vector providers yet.</td></tr>`}</tbody></table></div>`)}
262
273
  ${region('Vector columns', html`<div class="table-wrap"><table class="report report-reflow"><thead><tr><th>Column</th><th>Provider</th><th>Source columns</th><th>Kept by</th></tr></thead>
package/src/i18n/ar.ts CHANGED
@@ -676,6 +676,11 @@ export const ar: Record<MessageKey, string> = {
676
676
  'process.queued': "بدأ في الخلفية (المهمة {id}).",
677
677
  'process.background_type': "لا يمكن تشغيل العملية «{name}» ({type}) في الخلفية.",
678
678
  'workflow.started': "بدأ مسار العمل {id}.",
679
+ 'process.no_geocoder': 'لم يُعدّ أي محدِّد مواقع جغرافية لهذا التثبيت.',
680
+ 'process.address_not_found': 'لم يُعثر على مكان لـ "{address}".',
681
+ 'process.task_created': 'تم إنشاء المهمة {id}.',
682
+ 'item.map_of': 'خريطة {position}',
683
+ 'process.no_task': 'لا توجد مهمة للتعامل معها.',
679
684
  'workflow.terminated': "أُنهي مسار العمل {id}.",
680
685
  'workflow.retried': "يعمل مسار العمل {id} من جديد.",
681
686
  'workflow.no_instance': "لم يُحدد أي مسار عمل.",
package/src/i18n/cs.ts CHANGED
@@ -676,6 +676,11 @@ export const cs: Record<MessageKey, string> = {
676
676
  'process.queued': "Spuštěno na pozadí (úloha {id}).",
677
677
  'process.background_type': "Proces „{name}“ ({type}) nemůže běžet na pozadí.",
678
678
  'workflow.started': "Pracovní postup {id} byl spuštěn.",
679
+ 'process.no_geocoder': 'Tato instalace nemá nastavený geokodér.',
680
+ 'process.address_not_found': 'Pro „{address}“ nebylo nalezeno žádné místo.',
681
+ 'process.task_created': 'Úkol {id} byl vytvořen.',
682
+ 'item.map_of': 'Mapa: {position}',
683
+ 'process.no_task': 'Žádný úkol ke zpracování.',
679
684
  'workflow.terminated': "Pracovní postup {id} byl ukončen.",
680
685
  'workflow.retried': "Pracovní postup {id} běží znovu.",
681
686
  'workflow.no_instance': "Není vybrán žádný pracovní postup.",
package/src/i18n/da.ts CHANGED
@@ -676,6 +676,11 @@ export const da: Record<MessageKey, string> = {
676
676
  'process.queued': "Startet i baggrunden (job {id}).",
677
677
  'process.background_type': "Processen “{name}” ({type}) kan ikke køre i baggrunden.",
678
678
  'workflow.started': "Arbejdsgang {id} er startet.",
679
+ 'process.no_geocoder': 'Denne installation har ingen geokoder.',
680
+ 'process.address_not_found': 'Intet sted fundet for "{address}".',
681
+ 'process.task_created': 'Opgave {id} er oprettet.',
682
+ 'item.map_of': 'Kort over {position}',
683
+ 'process.no_task': 'Ingen opgave at behandle.',
679
684
  'workflow.terminated': "Arbejdsgang {id} er afsluttet.",
680
685
  'workflow.retried': "Arbejdsgang {id} kører igen.",
681
686
  'workflow.no_instance': "Ingen arbejdsgang er valgt.",
package/src/i18n/de.ts CHANGED
@@ -686,6 +686,11 @@ export const de: Record<MessageKey, string> = {
686
686
  'process.queued': 'Im Hintergrund gestartet (Job {id}).',
687
687
  'process.background_type': 'Der Prozess "{name}" ({type}) kann nicht im Hintergrund laufen.',
688
688
  'workflow.started': 'Workflow {id} gestartet.',
689
+ 'process.no_geocoder': 'Für diese Installation ist kein Geocoder eingerichtet.',
690
+ 'process.address_not_found': 'Kein Ort für „{address}“ gefunden.',
691
+ 'process.task_created': 'Aufgabe {id} wurde erstellt.',
692
+ 'item.map_of': 'Karte von {position}',
693
+ 'process.no_task': 'Keine Aufgabe zum Bearbeiten.',
689
694
  'workflow.terminated': 'Workflow {id} beendet.',
690
695
  'workflow.retried': 'Workflow {id} läuft wieder.',
691
696
  'workflow.no_instance': 'Es ist kein Workflow ausgewählt.',
package/src/i18n/el.ts CHANGED
@@ -676,6 +676,11 @@ export const el: Record<MessageKey, string> = {
676
676
  'process.queued': "Ξεκίνησε στο παρασκήνιο (εργασία {id}).",
677
677
  'process.background_type': "Η διαδικασία «{name}» ({type}) δεν μπορεί να εκτελεστεί στο παρασκήνιο.",
678
678
  'workflow.started': "Η ροή εργασίας {id} ξεκίνησε.",
679
+ 'process.no_geocoder': 'Δεν έχει ρυθμιστεί γεωκωδικοποιητής για αυτή την εγκατάσταση.',
680
+ 'process.address_not_found': 'Δεν βρέθηκε τοποθεσία για «{address}».',
681
+ 'process.task_created': 'Η εργασία {id} δημιουργήθηκε.',
682
+ 'item.map_of': 'Χάρτης του {position}',
683
+ 'process.no_task': 'Δεν υπάρχει εργασία για ενέργεια.',
679
684
  'workflow.terminated': "Η ροή εργασίας {id} τερματίστηκε.",
680
685
  'workflow.retried': "Η ροή εργασίας {id} εκτελείται ξανά.",
681
686
  'workflow.no_instance': "Δεν έχει επιλεγεί ροή εργασίας.",
package/src/i18n/es.ts CHANGED
@@ -686,6 +686,11 @@ export const es: Record<MessageKey, string> = {
686
686
  'process.queued': 'Iniciado en segundo plano (trabajo {id}).',
687
687
  'process.background_type': 'El proceso "{name}" ({type}) no se puede ejecutar en segundo plano.',
688
688
  'workflow.started': 'Flujo de trabajo {id} iniciado.',
689
+ 'process.no_geocoder': 'Esta instalación no tiene un geocodificador configurado.',
690
+ 'process.address_not_found': 'No se encontró ningún lugar para "{address}".',
691
+ 'process.task_created': 'Tarea {id} creada.',
692
+ 'item.map_of': 'Mapa de {position}',
693
+ 'process.no_task': 'No hay ninguna tarea sobre la que actuar.',
689
694
  'workflow.terminated': 'Flujo de trabajo {id} terminado.',
690
695
  'workflow.retried': 'El flujo de trabajo {id} se está ejecutando de nuevo.',
691
696
  'workflow.no_instance': 'No hay ningún flujo de trabajo seleccionado.',
package/src/i18n/fi.ts CHANGED
@@ -676,6 +676,11 @@ export const fi: Record<MessageKey, string> = {
676
676
  'process.queued': "Käynnistetty taustalla (työ {id}).",
677
677
  'process.background_type': "Prosessia ”{name}” ({type}) ei voi suorittaa taustalla.",
678
678
  'workflow.started': "Työnkulku {id} käynnistetty.",
679
+ 'process.no_geocoder': 'Tälle asennukselle ei ole määritetty geokoodaajaa.',
680
+ 'process.address_not_found': 'Paikkaa ei löytynyt haulle "{address}".',
681
+ 'process.task_created': 'Tehtävä {id} luotiin.',
682
+ 'item.map_of': 'Kartta: {position}',
683
+ 'process.no_task': 'Ei käsiteltävää tehtävää.',
679
684
  'workflow.terminated': "Työnkulku {id} lopetettu.",
680
685
  'workflow.retried': "Työnkulku {id} on taas käynnissä.",
681
686
  'workflow.no_instance': "Työnkulkua ei ole valittu.",
package/src/i18n/fr.ts CHANGED
@@ -686,6 +686,11 @@ export const fr: Record<MessageKey, string> = {
686
686
  'process.queued': 'Démarré en arrière-plan (tâche {id}).',
687
687
  'process.background_type': 'Le processus « {name} » ({type}) ne peut pas s’exécuter en arrière-plan.',
688
688
  'workflow.started': 'Workflow {id} démarré.',
689
+ 'process.no_geocoder': 'Aucun géocodeur n\'est configuré pour cette installation.',
690
+ 'process.address_not_found': 'Aucun lieu trouvé pour « {address} ».',
691
+ 'process.task_created': 'Tâche {id} créée.',
692
+ 'item.map_of': 'Carte de {position}',
693
+ 'process.no_task': 'Aucune tâche à traiter.',
689
694
  'workflow.terminated': 'Workflow {id} arrêté.',
690
695
  'workflow.retried': 'Workflow {id} relancé.',
691
696
  'workflow.no_instance': 'Aucun workflow n’est sélectionné.',
package/src/i18n/he.ts CHANGED
@@ -676,6 +676,11 @@ export const he: Record<MessageKey, string> = {
676
676
  'process.queued': "הופעל ברקע (עבודה {id}).",
677
677
  'process.background_type': "התהליך \"{name}\" ({type}) אינו יכול לפעול ברקע.",
678
678
  'workflow.started': "תהליך העבודה {id} הופעל.",
679
+ 'process.no_geocoder': 'לא הוגדר מקודד גאוגרפי בהתקנה זו.',
680
+ 'process.address_not_found': 'לא נמצא מקום עבור "{address}".',
681
+ 'process.task_created': 'המשימה {id} נוצרה.',
682
+ 'item.map_of': 'מפה של {position}',
683
+ 'process.no_task': 'אין משימה לטיפול.',
679
684
  'workflow.terminated': "תהליך העבודה {id} הופסק.",
680
685
  'workflow.retried': "תהליך העבודה {id} פועל שוב.",
681
686
  'workflow.no_instance': "לא נבחר תהליך עבודה.",
package/src/i18n/it.ts CHANGED
@@ -676,6 +676,11 @@ export const it: Record<MessageKey, string> = {
676
676
  'process.queued': "Avviato in background (job {id}).",
677
677
  'process.background_type': "Il processo “{name}” ({type}) non può essere eseguito in background.",
678
678
  'workflow.started': "Workflow {id} avviato.",
679
+ 'process.no_geocoder': 'Nessun geocodificatore configurato per questa installazione.',
680
+ 'process.address_not_found': 'Nessun luogo trovato per "{address}".',
681
+ 'process.task_created': 'Attività {id} creata.',
682
+ 'item.map_of': 'Mappa di {position}',
683
+ 'process.no_task': 'Nessuna attività da gestire.',
679
684
  'workflow.terminated': "Workflow {id} terminato.",
680
685
  'workflow.retried': "Il workflow {id} è di nuovo in esecuzione.",
681
686
  'workflow.no_instance': "Nessun workflow selezionato.",
package/src/i18n/ja.ts CHANGED
@@ -676,6 +676,11 @@ export const ja: Record<MessageKey, string> = {
676
676
  'process.queued': "バックグラウンドで開始しました (ジョブ {id})。",
677
677
  'process.background_type': "プロセス「{name}」({type}) はバックグラウンドで実行できません。",
678
678
  'workflow.started': "ワークフロー {id} を開始しました。",
679
+ 'process.no_geocoder': 'このインストールにはジオコーダーが設定されていません。',
680
+ 'process.address_not_found': '「{address}」の場所が見つかりません。',
681
+ 'process.task_created': 'タスク {id} を作成しました。',
682
+ 'item.map_of': '{position} の地図',
683
+ 'process.no_task': '処理するタスクがありません。',
679
684
  'workflow.terminated': "ワークフロー {id} を終了しました。",
680
685
  'workflow.retried': "ワークフロー {id} を再実行しています。",
681
686
  'workflow.no_instance': "ワークフローが選択されていません。",
package/src/i18n/ko.ts CHANGED
@@ -676,6 +676,11 @@ export const ko: Record<MessageKey, string> = {
676
676
  'process.queued': "백그라운드에서 시작했습니다(작업 {id}).",
677
677
  'process.background_type': "프로세스 '{name}'({type})은(는) 백그라운드에서 실행할 수 없습니다.",
678
678
  'workflow.started': "워크플로 {id}을(를) 시작했습니다.",
679
+ 'process.no_geocoder': '이 설치에는 지오코더가 설정되어 있지 않습니다.',
680
+ 'process.address_not_found': '"{address}"에 대한 장소를 찾을 수 없습니다.',
681
+ 'process.task_created': '작업 {id}이(가) 생성되었습니다.',
682
+ 'item.map_of': '{position} 지도',
683
+ 'process.no_task': '처리할 작업이 없습니다.',
679
684
  'workflow.terminated': "워크플로 {id}을(를) 종료했습니다.",
680
685
  'workflow.retried': "워크플로 {id}이(가) 다시 실행 중입니다.",
681
686
  'workflow.no_instance': "선택한 워크플로가 없습니다.",
package/src/i18n/nb.ts CHANGED
@@ -676,6 +676,11 @@ export const nb: Record<MessageKey, string> = {
676
676
  'process.queued': "Startet i bakgrunnen (jobb {id}).",
677
677
  'process.background_type': "Prosessen «{name}» ({type}) kan ikke kjøre i bakgrunnen.",
678
678
  'workflow.started': "Arbeidsflyt {id} er startet.",
679
+ 'process.no_geocoder': 'Denne installasjonen har ingen geokoder.',
680
+ 'process.address_not_found': 'Fant ikke noe sted for «{address}».',
681
+ 'process.task_created': 'Oppgave {id} er opprettet.',
682
+ 'item.map_of': 'Kart over {position}',
683
+ 'process.no_task': 'Ingen oppgave å behandle.',
679
684
  'workflow.terminated': "Arbeidsflyt {id} er avsluttet.",
680
685
  'workflow.retried': "Arbeidsflyt {id} kjører igjen.",
681
686
  'workflow.no_instance': "Ingen arbeidsflyt er valgt.",
package/src/i18n/pl.ts CHANGED
@@ -676,6 +676,11 @@ export const pl: Record<MessageKey, string> = {
676
676
  'process.queued': "Uruchomiono w tle (zadanie {id}).",
677
677
  'process.background_type': "Proces „{name}” ({type}) nie może działać w tle.",
678
678
  'workflow.started': "Przepływ pracy {id} uruchomiony.",
679
+ 'process.no_geocoder': 'Ta instalacja nie ma skonfigurowanego geokodera.',
680
+ 'process.address_not_found': 'Nie znaleziono miejsca dla „{address}”.',
681
+ 'process.task_created': 'Utworzono zadanie {id}.',
682
+ 'item.map_of': 'Mapa: {position}',
683
+ 'process.no_task': 'Brak zadania do obsłużenia.',
679
684
  'workflow.terminated': "Przepływ pracy {id} przerwany.",
680
685
  'workflow.retried': "Przepływ pracy {id} działa ponownie.",
681
686
  'workflow.no_instance': "Nie wybrano przepływu pracy.",
package/src/i18n/pt.ts CHANGED
@@ -676,6 +676,11 @@ export const pt: Record<MessageKey, string> = {
676
676
  'process.queued': "Iniciado em segundo plano (tarefa {id}).",
677
677
  'process.background_type': "O processo “{name}” ({type}) não pode ser executado em segundo plano.",
678
678
  'workflow.started': "Fluxo de trabalho {id} iniciado.",
679
+ 'process.no_geocoder': 'Esta instalação não tem um geocodificador configurado.',
680
+ 'process.address_not_found': 'Nenhum lugar encontrado para "{address}".',
681
+ 'process.task_created': 'Tarefa {id} criada.',
682
+ 'item.map_of': 'Mapa de {position}',
683
+ 'process.no_task': 'Nenhuma tarefa para tratar.',
679
684
  'workflow.terminated': "Fluxo de trabalho {id} terminado.",
680
685
  'workflow.retried': "O fluxo de trabalho {id} está novamente em execução.",
681
686
  'workflow.no_instance': "Nenhum fluxo de trabalho selecionado.",
package/src/i18n/ru.ts CHANGED
@@ -676,6 +676,11 @@ export const ru: Record<MessageKey, string> = {
676
676
  'process.queued': "Запущено в фоне (задание {id}).",
677
677
  'process.background_type': "Процесс «{name}» ({type}) нельзя выполнять в фоне.",
678
678
  'workflow.started': "Рабочий процесс {id} запущен.",
679
+ 'process.no_geocoder': 'Для этой установки не настроен геокодер.',
680
+ 'process.address_not_found': 'Место для «{address}» не найдено.',
681
+ 'process.task_created': 'Задача {id} создана.',
682
+ 'item.map_of': 'Карта: {position}',
683
+ 'process.no_task': 'Нет задачи для обработки.',
679
684
  'workflow.terminated': "Рабочий процесс {id} прерван.",
680
685
  'workflow.retried': "Рабочий процесс {id} снова выполняется.",
681
686
  'workflow.no_instance': "Рабочий процесс не выбран.",
package/src/i18n/sv.ts CHANGED
@@ -676,6 +676,11 @@ export const sv: Record<MessageKey, string> = {
676
676
  'process.queued': "Startad i bakgrunden (jobb {id}).",
677
677
  'process.background_type': "Processen ”{name}” ({type}) kan inte köras i bakgrunden.",
678
678
  'workflow.started': "Arbetsflöde {id} har startats.",
679
+ 'process.no_geocoder': 'Den här installationen har ingen geokodare.',
680
+ 'process.address_not_found': 'Ingen plats hittades för "{address}".',
681
+ 'process.task_created': 'Uppgift {id} har skapats.',
682
+ 'item.map_of': 'Karta över {position}',
683
+ 'process.no_task': 'Ingen uppgift att hantera.',
679
684
  'workflow.terminated': "Arbetsflöde {id} har avslutats.",
680
685
  'workflow.retried': "Arbetsflöde {id} körs igen.",
681
686
  'workflow.no_instance': "Inget arbetsflöde är valt.",
package/src/i18n/tr.ts CHANGED
@@ -676,6 +676,11 @@ export const tr: Record<MessageKey, string> = {
676
676
  'process.queued': "Arka planda başlatıldı (iş {id}).",
677
677
  'process.background_type': "“{name}” işlemi ({type}) arka planda çalıştırılamaz.",
678
678
  'workflow.started': "{id} iş akışı başlatıldı.",
679
+ 'process.no_geocoder': 'Bu kurulum için bir coğrafi kodlayıcı ayarlanmamış.',
680
+ 'process.address_not_found': '"{address}" için yer bulunamadı.',
681
+ 'process.task_created': '{id} görevi oluşturuldu.',
682
+ 'item.map_of': '{position} haritası',
683
+ 'process.no_task': 'İşlenecek görev yok.',
679
684
  'workflow.terminated': "{id} iş akışı sonlandırıldı.",
680
685
  'workflow.retried': "{id} iş akışı yeniden çalışıyor.",
681
686
  'workflow.no_instance': "İş akışı seçilmedi.",
package/src/i18n/uk.ts CHANGED
@@ -676,6 +676,11 @@ export const uk: Record<MessageKey, string> = {
676
676
  'process.queued': "Запущено у фоні (завдання {id}).",
677
677
  'process.background_type': "Процес «{name}» ({type}) не можна виконувати у фоні.",
678
678
  'workflow.started': "Робочий процес {id} запущено.",
679
+ 'process.no_geocoder': 'Для цього встановлення не налаштовано геокодер.',
680
+ 'process.address_not_found': 'Місце для «{address}» не знайдено.',
681
+ 'process.task_created': 'Завдання {id} створено.',
682
+ 'item.map_of': 'Карта: {position}',
683
+ 'process.no_task': 'Немає завдання для обробки.',
679
684
  'workflow.terminated': "Робочий процес {id} перервано.",
680
685
  'workflow.retried': "Робочий процес {id} знову виконується.",
681
686
  'workflow.no_instance': "Робочий процес не вибрано.",
package/src/i18n/zh.ts CHANGED
@@ -676,6 +676,11 @@ export const zh: Record<MessageKey, string> = {
676
676
  'process.queued': "已在后台启动(作业 {id})。",
677
677
  'process.background_type': "流程“{name}”({type}) 不能在后台运行。",
678
678
  'workflow.started': "工作流 {id} 已启动。",
679
+ 'process.no_geocoder': '此安装未设置地理编码器。',
680
+ 'process.address_not_found': '找不到"{address}"的位置。',
681
+ 'process.task_created': '已创建任务 {id}。',
682
+ 'item.map_of': '{position} 的地图',
683
+ 'process.no_task': '没有可处理的任务。',
679
684
  'workflow.terminated': "工作流 {id} 已终止。",
680
685
  'workflow.retried': "工作流 {id} 正在重新运行。",
681
686
  'workflow.no_instance': "未选择工作流。",
package/src/i18n.ts CHANGED
@@ -703,6 +703,11 @@ const en = {
703
703
  'process.queued': 'Started in the background (job {id}).',
704
704
  'process.background_type': 'Process "{name}" ({type}) cannot run in the background.',
705
705
  'workflow.started': 'Workflow {id} started.',
706
+ 'process.no_geocoder': 'No geocoder is set up for this installation.',
707
+ 'process.address_not_found': 'No place found for "{address}".',
708
+ 'process.task_created': 'Task {id} created.',
709
+ 'item.map_of': 'Map of {position}',
710
+ 'process.no_task': 'No task to act on.',
706
711
  'workflow.terminated': 'Workflow {id} terminated.',
707
712
  'workflow.retried': 'Workflow {id} is running again.',
708
713
  'workflow.no_instance': 'No workflow is selected.',
@@ -1607,6 +1612,11 @@ const nl: Record<MessageKey, string> = {
1607
1612
  'process.queued': 'Op de achtergrond gestart (taak {id}).',
1608
1613
  'process.background_type': 'Proces "{name}" ({type}) kan niet op de achtergrond draaien.',
1609
1614
  'workflow.started': 'Workflow {id} is gestart.',
1615
+ 'process.no_geocoder': 'Deze installatie heeft geen geocoder.',
1616
+ 'process.address_not_found': 'Geen plaats gevonden voor "{address}".',
1617
+ 'process.task_created': 'Taak {id} is aangemaakt.',
1618
+ 'item.map_of': 'Kaart van {position}',
1619
+ 'process.no_task': 'Geen taak om op te handelen.',
1610
1620
  'workflow.terminated': 'Workflow {id} is beëindigd.',
1611
1621
  'workflow.retried': 'Workflow {id} loopt weer.',
1612
1622
  'workflow.no_instance': 'Er is geen workflow gekozen.',