pgkiln 0.43.0 → 0.45.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 (69) hide show
  1. package/db/migrations/100_generated_labels.sql +381 -0
  2. package/db/migrations/101_theme_choice.sql +15 -0
  3. package/db/migrations/102_app_computations.sql +345 -0
  4. package/db/migrations/103_shortcuts.sql +341 -0
  5. package/db/migrations/104_report_queries.sql +413 -0
  6. package/db/migrations/105_component_settings_groups.sql +367 -0
  7. package/docs/apex-feature-parity.md +12 -12
  8. package/docs/guide/02-concepts.md +24 -0
  9. package/docs/guide/03-builder.md +48 -9
  10. package/docs/guide/04-pages-and-regions.md +20 -4
  11. package/docs/guide/06-processing.md +22 -0
  12. package/docs/guide/09-reference.md +14 -0
  13. package/docs/guide/12-development.md +2 -1
  14. package/docs/guide/14-globalization.md +5 -1
  15. package/docs/guide/16-files.md +23 -0
  16. package/docs/guide/18-cli.md +4 -0
  17. package/package.json +1 -1
  18. package/public/app.css +40 -1
  19. package/public/app.js +69 -1
  20. package/src/appfiles.ts +18 -2
  21. package/src/builder/advisor.ts +7 -2
  22. package/src/builder/appsheets.ts +53 -13
  23. package/src/builder/appwizard.ts +5 -3
  24. package/src/builder/code-editor.ts +6 -1
  25. package/src/builder/components.ts +85 -3
  26. package/src/builder/forms.ts +1 -1
  27. package/src/builder/shared.ts +39 -6
  28. package/src/builder/subscriptions.ts +30 -4
  29. package/src/cli/replace.ts +6 -2
  30. package/src/i18n/ar.ts +2 -0
  31. package/src/i18n/cs.ts +2 -0
  32. package/src/i18n/da.ts +2 -0
  33. package/src/i18n/de.ts +2 -0
  34. package/src/i18n/el.ts +2 -0
  35. package/src/i18n/es.ts +2 -0
  36. package/src/i18n/fi.ts +2 -0
  37. package/src/i18n/fr.ts +2 -0
  38. package/src/i18n/he.ts +2 -0
  39. package/src/i18n/it.ts +2 -0
  40. package/src/i18n/ja.ts +2 -0
  41. package/src/i18n/ko.ts +2 -0
  42. package/src/i18n/nb.ts +2 -0
  43. package/src/i18n/pl.ts +2 -0
  44. package/src/i18n/pt.ts +2 -0
  45. package/src/i18n/ru.ts +2 -0
  46. package/src/i18n/sv.ts +2 -0
  47. package/src/i18n/tr.ts +2 -0
  48. package/src/i18n/uk.ts +2 -0
  49. package/src/i18n/zh.ts +2 -0
  50. package/src/i18n.ts +4 -0
  51. package/src/metadata.ts +14 -1
  52. package/src/runtime/account.ts +5 -5
  53. package/src/runtime/component-settings.ts +42 -0
  54. package/src/runtime/context.ts +4 -0
  55. package/src/runtime/devbar.ts +16 -13
  56. package/src/runtime/documents.ts +29 -3
  57. package/src/runtime/engine.ts +2 -2
  58. package/src/runtime/facets.ts +34 -3
  59. package/src/runtime/grid.ts +22 -4
  60. package/src/runtime/items.ts +14 -11
  61. package/src/runtime/locale.ts +4 -1
  62. package/src/runtime/logic.ts +12 -7
  63. package/src/runtime/regions.ts +4 -3
  64. package/src/runtime/render.ts +8 -2
  65. package/src/runtime/report.ts +16 -10
  66. package/src/runtime/routes.ts +9 -6
  67. package/src/runtime/shortcuts.ts +43 -0
  68. package/src/security.ts +2 -1
  69. package/src/subscriptions.ts +67 -0
