pgkiln 0.84.0 → 0.86.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 (105) hide show
  1. package/db/migrations/155_mail_available.sql +23 -0
  2. package/docs/apex-feature-parity.md +2 -2
  3. package/docs/guide/03-builder.md +14 -5
  4. package/docs/guide/04-pages-and-regions.md +1 -1
  5. package/docs/guide/06-processing.md +7 -3
  6. package/docs/guide/07-dynamic-actions.md +3 -3
  7. package/docs/guide/09-reference.md +1 -0
  8. package/docs/guide/12-development.md +10 -1
  9. package/examples/gallery/README.md +23 -6
  10. package/examples/gallery/gallery.json +4 -0
  11. package/examples/gallery/samples/sample-approvals.sql +102 -11
  12. package/examples/gallery/samples/sample-calendar.sql +40 -5
  13. package/examples/gallery/samples/sample-dynamic-actions.sql +237 -9
  14. package/examples/gallery/samples/sample-grids.sql +35 -3
  15. package/examples/gallery/samples/sample-maps.sql +65 -7
  16. package/examples/gallery/samples/sample-processes.sql +466 -0
  17. package/examples/gallery/samples/sample-validations.sql +407 -0
  18. package/examples/gallery/samples/ux-patterns.sql +154 -14
  19. package/examples/hr/README.md +2 -0
  20. package/examples/hr/hr_80_notifications.sql +125 -0
  21. package/examples/hr/hr_81_vector_search_type.sql +33 -0
  22. package/package.json +1 -1
  23. package/public/app.css +8 -1
  24. package/public/app.js +28 -9
  25. package/src/app.ts +9 -0
  26. package/src/builder/ai-agents.ts +86 -85
  27. package/src/builder/ai-builder.ts +30 -30
  28. package/src/builder/ai.ts +93 -92
  29. package/src/builder/api.ts +47 -47
  30. package/src/builder/app-groups.ts +33 -32
  31. package/src/builder/appfromfile.ts +57 -58
  32. package/src/builder/appsheets.ts +33 -32
  33. package/src/builder/appwizard.ts +65 -65
  34. package/src/builder/arrange.ts +23 -22
  35. package/src/builder/assistant.ts +39 -38
  36. package/src/builder/automations.ts +32 -31
  37. package/src/builder/availability.ts +24 -24
  38. package/src/builder/backups.ts +30 -31
  39. package/src/builder/blueprints.ts +62 -61
  40. package/src/builder/create-page.ts +52 -50
  41. package/src/builder/cross-app.ts +37 -35
  42. package/src/builder/dataload.ts +2 -2
  43. package/src/builder/datasets.ts +107 -106
  44. package/src/builder/dependencies.ts +20 -19
  45. package/src/builder/designer.ts +36 -36
  46. package/src/builder/diagnostics.ts +58 -57
  47. package/src/builder/features.ts +39 -35
  48. package/src/builder/feedback.ts +23 -20
  49. package/src/builder/find-workspace.ts +28 -24
  50. package/src/builder/form-designer.ts +36 -35
  51. package/src/builder/gallery.ts +25 -29
  52. package/src/builder/globalization.ts +50 -49
  53. package/src/builder/i18n/de.ts +3453 -0
  54. package/src/builder/i18n/en.ts +3453 -0
  55. package/src/builder/i18n/es.ts +3453 -0
  56. package/src/builder/i18n/fr.ts +3453 -0
  57. package/src/builder/i18n/it.ts +3453 -0
  58. package/src/builder/i18n/ja.ts +3453 -0
  59. package/src/builder/i18n/ko.ts +3453 -0
  60. package/src/builder/i18n/nl.ts +3453 -0
  61. package/src/builder/i18n/pt.ts +3453 -0
  62. package/src/builder/i18n/zh-cn.ts +3453 -0
  63. package/src/builder/i18n/zh-tw.ts +3453 -0
  64. package/src/builder/i18n.ts +17 -3
  65. package/src/builder/json-sources.ts +63 -62
  66. package/src/builder/layouts.ts +12 -11
  67. package/src/builder/locks.ts +29 -28
  68. package/src/builder/login-page.ts +26 -25
  69. package/src/builder/mail.ts +148 -147
  70. package/src/builder/newapp.ts +15 -12
  71. package/src/builder/plugins.ts +47 -44
  72. package/src/builder/process-jobs.ts +11 -10
  73. package/src/builder/pwa.ts +22 -21
  74. package/src/builder/remote-databases.ts +55 -56
  75. package/src/builder/report-settings.ts +1 -1
  76. package/src/builder/reporter.ts +43 -42
  77. package/src/builder/rest-catalogs.ts +87 -86
  78. package/src/builder/rest.ts +9 -8
  79. package/src/builder/routes.ts +27 -27
  80. package/src/builder/search.ts +14 -13
  81. package/src/builder/static-files.ts +31 -32
  82. package/src/builder/subscriptions.ts +47 -45
  83. package/src/builder/supporting.ts +23 -18
  84. package/src/builder/team.ts +179 -179
  85. package/src/builder/telemetry.ts +59 -62
  86. package/src/builder/templates.ts +66 -65
  87. package/src/builder/themeroller.ts +60 -63
  88. package/src/builder/ui-defaults.ts +21 -20
  89. package/src/builder/ui.ts +2 -1
  90. package/src/builder/upgrade.ts +25 -24
  91. package/src/builder/utilities.ts +48 -47
  92. package/src/builder/vector-providers.ts +67 -66
  93. package/src/builder/websources.ts +43 -42
  94. package/src/builder/wizards.ts +95 -89
  95. package/src/builder/workflow-migrate.ts +30 -31
  96. package/src/builder/workflows.ts +40 -39
  97. package/src/builder/workingcopies.ts +41 -49
  98. package/src/builder/workspace-backups.ts +21 -20
  99. package/src/builder/workspace-mail.ts +17 -16
  100. package/src/builder/workspace-resources.ts +104 -112
  101. package/src/builder/workspace-transfer.ts +34 -26
  102. package/src/runtime/engine.ts +6 -3
  103. package/src/runtime/lists.ts +2 -2
  104. package/src/runtime/push.ts +7 -1
  105. package/src/runtime/regions.ts +9 -4
@@ -4,6 +4,7 @@ import { html, raw } from '../html.ts';
4
4
  import { icon } from '../icons.ts';
5
5
  import { BASE, developer, region, send, shell, type Req } from './ui.ts';
6
6
  import { currentWorkspace } from './workspaces.ts';
7
+ import { scopedTx as tx, type BuilderKey } from './i18n.ts';
7
8
 
8
9
  // Cross-application reports (sprint 61; APEX: Workspace Utilities → Cross
9
10
  // Application Reports): one read-only report per topic over every application
@@ -11,9 +12,10 @@ import { currentWorkspace } from './workspaces.ts';
11
12
  // returns app_id and app (the name) first; those become a link.
12
13
 
13
14
  interface Report {
14
- title: string;
15
- about: string;
16
- columns: [key: string, label: string, cls?: 'num'][];
15
+ /** Message keys (src/builder/i18n): the title, the introduction and the column labels. */
16
+ title: BuilderKey;
17
+ about: BuilderKey;
18
+ columns: [key: string, label: BuilderKey, cls?: 'num'][];
17
19
  sql: string;
18
20
  }
19
21
 
@@ -22,56 +24,56 @@ const IN_WS = 'meta.app_workspace(a.id) = $1';
22
24
 
23
25
  export const REPORTS: Record<string, Report> = {
24
26
  attributes: {
25
- title: 'Application attributes',
26
- about: 'The main settings of every application: type, sign-in, availability, build status, debug level, language and database role.',
27
- columns: [['alias', 'Alias'], ['app_type', 'Type'], ['authentication', 'Sign-in'], ['availability', 'Availability'], ['build_status', 'Build status'], ['debug_level', 'Debug', 'num'], ['language', 'Language'], ['db_role', 'Database role'], ['updated', 'Updated']],
27
+ title: 'xap.attributes.title',
28
+ about: 'xap.attributes.about',
29
+ columns: [['alias', 'xap.col.alias'], ['app_type', 'xap.col.type'], ['authentication', 'xap.col.sign_in'], ['availability', 'xap.col.availability'], ['build_status', 'xap.col.build_status'], ['debug_level', 'xap.col.debug', 'num'], ['language', 'xap.col.language'], ['db_role', 'xap.col.database_role'], ['updated', 'xap.col.updated']],
28
30
  sql: `select a.id as app_id, a.name as app, a.alias, a.app_type, a.authentication, a.availability, a.build_status, a.debug_level,
29
31
  a.language, coalesce(a.db_role, '(runtime connection)') as db_role, to_char(a.updated_at, 'YYYY-MM-DD HH24:MI') as updated
30
32
  from meta.app a where ${IN_WS} order by lower(a.name)`,
31
33
  },
32
34
  'build-options': {
33
- title: 'Build options',
34
- about: 'The build options of every application and whether their components are included.',
35
- columns: [['name', 'Build option'], ['status', 'Status'], ['description', 'Description']],
35
+ title: 'xap.build_options.title',
36
+ about: 'xap.build_options.about',
37
+ columns: [['name', 'xap.col.build_option'], ['status', 'xap.col.status'], ['description', 'xap.col.description']],
36
38
  sql: `select a.id as app_id, a.name as app, b.name, b.status, b.description
37
39
  from meta.app a join meta.build_option b on b.app_id = a.id where ${IN_WS} order by lower(a.name), b.name`,
38
40
  },
39
41
  comments: {
40
- title: 'Developer comments',
41
- about: 'The developers\' comments on applications and pages, newest first.',
42
- columns: [['page_no', 'Page', 'num'], ['author', 'Author'], ['created', 'Written'], ['body', 'Comment']],
42
+ title: 'xap.comments.title',
43
+ about: 'xap.comments.about',
44
+ columns: [['page_no', 'xap.col.page', 'num'], ['author', 'xap.col.author'], ['created', 'xap.col.written'], ['body', 'xap.col.comment']],
43
45
  sql: `select a.id as app_id, a.name as app, c.page_no, c.author, to_char(c.created_at, 'YYYY-MM-DD HH24:MI') as created, left(c.body, 500) as body
44
46
  from meta.app a join meta.dev_comment c on c.app_id = a.id where ${IN_WS} order by c.created_at desc limit 500`,
45
47
  },
46
48
  authentication: {
47
- title: 'Authentication',
48
- about: 'How people sign in to every application: the sign-in method, password sign-in, identity providers and LDAP directories, access control, the login page and Forgot password.',
49
- columns: [['authentication', 'Sign-in'], ['local_login', 'Passwords'], ['providers', 'Identity providers'], ['ldap', 'LDAP directories'], ['access_control', 'Access'], ['remember_me_days', 'Keep me signed in (days)', 'num'], ['login_page', 'Login page', 'num'], ['password_reset', 'Forgot password']],
49
+ title: 'xap.authentication.title',
50
+ about: 'xap.authentication.about',
51
+ columns: [['authentication', 'xap.col.sign_in'], ['local_login', 'xap.col.passwords'], ['providers', 'xap.col.identity_providers'], ['ldap', 'xap.col.ldap_directories'], ['access_control', 'xap.col.access'], ['remember_me_days', 'xap.col.keep_me_signed_in_days', 'num'], ['login_page', 'xap.col.login_page', 'num'], ['password_reset', 'xap.col.forgot_password']],
50
52
  sql: `select a.id as app_id, a.name as app, a.authentication, case when a.local_login then 'yes' else 'no' end as local_login,
51
53
  array_to_string(a.sso_providers, ', ') as providers, array_to_string(a.ldap_directories, ', ') as ldap,
52
54
  a.access_control, a.remember_me_days, a.login_page, case when a.password_reset then 'yes' else 'no' end as password_reset
53
55
  from meta.app a where ${IN_WS} order by lower(a.name)`,
54
56
  },
55
57
  authorization: {
56
- title: 'Authorization schemes',
57
- about: 'The authorization schemes of every application: their type and what they check.',
58
- columns: [['name', 'Scheme'], ['type', 'Type'], ['value', 'Checks'], ['error_message', 'Error message']],
58
+ title: 'xap.authorization.title',
59
+ about: 'xap.authorization.about',
60
+ columns: [['name', 'xap.col.scheme'], ['type', 'xap.col.type'], ['value', 'xap.col.checks'], ['error_message', 'xap.col.error_message']],
59
61
  sql: `select a.id as app_id, a.name as app, s.name, s.type, left(s.value, 300) as value, s.error_message
60
62
  from meta.app a join meta.authz_scheme s on s.app_id = a.id where ${IN_WS} order by lower(a.name), s.name`,
61
63
  },
62
64
  'password-items': {
63
- title: 'Password items',
64
- about: 'Every password item: pgkiln never shows their values again, never keeps them in session state for the next page and never writes them to the debug log.',
65
- columns: [['page_no', 'Page', 'num'], ['name', 'Item'], ['label', 'Label'], ['reveal', 'Show button'], ['required', 'Required']],
65
+ title: 'xap.password_items.title',
66
+ about: 'xap.password_items.about',
67
+ columns: [['page_no', 'xap.col.page', 'num'], ['name', 'xap.col.item'], ['label', 'xap.col.label'], ['reveal', 'xap.col.show_button'], ['required', 'xap.col.required']],
66
68
  sql: `select a.id as app_id, a.name as app, p.page_no, i.name, i.label,
67
69
  case when (i.config ->> 'reveal')::boolean then 'yes' else 'no' end as reveal, case when i.required then 'yes' else 'no' end as required
68
70
  from meta.app a join meta.page p on p.app_id = a.id join meta.item i on i.page_id = p.id
69
71
  where ${IN_WS} and i.type = 'password' order by lower(a.name), p.page_no, i.seq`,
70
72
  },
71
73
  theme: {
72
- title: 'Theme summary',
73
- about: 'The look of every application: base style, Theme Roller style, navigation and the colours it sets.',
74
- columns: [['base', 'Base style'], ['style', 'Style'], ['nav', 'Navigation'], ['colors', 'Colours set', 'num'], ['pwa', 'Installable (PWA)']],
74
+ title: 'xap.theme.title',
75
+ about: 'xap.theme.about',
76
+ columns: [['base', 'xap.col.base_style'], ['style', 'xap.col.style'], ['nav', 'xap.col.navigation'], ['colors', 'xap.col.colours_set', 'num'], ['pwa', 'xap.col.installable_pwa']],
75
77
  sql: `select a.id as app_id, a.name as app, coalesce(a.theme ->> 'base', 'default') as base, coalesce(a.theme ->> 'style', '-') as style,
76
78
  coalesce(a.theme ->> 'nav', 'side') as nav,
77
79
  (select count(*) from jsonb_object_keys(coalesce(a.theme -> 'colors', '{}'::jsonb)))::int as colors,
@@ -82,7 +84,7 @@ export const REPORTS: Record<string, Report> = {
82
84
 
83
85
  /** The links in Workspace utilities' side column. */
84
86
  export const crossAppLinks = () =>
85
- Object.entries(REPORTS).map(([k, r]) => html`<li><a href="${BASE}/utilities/reports/${k}">${r.title}${icon('chevron')}</a></li>`);
87
+ Object.entries(REPORTS).map(([k, r]) => html`<li><a href="${BASE}/utilities/reports/${k}">${tx(r.title)}${icon('chevron')}</a></li>`);
86
88
 
87
89
  export async function crossAppRoutes(app: FastifyInstance) {
88
90
  app.get(`${BASE}/utilities/reports/:kind`, async (req: Req, reply) => {
@@ -92,15 +94,15 @@ export async function crossAppRoutes(app: FastifyInstance) {
92
94
  if (!r) return reply.code(404).send('Not found');
93
95
  const rows = (await owner.query(r.sql, [currentWorkspace(s)])).rows;
94
96
  const cell = (v: unknown) => (v === null || v === undefined || v === '' ? html`<span class="muted">-</span>` : String(v));
95
- const main = html`<div class="title-row"><h1>${r.title}</h1></div>
96
- <p class="muted u-mt0">${r.about} Applications of the workspace ${s.workspace?.name ?? ''}.</p>
97
- <nav class="ide-tabs u-mb1" aria-label="Cross-application reports">${Object.entries(REPORTS).map(([k, x]) =>
98
- html`<a class="ide-tab" href="${BASE}/utilities/reports/${k}"${k === req.params.kind ? raw(' aria-current="page"') : ''}><span>${x.title}</span></a>`)}</nav>
99
- ${region(r.title, rows.length
100
- ? html`<div class="table-wrap"><table class="report report-reflow"><thead><tr><th>Application</th>${r.columns.map(([, l, c]) => html`<th${c ? raw(` class="${c}"`) : ''}>${l}</th>`)}</tr></thead>
101
- <tbody>${rows.map((row) => html`<tr><td data-label="Application"><a href="${BASE}/apps/${row.app_id}">${row.app}</a></td>
102
- ${r.columns.map(([k, l, c]) => html`<td data-label="${l}"${c ? raw(` class="${c}"`) : ''}>${cell(row[k])}</td>`)}</tr>`)}</tbody></table></div>`
103
- : html`<p class="muted">Nothing to report in this workspace.</p>`, html`<span class="count">${rows.length}</span>`)}`;
104
- return send(reply, s, shell(s, r.title, [['App Builder', BASE], ['Workspace utilities', `${BASE}/utilities`], [r.title]], main));
97
+ const main = html`<div class="title-row"><h1>${tx(r.title)}</h1></div>
98
+ <p class="muted u-mt0">${tx(r.about)} ${tx('xap.of_workspace', { name: s.workspace?.name ?? '' })}</p>
99
+ <nav class="ide-tabs u-mb1" aria-label="${tx('xap.nav')}">${Object.entries(REPORTS).map(([k, x]) =>
100
+ html`<a class="ide-tab" href="${BASE}/utilities/reports/${k}"${k === req.params.kind ? raw(' aria-current="page"') : ''}><span>${tx(x.title)}</span></a>`)}</nav>
101
+ ${region(tx(r.title), rows.length
102
+ ? html`<div class="table-wrap"><table class="report report-reflow"><thead><tr><th>${tx('xap.application')}</th>${r.columns.map(([, l, c]) => html`<th${c ? raw(` class="${c}"`) : ''}>${tx(l)}</th>`)}</tr></thead>
103
+ <tbody>${rows.map((row) => html`<tr><td data-label="${tx('xap.application')}"><a href="${BASE}/apps/${row.app_id}">${row.app}</a></td>
104
+ ${r.columns.map(([k, l, c]) => html`<td data-label="${tx(l)}"${c ? raw(` class="${c}"`) : ''}>${cell(row[k])}</td>`)}</tr>`)}</tbody></table></div>`
105
+ : html`<p class="muted">${tx('xap.nothing')}</p>`, html`<span class="count">${rows.length}</span>`)}`;
106
+ return send(reply, s, shell(s, tx(r.title), [[tx('rail.app_builder'), BASE], [tx('wu.title'), `${BASE}/utilities`], [tx(r.title)]], main));
105
107
  });
106
108
  }
@@ -27,7 +27,7 @@ import type { Session } from '../session.ts';
27
27
  import type { ComponentSpec } from './components.ts';
28
28
  import { back, BASE, csrf, developer, flash, region, send, shell, workshopTabs, type Body, type Req } from './ui.ts';
29
29
  import { appWorkspace, currentWorkspace, workshopDb } from './workspaces.ts';
30
- import { tr, translatorFor, type BuilderTranslate } from './i18n.ts';
30
+ import { scopedTx, tr, translatorFor, type BuilderTranslate } from './i18n.ts';
31
31
 
32
32
  // SQL Workshop → Load Data: upload a CSV/TSV/XLSX/JSON/XML file, preview it,
33
33
  // and load it into a new table (column types inferred) or an existing one
@@ -73,7 +73,7 @@ export const DATA_LOAD_DEF_SPEC: ComponentSpec = {
73
73
 
74
74
  /** "Load a file with it" on a definition's Shared Components page. */
75
75
  export const dataLoadDefExtras = (row: { id: number }) =>
76
- html`<p class="u-mt1"><a class="btn" href="${BASE}/sql/load?definition=${row.id}">${icon('upload')} Load a file with this definition</a></p>`;
76
+ html`<p class="u-mt1"><a class="btn" href="${BASE}/sql/load?definition=${row.id}">${icon('upload')} ${scopedTx('dl.load_with_def')}</a></p>`;
77
77
 
78
78
  /** The data load definitions of the current workspace's applications. */
79
79
  async function definitions(s: Session) {