@@ -0,0 +1,367 @@
1
+ -- =====================================================================
2
+ -- 105: component settings and component groups (APEX: Shared Components →
3
+ -- Component Settings, Component Groups)
4
+ --
5
+ -- meta.app.component_settings: an application's defaults for its built-in
6
+ -- component types, used where a component sets nothing itself:
7
+ -- report_rows rows per page of a report (15)
8
+ -- grid_rows rows per page of an interactive grid (25)
9
+ -- popup_lov_rows rows per page of a popup list of values (25)
10
+ -- lov_max_rows rows a list of values reads (5,000)
11
+ -- rating_max stars of a star rating (5)
12
+ -- The builder bounds the values (src/runtime/component-settings.ts); the
13
+ -- runtime bounds them again. Exported with the application (export_app
14
+ -- copies every column of meta.app).
15
+ --
16
+ -- meta.component_group: a named set of an application's components that
17
+ -- other applications subscribe to at once (subscriptions, migration 056):
18
+ -- members are "kind:name" of its lists of values, authorization schemes,
19
+ -- build options, template components and lists. Builder data: the
20
+ -- runtime does not read it.
21
+ -- export_app / import_app are redefined from 104 to carry component_groups.
22
+ -- =====================================================================
23
+ alter table meta.app add column component_settings jsonb not null default '{}'
24
+ check (jsonb_typeof(component_settings) = 'object');
25
+ comment on column meta.app.component_settings is 'Component settings (105): defaults of built-in component types (report_rows, grid_rows, popup_lov_rows, lov_max_rows, rating_max)';
26
+
27
+ create table meta.component_group (
28
+ id serial primary key,
29
+ app_id int not null references meta.app on delete cascade,
30
+ name text not null check (name ~ '^[A-Za-z0-9][A-Za-z0-9 _.-]{0,59}$'),
31
+ description text,
32
+ members text[] not null default '{}' check (cardinality(members) <= 500),
33
+ unique (app_id, name)
34
+ );
35
+ revoke all on meta.component_group from public;
36
+ comment on table meta.component_group is 'Component groups (105): sets of an application''s components ("kind:name") that other applications subscribe to at once';
37
+
38
+ -- (090) like the other tables of an application
39
+ alter table meta.component_group enable row level security;
40
+ create policy workspace on meta.component_group
41
+ using ((select meta.isolation_off()) or app_id in (select meta.visible_apps()))
42
+ with check ((select meta.isolation_off()) or app_id in (select meta.visible_apps()));
43
+
44
+ create or replace function meta.export_app(p_alias text) returns jsonb
45
+ language sql stable set search_path = meta, pg_catalog as $$
46
+ select jsonb_build_object(
47
+ 'format', 'pgkiln/2',
48
+ -- (083) availability and build status belong to the installation
49
+ 'app', to_jsonb(a) - 'id' - 'created_at' - 'updated_at' - 'availability' - 'availability_message' - 'availability_users' - 'build_status',
50
+ 'authz_schemes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.authz_scheme x where x.app_id = a.id), '[]'),
51
+ 'app_items', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.app_item x where x.app_id = a.id), '[]'),
52
+ 'app_processes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.seq, x.id) from meta.app_process x where x.app_id = a.id), '[]'),
53
+ 'lovs', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.lov x where x.app_id = a.id), '[]'),
54
+ 'group_roles', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.group_name, x.role) from meta.app_group_role x where x.app_id = a.id), '[]'),
55
+ 'text_messages', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.name, x.language) from meta.text_message x where x.app_id = a.id), '[]'),
56
+ 'translations', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.language, x.source) from meta.translation x where x.app_id = a.id), '[]'),
57
+ 'report_layouts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'logo' || jsonb_build_object('logo', encode(x.logo, 'base64')) order by x.name)
58
+ from meta.report_layout x where x.app_id = a.id), '[]'),
59
+ 'automations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'next_run_at' - 'last_run_at' - 'last_status' - 'code' order by x.name)
60
+ from meta.automation x where x.app_id = a.id), '[]'),
61
+ -- (044) the actions of the automations, by automation name
62
+ 'automation_actions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.automation_name collate "C", x.seq, x.name collate "C")
63
+ from meta.automation_action x where x.app_id = a.id), '[]'),
64
+ 'document_templates', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.document_template x where x.app_id = a.id), '[]'),
65
+ 'task_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.task_definition x where x.app_id = a.id), '[]'),
66
+ 'workflow_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.workflow_definition x where x.app_id = a.id), '[]'),
67
+ 'rest_modules', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.rest_module x where x.app_id = a.id), '[]'),
68
+ 'template_components', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.static_id) from meta.template_component x where x.app_id = a.id), '[]'),
69
+ 'build_options', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.build_option x where x.app_id = a.id), '[]'),
70
+ -- (030) secrets never leave the installation: they are entered again after an import
71
+ -- (050) nor do the password and refresh token of the OAuth2 flows, or the token state
72
+ 'web_credentials', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'secret_enc' - 'password_enc' - 'refresh_token_enc' - 'token_refreshed_at' order by x.name)
73
+ from meta.web_credential x where x.app_id = a.id), '[]'),
74
+ -- (050) the synchronisation's state (next and last run) belongs to the installation
75
+ 'rest_sources', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'sync_next_at' - 'sync_last_at' - 'sync_last_status' order by x.name)
76
+ from meta.rest_source x where x.app_id = a.id), '[]'),
77
+ -- (041) data load definitions
78
+ 'data_load_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.data_load_def x where x.app_id = a.id), '[]'),
79
+ -- (042) lists; entries keep their ids so parents can be remapped on import
80
+ 'lists', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.list x where x.app_id = a.id), '[]'),
81
+ 'list_entries', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.list_name, x.parent_id nulls first, x.seq, x.id) from meta.list_entry x where x.app_id = a.id), '[]'),
82
+ -- (042) supporting objects: install, upgrade and deinstall scripts (never run on import)
83
+ 'supporting_scripts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.supporting_script x where x.app_id = a.id), '[]'),
84
+ -- (069) plug-ins (their files are static files, their templates template components)
85
+ 'plugins', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.plugin x where x.app_id = a.id), '[]'),
86
+ -- (080) search configurations
87
+ 'search_configs', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.search_config x where x.app_id = a.id), '[]'),
88
+ -- (081) e-mail templates
89
+ 'email_templates', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.email_template x where x.app_id = a.id), '[]'),
90
+ -- (086) application settings (with their values) and dynamic translations
91
+ 'app_settings', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.app_setting x where x.app_id = a.id), '[]'),
92
+ 'page_groups', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.page_group x where x.app_id = a.id), '[]'),
93
+ -- (105) component groups
94
+ 'component_groups', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.component_group x where x.app_id = a.id), '[]'),
95
+ -- (104) report queries
96
+ 'report_queries', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.report_query x where x.app_id = a.id), '[]'),
97
+ -- (103) shortcuts
98
+ 'shortcuts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.shortcut x where x.app_id = a.id), '[]'),
99
+ -- (102) application computations
100
+ 'app_computations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.seq, x.id) from meta.app_computation x where x.app_id = a.id), '[]'),
101
+ 'dynamic_translations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.language, x.source collate "C") from meta.dynamic_translation x where x.app_id = a.id), '[]'),
102
+ -- (068) static application files (JavaScript, CSS, images, fonts) as base64
103
+ 'static_files', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'content' - 'updated_at' || jsonb_build_object('content', translate(encode(x.content, 'base64'), E'\n', '')) order by x.name)
104
+ from meta.static_file x where x.app_id = a.id), '[]'),
105
+ -- nav entries and regions keep their ids, so parents and references can be remapped on import
106
+ 'nav', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.parent_id nulls first, x.seq, x.id) from meta.nav_entry x where x.app_id = a.id), '[]'),
107
+ 'pages', coalesce((
108
+ select jsonb_agg(to_jsonb(p) - 'id' - 'app_id' || jsonb_build_object(
109
+ 'regions', coalesce((select jsonb_agg(to_jsonb(r) - 'page_id' order by r.seq, r.id) from meta.region r where r.page_id = p.id), '[]'),
110
+ 'items', coalesce((select jsonb_agg(to_jsonb(i) - 'id' - 'page_id' order by i.seq, i.id) from meta.item i where i.page_id = p.id), '[]'),
111
+ 'buttons', coalesce((select jsonb_agg(to_jsonb(b) - 'id' - 'page_id' order by b.seq, b.id) from meta.button b where b.page_id = p.id), '[]'),
112
+ 'dynamic_actions', coalesce((select jsonb_agg(to_jsonb(d) - 'id' - 'page_id' order by d.seq, d.id) from meta.dynamic_action d where d.page_id = p.id), '[]'),
113
+ 'validations', coalesce((select jsonb_agg(to_jsonb(v) - 'id' - 'page_id' order by v.seq, v.id) from meta.validation v where v.page_id = p.id), '[]'),
114
+ 'processes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.process x where x.page_id = p.id), '[]'),
115
+ 'computations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.computation x where x.page_id = p.id), '[]'),
116
+ 'branches', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.branch x where x.page_id = p.id), '[]')
117
+ ) order by p.page_no)
118
+ from meta.page p where p.app_id = a.id), '[]'))
119
+ from meta.app a
120
+ where a.alias = p_alias
121
+ $$;
122
+
123
+ -- ---------------------------------------------------------------- import
124
+ -- Same as 081, plus app_settings and dynamic_translations.
125
+
126
+ create or replace function meta.import_app(p_doc jsonb, p_alias text default null) returns int
127
+ language plpgsql set search_path = meta, pg_catalog as $$
128
+ declare
129
+ v_app_id int;
130
+ v_page_id int;
131
+ v_page jsonb;
132
+ v_e jsonb;
133
+ v_rmap jsonb;
134
+ v_nmap jsonb := '{}';
135
+ v_lmap jsonb := '{}';
136
+ v_new_id int;
137
+ begin
138
+ if p_doc->>'format' is distinct from 'pgkiln/2' then
139
+ raise exception 'unsupported export format %', coalesce(p_doc->>'format', '(none)');
140
+ end if;
141
+
142
+ insert into meta.app
143
+ select (jsonb_populate_record(null::meta.app, p_doc->'app' || jsonb_build_object(
144
+ 'id', nextval('meta.app_id_seq'),
145
+ 'alias', coalesce(p_alias, p_doc->'app'->>'alias'),
146
+ 'created_at', now(), 'updated_at', now()))).*
147
+ returning id into v_app_id;
148
+
149
+ insert into meta.authz_scheme
150
+ select (jsonb_populate_record(null::meta.authz_scheme, e || jsonb_build_object('id', nextval('meta.authz_scheme_id_seq'), 'app_id', v_app_id))).*
151
+ from jsonb_array_elements(coalesce(p_doc->'authz_schemes', '[]')) e;
152
+ insert into meta.app_item
153
+ select (jsonb_populate_record(null::meta.app_item, e || jsonb_build_object('id', nextval('meta.app_item_id_seq'), 'app_id', v_app_id))).*
154
+ from jsonb_array_elements(coalesce(p_doc->'app_items', '[]')) e;
155
+ insert into meta.app_process
156
+ select (jsonb_populate_record(null::meta.app_process, e || jsonb_build_object('id', nextval('meta.app_process_id_seq'), 'app_id', v_app_id))).*
157
+ from jsonb_array_elements(coalesce(p_doc->'app_processes', '[]')) e;
158
+ insert into meta.lov
159
+ select (jsonb_populate_record(null::meta.lov, e || jsonb_build_object('id', nextval('meta.lov_id_seq'), 'app_id', v_app_id))).*
160
+ from jsonb_array_elements(coalesce(p_doc->'lovs', '[]')) e;
161
+ insert into meta.app_group_role
162
+ select (jsonb_populate_record(null::meta.app_group_role, e || jsonb_build_object('app_id', v_app_id))).*
163
+ from jsonb_array_elements(coalesce(p_doc->'group_roles', '[]')) e;
164
+ insert into meta.text_message
165
+ select (jsonb_populate_record(null::meta.text_message, e || jsonb_build_object('app_id', v_app_id))).*
166
+ from jsonb_array_elements(coalesce(p_doc->'text_messages', '[]')) e;
167
+ insert into meta.translation
168
+ select (jsonb_populate_record(null::meta.translation, e || jsonb_build_object('app_id', v_app_id))).*
169
+ from jsonb_array_elements(coalesce(p_doc->'translations', '[]')) e;
170
+ insert into meta.report_layout
171
+ select (jsonb_populate_record(null::meta.report_layout, (e - 'logo') || jsonb_build_object(
172
+ 'id', nextval('meta.report_layout_id_seq'), 'app_id', v_app_id,
173
+ 'logo', null))).*
174
+ from jsonb_array_elements(coalesce(p_doc->'report_layouts', '[]')) e;
175
+ update meta.report_layout l
176
+ set logo = decode(e->>'logo', 'base64')
177
+ from jsonb_array_elements(coalesce(p_doc->'report_layouts', '[]')) e
178
+ where l.app_id = v_app_id and l.name = e->>'name' and e->>'logo' is not null;
179
+ -- imported automations start switched off: the copy must not run the original's jobs unasked.
180
+ -- (044) a file with actions: the automations' code is ignored; an older file: code becomes the single action
181
+ insert into meta.automation
182
+ select (jsonb_populate_record(null::meta.automation, '{"error_handling": "stop"}'::jsonb
183
+ || jsonb_strip_nulls(case when p_doc ? 'automation_actions' then e - 'code' else e end)
184
+ || jsonb_build_object('id', nextval('meta.automation_id_seq'), 'app_id', v_app_id, 'enabled', false))).*
185
+ from jsonb_array_elements(coalesce(p_doc->'automations', '[]')) e;
186
+ insert into meta.automation_action
187
+ select (jsonb_populate_record(null::meta.automation_action, '{"seq": 10}'::jsonb || jsonb_strip_nulls(e)
188
+ || jsonb_build_object('id', nextval('meta.automation_action_id_seq'), 'app_id', v_app_id))).*
189
+ from jsonb_array_elements(coalesce(p_doc->'automation_actions', '[]')) e;
190
+ insert into meta.document_template
191
+ select (jsonb_populate_record(null::meta.document_template, e || jsonb_build_object('id', nextval('meta.document_template_id_seq'), 'app_id', v_app_id))).*
192
+ from jsonb_array_elements(coalesce(p_doc->'document_templates', '[]')) e;
193
+ insert into meta.task_definition
194
+ select (jsonb_populate_record(null::meta.task_definition, e || jsonb_build_object('id', nextval('meta.task_definition_id_seq'), 'app_id', v_app_id))).*
195
+ from jsonb_array_elements(coalesce(p_doc->'task_definitions', '[]')) e;
196
+ insert into meta.workflow_definition
197
+ select (jsonb_populate_record(null::meta.workflow_definition, e || jsonb_build_object('id', nextval('meta.workflow_definition_id_seq'), 'app_id', v_app_id))).*
198
+ from jsonb_array_elements(coalesce(p_doc->'workflow_definitions', '[]')) e;
199
+ insert into meta.rest_module
200
+ select (jsonb_populate_record(null::meta.rest_module, e || jsonb_build_object('id', nextval('meta.rest_module_id_seq'), 'app_id', v_app_id))).*
201
+ from jsonb_array_elements(coalesce(p_doc->'rest_modules', '[]')) e;
202
+ -- (028) template components: regions and report columns refer to them by static id
203
+ insert into meta.template_component
204
+ select (jsonb_populate_record(null::meta.template_component, e || jsonb_build_object('id', nextval('meta.template_component_id_seq'), 'app_id', v_app_id))).*
205
+ from jsonb_array_elements(coalesce(p_doc->'template_components', '[]')) e;
206
+ -- (029) build options (components name them by name)
207
+ insert into meta.build_option
208
+ select (jsonb_populate_record(null::meta.build_option, e || jsonb_build_object('id', nextval('meta.build_option_id_seq'), 'app_id', v_app_id))).*
209
+ from jsonb_array_elements(coalesce(p_doc->'build_options', '[]')) e;
210
+ -- (030) web credentials arrive without a secret, whatever the document holds
211
+ -- (050) nor a password or refresh token
212
+ insert into meta.web_credential
213
+ select (jsonb_populate_record(null::meta.web_credential, '{"type": "basic", "valid_for": [], "grant_type": "client_credentials"}'::jsonb
214
+ || jsonb_strip_nulls(e - 'secret_enc' - 'password_enc' - 'refresh_token_enc' - 'token_refreshed_at')
215
+ || jsonb_build_object('id', nextval('meta.web_credential_id_seq'), 'app_id', v_app_id))).*
216
+ from jsonb_array_elements(coalesce(p_doc->'web_credentials', '[]')) e;
217
+ -- (050) write-back operations and synchronisation; an imported synchronisation starts switched off
218
+ insert into meta.rest_source
219
+ select (jsonb_populate_record(null::meta.rest_source, '{"method": "GET", "headers": {}, "params": [], "columns": [], "cache_seconds": 0, "timeout_s": 10, "max_rows": 1000,
220
+ "operations": {}, "key_columns": [], "sync_mode": "merge", "sync_delete": false, "sync_time_zone": "UTC"}'::jsonb
221
+ || jsonb_strip_nulls(e - 'sync_next_at' - 'sync_last_at' - 'sync_last_status')
222
+ || jsonb_build_object('id', nextval('meta.rest_source_id_seq'), 'app_id', v_app_id, 'sync_enabled', false))).*
223
+ from jsonb_array_elements(coalesce(p_doc->'rest_sources', '[]')) e;
224
+
225
+ -- (041) data load definitions (the data_load process names them)
226
+ insert into meta.data_load_def
227
+ select (jsonb_populate_record(null::meta.data_load_def, '{"format": "auto", "headers": true, "mode": "append", "skip_errors": false, "columns": []}'::jsonb
228
+ || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.data_load_def_id_seq'), 'app_id', v_app_id))).*
229
+ from jsonb_array_elements(coalesce(p_doc->'data_load_definitions', '[]')) e;
230
+
231
+ -- (042) lists and their entries, supporting scripts. Entries are inserted
232
+ -- without a parent first and the parents set afterwards, so the order of
233
+ -- the entries in the document doesn't matter (a moved entry may have a
234
+ -- higher id than its children).
235
+ insert into meta.list
236
+ select (jsonb_populate_record(null::meta.list, '{"type": "static"}'::jsonb || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.list_id_seq'), 'app_id', v_app_id))).*
237
+ from jsonb_array_elements(coalesce(p_doc->'lists', '[]')) e;
238
+ for v_e in select * from jsonb_array_elements(coalesce(p_doc->'list_entries', '[]')) loop
239
+ insert into meta.list_entry
240
+ select (jsonb_populate_record(null::meta.list_entry, '{"target_items": {}}'::jsonb || jsonb_strip_nulls(v_e) || jsonb_build_object(
241
+ 'id', nextval('meta.list_entry_id_seq'), 'app_id', v_app_id, 'parent_id', null))).*
242
+ returning id into v_new_id;
243
+ if v_e->>'id' is not null then
244
+ v_lmap := v_lmap || jsonb_build_object(v_e->>'id', v_new_id);
245
+ end if;
246
+ end loop;
247
+ update meta.list_entry x
248
+ set parent_id = (v_lmap->>(e->>'parent_id'))::int
249
+ from jsonb_array_elements(coalesce(p_doc->'list_entries', '[]')) e
250
+ where e->>'parent_id' is not null and v_lmap ? (e->>'parent_id') and v_lmap ? (e->>'id')
251
+ and x.id = (v_lmap->>(e->>'id'))::int;
252
+ insert into meta.supporting_script
253
+ select (jsonb_populate_record(null::meta.supporting_script, '{"kind": "install", "seq": 10, "script": ""}'::jsonb || jsonb_strip_nulls(e)
254
+ || jsonb_build_object('id', nextval('meta.supporting_script_id_seq'), 'app_id', v_app_id))).*
255
+ from jsonb_array_elements(coalesce(p_doc->'supporting_scripts', '[]')) e;
256
+
257
+ -- (069) plug-ins
258
+ insert into meta.plugin
259
+ select (jsonb_populate_record(null::meta.plugin, e || jsonb_build_object('id', nextval('meta.plugin_id_seq'), 'app_id', v_app_id))).*
260
+ from jsonb_array_elements(coalesce(p_doc->'plugins', '[]')) e;
261
+
262
+ -- (080) search configurations (search regions name them)
263
+ insert into meta.search_config
264
+ select (jsonb_populate_record(null::meta.search_config, '{"seq": 10, "source_type": "table", "searchable_columns": [], "max_results": 10}'::jsonb
265
+ || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.search_config_id_seq'), 'app_id', v_app_id))).*
266
+ from jsonb_array_elements(coalesce(p_doc->'search_configs', '[]')) e;
267
+
268
+ -- (081) e-mail templates (meta.send_mail_template and the send_email process name them)
269
+ insert into meta.email_template
270
+ select (jsonb_populate_record(null::meta.email_template, e || jsonb_build_object('id', nextval('meta.email_template_id_seq'), 'app_id', v_app_id))).*
271
+ from jsonb_array_elements(coalesce(p_doc->'email_templates', '[]')) e;
272
+
273
+ -- (105) component groups
274
+ insert into meta.component_group
275
+ select (jsonb_populate_record(null::meta.component_group, '{"members": []}'::jsonb || jsonb_strip_nulls(e)
276
+ || jsonb_build_object('id', nextval('meta.component_group_id_seq'), 'app_id', v_app_id))).*
277
+ from jsonb_array_elements(coalesce(p_doc->'component_groups', '[]')) e;
278
+
279
+ -- (104) report queries
280
+ insert into meta.report_query
281
+ select (jsonb_populate_record(null::meta.report_query, '{"format": "pdf"}'::jsonb || jsonb_strip_nulls(e)
282
+ || jsonb_build_object('id', nextval('meta.report_query_id_seq'), 'app_id', v_app_id))).*
283
+ from jsonb_array_elements(coalesce(p_doc->'report_queries', '[]')) e;
284
+
285
+ -- (103) shortcuts
286
+ insert into meta.shortcut
287
+ select (jsonb_populate_record(null::meta.shortcut, '{"type": "text"}'::jsonb || jsonb_strip_nulls(e)
288
+ || jsonb_build_object('id', nextval('meta.shortcut_id_seq'), 'app_id', v_app_id))).*
289
+ from jsonb_array_elements(coalesce(p_doc->'shortcuts', '[]')) e;
290
+
291
+ -- (102) application computations
292
+ insert into meta.app_computation
293
+ select (jsonb_populate_record(null::meta.app_computation, '{"seq": 10, "point": "before_header", "type": "static"}'::jsonb || jsonb_strip_nulls(e)
294
+ || jsonb_build_object('id', nextval('meta.app_computation_id_seq'), 'app_id', v_app_id))).*
295
+ from jsonb_array_elements(coalesce(p_doc->'app_computations', '[]')) e;
296
+
297
+ -- (089) page groups
298
+ insert into meta.page_group
299
+ select (jsonb_populate_record(null::meta.page_group, e || jsonb_build_object('id', nextval('meta.page_group_id_seq'), 'app_id', v_app_id))).*
300
+ from jsonb_array_elements(coalesce(p_doc->'page_groups', '[]')) e;
301
+
302
+ -- (086) application settings and dynamic translations
303
+ insert into meta.app_setting
304
+ select (jsonb_populate_record(null::meta.app_setting, e || jsonb_build_object('id', nextval('meta.app_setting_id_seq'), 'app_id', v_app_id))).*
305
+ from jsonb_array_elements(coalesce(p_doc->'app_settings', '[]')) e;
306
+ insert into meta.dynamic_translation
307
+ select (jsonb_populate_record(null::meta.dynamic_translation, e || jsonb_build_object('id', nextval('meta.dynamic_translation_id_seq'), 'app_id', v_app_id))).*
308
+ from jsonb_array_elements(coalesce(p_doc->'dynamic_translations', '[]')) e;
309
+
310
+ -- (068) static application files
311
+ insert into meta.static_file (app_id, name, mime, content, updated_at)
312
+ select v_app_id, e->>'name', e->>'mime', decode(coalesce(e->>'content', ''), 'base64'), now()
313
+ from jsonb_array_elements(coalesce(p_doc->'static_files', '[]')) e;
314
+
315
+ for v_e in select * from jsonb_array_elements(coalesce(p_doc->'nav', '[]')) loop
316
+ insert into meta.nav_entry
317
+ select (jsonb_populate_record(null::meta.nav_entry, v_e || jsonb_build_object(
318
+ 'id', nextval('meta.nav_entry_id_seq'), 'app_id', v_app_id,
319
+ 'parent_id', v_nmap->>(v_e->>'parent_id')))).*
320
+ returning id into v_new_id;
321
+ v_nmap := v_nmap || jsonb_build_object(v_e->>'id', v_new_id);
322
+ end loop;
323
+
324
+ for v_page in select * from jsonb_array_elements(coalesce(p_doc->'pages', '[]')) loop
325
+ insert into meta.page
326
+ select (jsonb_populate_record(null::meta.page, v_page || jsonb_build_object('id', nextval('meta.page_id_seq'), 'app_id', v_app_id))).*
327
+ returning id into v_page_id;
328
+
329
+ v_rmap := '{}';
330
+ for v_e in select * from jsonb_array_elements(coalesce(v_page->'regions', '[]')) loop
331
+ insert into meta.region
332
+ select (jsonb_populate_record(null::meta.region, v_e || jsonb_build_object('id', nextval('meta.region_id_seq'), 'page_id', v_page_id))).*
333
+ returning id into v_new_id;
334
+ v_rmap := v_rmap || jsonb_build_object(v_e->>'id', v_new_id);
335
+ end loop;
336
+ -- facet and map regions point at their report region by id
337
+ update meta.region r
338
+ set config = jsonb_set(r.config, '{report}', to_jsonb((v_rmap->>(r.config->>'report'))::int))
339
+ where r.page_id = v_page_id and r.type in ('facets', 'smart_filters', 'map') and v_rmap ? (r.config->>'report');
340
+
341
+ insert into meta.item
342
+ select (jsonb_populate_record(null::meta.item, e || jsonb_build_object('id', nextval('meta.item_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
343
+ from jsonb_array_elements(coalesce(v_page->'items', '[]')) e;
344
+ insert into meta.button
345
+ select (jsonb_populate_record(null::meta.button, e || jsonb_build_object('id', nextval('meta.button_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
346
+ from jsonb_array_elements(coalesce(v_page->'buttons', '[]')) e;
347
+ insert into meta.dynamic_action
348
+ select (jsonb_populate_record(null::meta.dynamic_action, e || jsonb_build_object('id', nextval('meta.dynamic_action_id_seq'), 'page_id', v_page_id, 'affected_region_id', v_rmap->>(e->>'affected_region_id')))).*
349
+ from jsonb_array_elements(coalesce(v_page->'dynamic_actions', '[]')) e;
350
+ insert into meta.validation
351
+ select (jsonb_populate_record(null::meta.validation, e || jsonb_build_object('id', nextval('meta.validation_id_seq'), 'page_id', v_page_id))).*
352
+ from jsonb_array_elements(coalesce(v_page->'validations', '[]')) e;
353
+ insert into meta.process
354
+ select (jsonb_populate_record(null::meta.process, e || jsonb_build_object('id', nextval('meta.process_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
355
+ from jsonb_array_elements(coalesce(v_page->'processes', '[]')) e;
356
+ insert into meta.computation
357
+ select (jsonb_populate_record(null::meta.computation, e || jsonb_build_object('id', nextval('meta.computation_id_seq'), 'page_id', v_page_id))).*
358
+ from jsonb_array_elements(coalesce(v_page->'computations', '[]')) e;
359
+ insert into meta.branch
360
+ select (jsonb_populate_record(null::meta.branch, e || jsonb_build_object('id', nextval('meta.branch_id_seq'), 'page_id', v_page_id))).*
361
+ from jsonb_array_elements(coalesce(v_page->'branches', '[]')) e;
362
+ end loop;
363
+
364
+ return v_app_id;
365
+ end
366
+ $$;
367
+
@@ -7,7 +7,7 @@ open item and open an issue or pull request; see [CONTRIBUTING.md](../CONTRIBUTI
7
7
  Legend: ✅ available · 🟡 partial (see notes) · ❌ not yet · ➖ not planned (a deliberate choice,
8
8
  or better served by the PostgreSQL ecosystem; see the notes and [extensions](guide/15-extensions.md))
9
9
 
10
- Last reviewed: 2026-10-14 (sprint 77: the Page Designer's layout and tree by display position, button positions, the tree's context menu with Duplicate and Copy to other page, Show Common / Show All, Go to group and the Template Options dialog; display positions, button positions and side-column page templates at runtime; sprint 76: the interactive report's menus and pagination, date items in the date format; sprint 75: the SQL Workshop's Object Browser and tabs; sprint 74: the builder's information architecture: Shared Components grid, Settings tabs, application home search and tasks; sprint 73: the Create Application wizard on one page with page types and features; sprint 72: the runtime chrome compared with APEX 26.2's: the developer toolbar, the Iris look, the navigation bar's Help menu and Install button; sprint 71: generated applications compared with APEX 26.2's: labels, foreign key names in reports, file columns, format masks, the Home page and the dashboard; sprint 70: treemap, sunburst and timeline charts; sprint 69: dragging tasks on a Gantt chart; sprint 68: the Data Reporter's welcome page, Find my workspace; sprint 67: REST source catalogs from OpenAPI documents, a workspace's export and import (two rows added); sprint 66: a blueprint from an existing application; sprint 65: facet charts; sprint 64: approval vacation rules and expiration policies; sprint 63: range, box plot and stock charts; sprint 62: workspace preferences (SQL Workshop and RESTful services per workspace), extension links, Manage backups across applications, SQL Workshop utilities with object reports, About Database and a database monitor; sprint 61: the builder's screens compared with APEX 26.1: the workspace sign-in page, the Administration menu with workspace message, environment banner and utilization, the SQL Workshop home, the App Gallery, application groups and cross application reports (four rows added); sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
10
+ Last reviewed: 2026-10-15 (sprint 83: Component Settings and Component Groups; sprint 82: Report Queries; sprint 81: Application Computations and Shortcuts in Shared Components; sprint 80: the master grid's first row selected when the page opens and after a search; grid cells as text with their format mask until edited, with cell navigation by arrow keys, Enter, F2 and Escape; sprint 79: generated dashboards with sums, donuts and lines, leaving out charts that say nothing; applications open in their own light or dark mode until the user chooses, per application; a user menu with Appearance and Sign in when signed out; the developer toolbar's App and Page texts and debug state; the report search's column scope in the magnifier; sprint 78: generated faceted search reports with one column per foreign key, the faceted search's total row count, Show all and Yes/No values, one search field, friendly headings and number formats in generated grids and master-detail pages, friendly names in the Create Application wizard's page list; sprint 77: the Page Designer's layout and tree by display position, button positions, the tree's context menu with Duplicate and Copy to other page, Show Common / Show All, Go to group and the Template Options dialog; display positions, button positions and side-column page templates at runtime; sprint 76: the interactive report's menus and pagination, date items in the date format; sprint 75: the SQL Workshop's Object Browser and tabs; sprint 74: the builder's information architecture: Shared Components grid, Settings tabs, application home search and tasks; sprint 73: the Create Application wizard on one page with page types and features; sprint 72: the runtime chrome compared with APEX 26.2's: the developer toolbar, the Iris look, the navigation bar's Help menu and Install button; sprint 71: generated applications compared with APEX 26.2's: labels, foreign key names in reports, file columns, format masks, the Home page and the dashboard; sprint 70: treemap, sunburst and timeline charts; sprint 69: dragging tasks on a Gantt chart; sprint 68: the Data Reporter's welcome page, Find my workspace; sprint 67: REST source catalogs from OpenAPI documents, a workspace's export and import (two rows added); sprint 66: a blueprint from an existing application; sprint 65: facet charts; sprint 64: approval vacation rules and expiration policies; sprint 63: range, box plot and stock charts; sprint 62: workspace preferences (SQL Workshop and RESTful services per workspace), extension links, Manage backups across applications, SQL Workshop utilities with object reports, About Database and a database monitor; sprint 61: the builder's screens compared with APEX 26.1: the workspace sign-in page, the Administration menu with workspace message, environment banner and utilization, the SQL Workshop home, the App Gallery, application groups and cross application reports (four rows added); sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
11
11
 
12
12
  ## At a glance
13
13
 
@@ -62,13 +62,13 @@ two, REST source catalogs and the workspace export, with their features.)
62
62
  |---|---|---|
63
63
  | Classic report | ✅ | `report` with `interactive: false` |
64
64
  | Interactive report | ✅ | (sprint 76) An Actions menu of nine entries with dialogs, a column heading menu (sort, hide, control break, the column's values to filter by), a search in one column, hidden columns, pagination with the range, first/last and rows per page. Search, column filters, sort, rows per page, control break, aggregates (with subtotals), highlight, saved private and public reports, computed columns, group by, pivot, chart view, row selection into a page item, CSV, Excel (typed cells) and PDF download, print, reset, reflow on phones. a maximum row count (`max_rows`), natural-language control (*Ask in your own words*, 26.1) and row selection that stays across pages. Not possible: flashback queries (PostgreSQL has none; use an audit or temporal table) |
65
- | Interactive grid | ✅ | Inline edit, add and delete rows, lists of values, required columns, per-row errors, all-or-nothing save, signed row keys, search and paging; **aggregates** over the whole result (developer and user defined), **frozen columns**, column **reorder/resize/hide** (Actions → Columns, or drag and drop), per-user layouts and **saved grid reports** (private and public), **master-detail** (signed row selection, details refreshed in place), a **row actions menu** (edit, duplicate, delete, links) and **copy/paste** of cell ranges (tab-separated, spreadsheet compatible) ([chapter 4](guide/04-pages-and-regions.md)) |
65
+ | Interactive grid | ✅ | Inline edit, add and delete rows, lists of values, required columns, per-row errors, all-or-nothing save, signed row keys, search and paging; **aggregates** over the whole result (developer and user defined), **frozen columns**, column **reorder/resize/hide** (Actions → Columns, or drag and drop), per-user layouts and **saved grid reports** (private and public), **master-detail** (signed row selection, the first row selected on load and after a search, details refreshed in place), **navigation and edit mode** (cells as text with their format mask until clicked, Enter or F2; arrow keys between cells), a **row actions menu** (edit, duplicate, delete, links) and **copy/paste** of cell ranges (tab-separated, spreadsheet compatible) ([chapter 4](guide/04-pages-and-regions.md)) |
66
66
  | Interactive grid views: single row view, chart view, group by | ✅ | (sprint 54) **Single row view**: one row of the search as a form with first/previous/next/last and *Add row*, from the Actions or row actions menu, saved by the grid's own process with the same checks; **group by** and **chart** set up in the Actions menu over every row of the search, as for the interactive report; `views` limits them; Save comes back to the same search, page and view ([chapter 4](guide/04-pages-and-regions.md#grid-interactive-grid)). No pivot view on grids (the interactive report has one), no editing in the single row view of columns the grid doesn't show |
67
67
  | Form (automatic row processing) | ✅ | Fetch, insert, update, delete, in a page or a modal dialog. Detects rows deleted meanwhile. (sprint 50) **Lost update detection** (APEX: *Prevent Lost Updates* and *Lock Row*): the page carries a checksum of the row's form columns (`meta.row_version`, independent of time zone and date format, as the app's role); *Save* and *Delete* lock the row and refuse when it changed meanwhile; off per form with `{"lost_update": false}` ([chapter 4](guide/04-pages-and-regions.md#form)) |
68
68
  | Charts | ✅ | Bar, column, stacked, line, area, combination (columns + lines), scatter, bubble, donut, pie, gauge, funnel, radar, **Gantt** (start/end, progress, milestones, dependencies, a today line, a time axis from hours to years), **pyramid** (area-proportional segments, or two series back to back) and **polar** (polar area); (sprint 63) **range** (floating columns from low to high), **box plot** (from one value per row or five statistics) and **stock** (candlesticks from open, high, low and close); multi-series, tooltips, data table, (sprint 71) a value axis with gridlines on horizontal bars and top-N with an *Other* bar (`"top": 10`), palette checked for colour-vision deficiency; drill-down links on chart marks (checksummed, only to pages the user may open). Drawn on the server as SVG without inline styles. (sprint 69) **Gantt editing**: drag a task or its end, or the arrow keys, runs the region's SQL as the app's role. (sprint 70) **treemap** (squarified, two levels shown), **sunburst** (up to four rings) from `node_id` / `parent_id` columns, and **timeline** (lanes by a `series` column, points and periods, overlaps in rows). **Missing:** other Oracle JET types (e.g. picto, NBox, Sankey) |
69
69
  | Cards and metric cards (26.1 metric card template) | ✅ | Cards from SQL, with a KPI "metric" style |
70
70
  | Calendar | ✅ | Month, week, day and list views (agenda list on phones); create on click (a checksummed link with the slot); drag and drop to move events through the developer's move SQL, run as the app's role (CSRF, a *who may drag* authorization, only events the user sees). Works without JavaScript; dragging adds on top ([chapter 4](guide/04-pages-and-regions.md#calendar)) |
71
- | Faceted search | ✅ | Checkbox, range (predefined, or a custom from/to on numbers and dates), star rating and search facets with live counts that honour the other facets; exclude on checkbox facets (26.1). Values travel as query parameters, never as SQL text. (sprint 65) **Facet charts**: a bar or pie chart of a facet's counts, each mark filtering on its value |
71
+ | Faceted search | ✅ | Checkbox, range (predefined, or a custom from/to on numbers and dates), star rating and search facets with live counts that honour the other facets; exclude on checkbox facets (26.1). Values travel as query parameters, never as SQL text. (sprint 65) **Facet charts**: a bar or pie chart of a facet's counts, each mark filtering on its value. (sprint 78) The **total row count**, *Show all* after seven values, booleans as Yes/No, one search field (the report's own is left out). No per-facet menu (sort by count or name) |
72
72
  | Smart filters | ✅ | One search field with filter chips and suggestions over a report, the same facet types; URL-based, works without JavaScript ([chapter 4](guide/04-pages-and-regions.md#smart_filters)) |
73
73
  | Static content, dynamic content | ✅ | `static` (HTML with substitutions) and `dynamic` (a SELECT returning HTML) |
74
74
  | Breadcrumb | ✅ | Automatic, from the page's breadcrumb parent |
@@ -110,7 +110,7 @@ two, REST source catalogs and the workspace export, with their features.)
110
110
  | Application items and processes | ✅ | `after_login`, `before_page` |
111
111
  | Validations | ✅ | Not null, SQL expression, regex, required items; `RAISE … USING COLUMN` in PL/pgSQL targets a field |
112
112
  | Conditions and authorization on components | ✅ | Pages, regions, items, buttons, processes, dynamic actions, navigation entries; re-checked on submit |
113
- | Computations | ✅ | Static, item, SQL query, SQL expression and PL/pgSQL function body; before header and after submit; with a condition, authorization and build option ([chapter 6](guide/06-processing.md#computations)) |
113
+ | Computations | ✅ | Static, item, SQL query, SQL expression and PL/pgSQL function body; before header and after submit; with a condition, authorization and build option ([chapter 6](guide/06-processing.md#computations)); **application computations** (sprint 81) for every page: after authentication, before header, after submit ([chapter 6](guide/06-processing.md#application-computations)) |
114
114
  | Page processes | ✅ | SQL / PL/pgSQL, form DML, grid DML, data loading (`data_load`), **invoke API** (a REST data source or URL, response values into items), **download** (a file from a query; several rows become one zip; on submit or on load), **execution chains** (child processes with their own conditions, nested, optionally run in the background with a status view), **workflow** (start, terminate, retry), **Generate text with AI** (text or structured output into items) |
115
115
  | Branches | ✅ | Before header and after processing, to a page (with items) or a URL inside the app, *When button pressed*, server-side conditions (SQL, exists, item null/equals, request); the button's target page stays the fallback ([chapter 6](guide/06-processing.md#branches)); a PL/pgSQL function returning a path in the app, and a page of another application (with signed items) |
116
116
  | Dynamic actions | ✅ | Show, hide, enable, disable, set value (SQL), execute SQL, refresh region or item, alert, submit, set focus, add/remove class, show success/error message and clear errors (26.1); the **Dialog Closed** event (per dialog page, with the dialog's message). **Execute JavaScript**: calls a function a static application file registered with `pgkiln.actions.register` (no inline code: the CSP stays strict) ([chapter 7](guide/07-dynamic-actions.md#execute-javascript)); dynamic action plug-ins |
@@ -191,7 +191,7 @@ two, REST source catalogs and the workspace export, with their features.)
191
191
  | Data Workshop (load and unload) | ✅ | SQL Workshop → Load Data: CSV/TSV/XLSX/JSON into a new table (inferred types) or an existing one (append, merge, replace) with a per-row error report; `data_load` process for end users; **XML** (a repeating element, attributes and paths; DTDs refused) and saved **data load definitions** (mapping, transformations, format masks, defaults; used by Load Data and the process, exported with the app). **Unload Data**: a table or view (chosen columns, where, order) or a query to CSV (separator, enclosure, heading), JSON, XLSX or XML (element names), streamed with a cursor in a read-only transaction with a statement timeout ([chapter 16](guide/16-files.md)) |
192
192
  | Large downloads without buffering (ORDS streams) | ✅ | CSV and Excel downloads stream from a database cursor (1,000 rows at a time, a streaming Excel writer) up to `DOWNLOAD_MAX_ROWS` (default 1,000,000), so memory stays flat. Report PDFs read their rows from a cursor in batches of 500 (`PDF_MAX_ROWS`, default 5,000, at most 100,000); REST collection handlers stream a chunked JSON array from a cursor |
193
193
  | REST data sources, web credentials (26.1: OAuth refresh tokens, password flow) | ✅ | Shared Components → REST data sources: JSON endpoints with path, query, header and body parameters, a row selector, typed columns and a response cache feed reports, cards, charts, calendars, maps, trees, template components and shared lists of values as SQL over `rest`. **Write-back**: insert, update, delete and fetch operations (path and JSON body templates after the source's URL) used by forms and interactive grids. **Synchronisation** into a local table (merge on key columns with optional delete, replace, append) on demand, on a cron schedule or from SQL (`meta.request_rest_sync`), as the app's role, with a run log. Web credentials: basic, API-key header, bearer and OAuth2 client credentials, password and refresh-token grants (refresh tokens stored encrypted and rotated), secrets encrypted and write-only. Outgoing calls only to an allow-list of hosts, with SSRF checks ([chapter 19](guide/19-rest-data-sources.md)). **Missing:** XML/SOAP, the OAuth2 authorization code flow (consent in the browser) |
194
- | Printing, document generator (PDF) | ✅ | **Document templates**: a query (with JSON columns for lines) fills an HTML template with Mustache-style tags, drawn as PDF with a report layout; buttons and links download them. Report PDF with **report layouts** and a print stylesheet on every page. No Word/Excel templates or DOCX/XLSX output |
194
+ | Printing, document generator (PDF) | ✅ | **Document templates**: a query (with JSON columns for lines) fills an HTML template with Mustache-style tags, drawn as PDF with a report layout; buttons and links download them. (sprint 82) **Report queries**: a named query as PDF, Excel, CSV, JSON, XML or HTML. Report PDF with **report layouts** and a print stylesheet on every page. No Word/Excel templates or DOCX/XLSX output |
195
195
  | `APEX_DATA_EXPORT` (a query to CSV, XLSX, PDF, HTML or JSON from PL/SQL) | ✅ | (sprint 53) `meta.data_export(query, format, options)` returns CSV, JSON, XML or HTML as `bytea` with the caller's privileges; the download process's `{"export": …}` sends a query's rows as CSV, JSON, XML, HTML, Excel or PDF |
196
196
  | SQL Workshop utilities: schema comparison, generate DDL | ✅ | (sprint 56) SQL Workshop → **DDL**: a schema's objects (types, sequences, tables, functions, views, constraints, indexes, triggers, RLS and policies, comments, grants) from the catalog, in an order that runs, unqualified so it also creates them in another schema. **Compare**: two schemas by their DDL (only in one, different, with a diff) and a change script that makes the target like the source (tables column by column; drops commented out for review). Also `pgkiln ddl`, `pgkiln compare` and the MCP tools `generate_ddl`, `compare_schemas` ([chapter 3](guide/03-builder.md#sql-workshop)). Two schemas of the same database only (APEX: also within one workspace) |
197
197
  | Data Reporter: self-service reports for business users (26.1) | ✅ | A **Data Reporter** region ([chapter 4](guide/04-pages-and-regions.md#data_reporter-data-reporter)): the developer offers tables and views with chosen columns, labels and format masks; signed-in users pick columns, filter, group with totals (count, sum, average, minimum, maximum), sort and chart, and save reports privately or shared with the app's users. Runs as the app's role (grants and RLS apply); works without JavaScript and fits phones. (sprint 68) A **welcome page** like 26.1's: a greeting (the developer's own, or pgkiln's), the sources as cards that start a report, the user's reports latest first, shared ones, and a search. One source per report (offer a view to combine tables); no downloads from a reporter yet |
@@ -219,7 +219,7 @@ two, REST source catalogs and the workspace export, with their features.)
219
219
  | Workspace message, environment banner, workspace utilization, preferences and extension links (Manage Service) | ✅ | (sprint 61) An **Administration** menu in the icon rail; administrators and workspace administrators set a **workspace message** (on the App Builder home) and an **environment banner** (a coloured label on every builder page of the workspace), and see **workspace utilization** (applications, pages, page views, users, static files, schema storage) ([chapter 3](guide/03-builder.md#workspace-message-and-environment-banner)). (sprint 62) **Workspace preferences**: the SQL Workshop and the applications' RESTful services (REST modules) on or off per workspace; **extension links** in an Extensions menu of the rail ([chapter 3](guide/03-builder.md#workspace-preferences)). **Missing:** the account login controls of APEX's preferences (pgkiln's password policy and sign-in throttling are installation-wide) |
220
220
  | Instance administration, install/upgrade logs (26.1) | ✅ | Versioned migrations (`npm run db:migrate`), every run logged; Workspace utilities → **Installation** (administrators): server version, install and upgrade runs with errors, applied migrations, and warnings for migrations the database misses or the server doesn't know ([chapter 3](guide/03-builder.md)). Workspace utilities → **Instance settings**: session length and sign-in throttling (stored in the database, over the environment variables) and an overview of the server's configuration without secrets; a server whose database lacks migrations says so (503) or applies them (`MIGRATE_ON_START`). Other settings stay environment variables |
221
221
  | Debug messages | ✅ | Debug levels 1–9 per app; `meta.debug(level, text)` and `meta.debug_enabled(level)` from application SQL (APEX_DEBUG); timed entries per request (page steps, regions, processes, branches, errors, SQL notices); Activity → **Debug messages** viewer per page view with filters and the slowest step highlighted; retention 1–90 days; password item values are never recorded ([chapter 6](guide/06-processing.md)). (sprint 50) Debug for **one session** from the developer toolbar, the application staying out of debug. **Missing:** debug entries for background jobs and REST requests |
222
- | Developer toolbar, session state viewer | ✅ | (sprint 50) Developers signed in to the builder see a toolbar on the pages of their applications in the same browser: App Builder, the application, **Edit page** (Page Designer), **Session** (the session state viewer: user, roles, session, language, time zone, debug level, this page's items, application items and other pages' items; passwords never shown; clear the page's items) **Quick edit** (sprint 59: click a region, item or button to open it in the Page Designer) and **Debug** for this session only. A separate random token (cookie path `/a`, HttpOnly) of a live builder session of a developer of the application's workspace ([chapter 3](guide/03-builder.md#developer-toolbar)). (sprint 72) A compact bar of icons with tooltips like APEX's, centred at the bottom, **Customize** (the Theme Roller) and **Info**, hidden to a slim tab when not used, docked at the bottom, top, left or right; the page keeps room for it |
222
+ | Developer toolbar, session state viewer | ✅ | (sprint 50) Developers signed in to the builder see a toolbar on the pages of their applications in the same browser: App Builder, the application, **Edit page** (Page Designer), **Session** (the session state viewer: user, roles, session, language, time zone, debug level, this page's items, application items and other pages' items; passwords never shown; clear the page's items) **Quick edit** (sprint 59: click a region, item or button to open it in the Page Designer) and **Debug** for this session only. A separate random token (cookie path `/a`, HttpOnly) of a live builder session of a developer of the application's workspace ([chapter 3](guide/03-builder.md#developer-toolbar)). (sprint 72) A compact bar of icons with tooltips like APEX's, centred at the bottom, **Customize** (the Theme Roller) and **Info**, hidden to a slim tab when not used, docked at the bottom, top, left or right; the page keeps room for it (sprint 79) Each tool with a short text like APEX's (*App 12*, *Page 3*, *Debug off*), *Icons only* as a setting |
223
223
  | Feedback (users send feedback from a page, developers answer) | ✅ | (sprint 51) With *Users can send feedback* on, the navigation bar's **Feedback** link opens a page for a comment, idea or problem about the page (optional rating), with the user's earlier feedback and the developers' answers; developers read, filter, set a status and answer under Application → Feedback ([chapter 3](guide/03-builder.md#feedback)). No screenshots, no e-mail to developers |
224
224
  | Automatic application backups, page groups | ✅ | (sprint 51) **Backups**: before every import that replaces an application (CLI, MCP, working copy merge, restore), daily when it changed, and by hand; the newest 25 (configurable) are kept; download and restore from the builder ([chapter 3](guide/03-builder.md#backups)); (sprint 62) Workspace utilities → **Manage backups** across the workspace's applications, with a backup of all of them at once. (sprint 58) **Page groups**: Shared Components → Page groups, a page's *Page group*, the page list by group with a filter ([chapter 3](guide/03-builder.md#page-groups)) |
225
225
  | Application availability (Available, Developers only, Restricted access, Unavailable; build status *Run only*) | ✅ | (sprint 51) Available, available to developers only (a builder session in the same browser), restricted to named users, or unavailable with a message, for pages, file downloads, REST modules and PostgREST; build status **Run only** refuses builder changes as if the application were locked, while `pgkiln import --replace` still deploys. Installation settings: not exported, kept by an import ([chapter 3](guide/03-builder.md#availability-and-build-status)) |
@@ -312,22 +312,22 @@ Sources: [APEX 26.1 new features](https://docs.oracle.com/en/database/oracle/ape
312
312
  | APEX | pgkiln |
313
313
  |---|---|
314
314
  | Application Definition, Application Items, Application Processes, Application Settings, Build Options | The same names (Application Logic) |
315
- | Application Computations | ➖ Not planned: an application process *before every page* or an item's computation does it |
315
+ | Application Computations | The same name (Application Logic, sprint 81): after authentication, before header and after submit, before the page's own computations |
316
316
  | Authentication Schemes | Settings → Security: one authentication per application (its type and fields); several schemes to switch between are ➖ not planned |
317
317
  | Authorization Schemes, Access Control | The same names (Security) |
318
318
  | Security Attributes, Session State Protection | Settings → Security and Sign-in; session state protection is per page (Page Designer → *Protection*) |
319
319
  | Lists of Values, Lists, Email Templates, Supporting Objects, Subscriptions | The same names (Other Components) |
320
320
  | Navigation Menu, Navigation Bar List, Breadcrumbs, Search Configurations | The same names (Navigation and Search); breadcrumbs follow each page's breadcrumb parent |
321
- | Shortcuts | ➖ Not planned: application items and substitutions (`&ITEM.`) |
321
+ | Shortcuts | The same name (Other Components, sprint 81): text, HTML and PL/pgSQL function body; `"NAME"` in static content, help texts and confirm messages |
322
322
  | Themes, Templates, User Interface Attributes, Theme Roller | Themes and Theme Roller, Template Components, Settings → User Interface; page and region templates are a fixed list (➖ no template editor) |
323
- | Plug-ins, Component Settings | Plug-ins; component settings ➖ not planned |
324
- | Component Groups | Page Groups (pages); groups of other components ➖ not planned |
323
+ | Plug-ins, Component Settings | The same names (User Interface; component settings sprint 83): rows per page of reports, grids and popup lists of values, list of values rows, star rating stars |
324
+ | Component Groups | The same name (Other Components, sprint 83): a library application's lists of values, authorization schemes, build options, template components and lists, subscribed to at once; Page Groups for pages |
325
325
  | Progressive Web App | Settings → Progressive Web App |
326
326
  | Static Application Files, Static Workspace Files | Static Application Files; workspace files ➖ not planned |
327
- | Report Layouts, Report Queries | Report Layouts and Document Templates (a template with its own queries) |
327
+ | Report Layouts, Report Queries | The same names (Files and Reports, sprint 82): a report query is one query printed as PDF with a report layout, Excel, CSV, JSON, XML or HTML (`?doc=NAME`, document buttons); APEX's several queries for one layout: a document template's JSON columns |
328
328
  | REST Data Sources, Web Credentials, REST Enabled SQL, Data Load Definitions | The same names (Data Sources); REST Enabled SQL ➖ not planned (one database) |
329
329
  | Workflows, Task Definitions, Automations | The same names |
330
330
  | Text Messages, Translate, Dynamic Translations, Globalization Attributes | Translations and Text Messages, Dynamic Translations, Settings → Globalization |
331
- | Map Backgrounds | The installation's map tile server (`MAP_TILE_URL`) |
331
+ | Map Backgrounds | ➖ Not planned per application: the installation's map tile server (`MAP_TILE_URL`) is every map's background |
332
332
  | AI Attributes, AI Services, AI Agents | AI Services; AI agents ➖ not planned |
333
333
 
@@ -28,6 +28,10 @@ meta.app ─┬─ meta.page ─┬─ meta.region (reports, forms, grids
28
28
  ├─ meta.lov (shared lists of values)
29
29
  ├─ meta.app_item (application items)
30
30
  ├─ meta.app_process (application processes)
31
+ ├─ meta.app_computation (application computations)
32
+ ├─ meta.shortcut (shortcuts: "NAME" texts)
33
+ ├─ meta.report_query (report queries: ?doc=NAME files)
34
+ ├─ meta.component_group (component groups: subscribed at once)
31
35
  ├─ meta.build_option (build options: include / exclude switches)
32
36
  └─ meta.app_user (users of this application)
33
37
 
@@ -188,6 +192,26 @@ trailing dot) is replaced by the item's value, **HTML-escaped**:
188
192
  <p>Welcome back, <b>&AI_ENAME.</b>!</p>
189
193
  ```
190
194
 
195
+ ### Shortcuts: `"NAME"`
196
+
197
+ **Shared Components → Shortcuts** (APEX: Shortcuts, migration 103) are named texts an application
198
+ reuses. `"NAME"`, the shortcut's upper-case name in double quotes, is replaced by it in a static
199
+ region's content, an item's help text and a button's or menu entry's confirm message, before the
200
+ `&NAME.` substitutions (so a shortcut may contain them). A quoted word that is not the name of one
201
+ of the application's shortcuts stays as it is.
202
+
203
+ | `type` | Replaced by |
204
+ |---|---|
205
+ | `text` | The value, HTML-escaped in a static region |
206
+ | `html` | The value as HTML (in a help text or a confirm message, which are text, its tags show as text) |
207
+ | `function_body` | What the PL/pgSQL function body returns, run as the application's role once per request; an error shows on the page and leaves it empty |
208
+
209
+ ```html
210
+ <p>"SUPPORT_CONTACT"</p> <!-- a shortcut SUPPORT_CONTACT, type html -->
211
+ ```
212
+
213
+ A *Delete* button's confirm message `"DELETE_CONFIRM_MSG"` then reads the same on every page.
214
+
191
215
  ## The application's database role
192
216
 
193
217
  Each application has a `db_role`, the equivalent of APEX's *parsing schema*. Every request runs