@bluestep-systems/bspecs 0.10.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.
- package/README.md +129 -0
- package/cli.js +74 -0
- package/package.json +30 -0
- package/src/prompts.js +74 -0
- package/src/scaffold.js +152 -0
- package/src/sync.js +123 -0
- package/src/utils.js +95 -0
- package/templates/claude/agents/b6p-code-review.md +81 -0
- package/templates/claude/agents/b6p-commenter.md +59 -0
- package/templates/claude/agents/b6p-task-implementer.md +77 -0
- package/templates/claude/hooks/block-generated-files.sh +16 -0
- package/templates/claude/hooks/block-tsc.sh +16 -0
- package/templates/claude/hooks/prettier-on-save.sh +21 -0
- package/templates/claude/instructions/b6p-platform.md.template +185 -0
- package/templates/claude/instructions/bsjs-development.md.template +430 -0
- package/templates/claude/instructions/conventions/always-snapshot.md.template +25 -0
- package/templates/claude/instructions/conventions/blueiq-no-ai-branding.md.template +11 -0
- package/templates/claude/instructions/conventions/date-format.md.template +27 -0
- package/templates/claude/instructions/conventions/endpoint-approach.md.template +9 -0
- package/templates/claude/instructions/conventions/formula-patterns.md.template +71 -0
- package/templates/claude/instructions/conventions/no-global-dollar.md.template +9 -0
- package/templates/claude/instructions/conventions/push-inner-draft.md.template +21 -0
- package/templates/claude/instructions/conventions/separate-files.md.template +17 -0
- package/templates/claude/instructions/conventions/single-script.md.template +28 -0
- package/templates/claude/instructions/conventions/snapshot-integrity.md.template +23 -0
- package/templates/claude/instructions/conventions/top-level-const-tdz.md.template +33 -0
- package/templates/claude/instructions/conventions/ts-in-template-literal.md.template +48 -0
- package/templates/claude/instructions/conventions/tsc-rootdir.md.template +17 -0
- package/templates/claude/instructions/gotchas/common-gotchas.md.template +91 -0
- package/templates/claude/instructions/gotchas/fetched-resource-code.md.template +9 -0
- package/templates/claude/instructions/index.md.template +82 -0
- package/templates/claude/instructions/reference/api-patterns.md.template +487 -0
- package/templates/claude/instructions/reference/blueiq-credit-integration-playbook.md.template +31 -0
- package/templates/claude/instructions/reference/chronounit-months.md.template +37 -0
- package/templates/claude/instructions/reference/code-patterns.md.template +265 -0
- package/templates/claude/instructions/reference/component-library.md.template +217 -0
- package/templates/claude/instructions/reference/crm-dashboard-inspo.md.template +17 -0
- package/templates/claude/instructions/reference/csv-parsing.md.template +18 -0
- package/templates/claude/instructions/reference/dashboard-design-system.md.template +38 -0
- package/templates/claude/instructions/reference/datetime-field-write.md.template +27 -0
- package/templates/claude/instructions/reference/design-system.md.template +150 -0
- package/templates/claude/instructions/reference/dpn-dashboard-framework.md.template +29 -0
- package/templates/claude/instructions/reference/endpoint-method-call.md.template +10 -0
- package/templates/claude/instructions/reference/endpoint-no-delete-method.md.template +9 -0
- package/templates/claude/instructions/reference/endpoint-output-channel.md.template +23 -0
- package/templates/claude/instructions/reference/endpoint-urls.md.template +15 -0
- package/templates/claude/instructions/reference/entry-delete.md.template +40 -0
- package/templates/claude/instructions/reference/file-execution.md.template +113 -0
- package/templates/claude/instructions/reference/http-requester.md.template +37 -0
- package/templates/claude/instructions/reference/id-full-vs-short.md.template +15 -0
- package/templates/claude/instructions/reference/internal-loopback-fetch.md.template +24 -0
- package/templates/claude/instructions/reference/localdate-parse.md.template +16 -0
- package/templates/claude/instructions/reference/merge-report-memo-json.md.template +25 -0
- package/templates/claude/instructions/reference/merge-report-static-index.md.template +29 -0
- package/templates/claude/instructions/reference/merge-report-urls.md.template +67 -0
- package/templates/claude/instructions/reference/multi-entry-in-multi-entry.md.template +21 -0
- package/templates/claude/instructions/reference/named-controls-submit.md.template +11 -0
- package/templates/claude/instructions/reference/new-entry-id.md.template +30 -0
- package/templates/claude/instructions/reference/relationship-field-set.md.template +37 -0
- package/templates/claude/instructions/reference/send-message-abort.md.template +37 -0
- package/templates/claude/instructions/reference/session-cookie-forwarding.md.template +31 -0
- package/templates/claude/instructions/reference/singleselect-null-copy.md.template +21 -0
- package/templates/claude/instructions/reference/staff-query-permission-gating.md.template +27 -0
- package/templates/claude/instructions/reference/timefield-vs-datetimefield.md.template +13 -0
- package/templates/claude/instructions/reference/user-zone-id.md.template +16 -0
- package/templates/claude/settings.json.template +46 -0
- package/templates/claude/skills/b6p-audit/SKILL.md +82 -0
- package/templates/claude/skills/b6p-pull/SKILL.md +123 -0
- package/templates/claude/skills/b6p-push/SKILL.md +70 -0
- package/templates/claude/skills/bug-fix/SKILL.md +28 -0
- package/templates/claude/skills/spec-create/SKILL.md +60 -0
- package/templates/claude/skills/spec-execute/SKILL.md +51 -0
- package/templates/claude/skills/spec-status/SKILL.md +20 -0
- package/templates/claude/skills/task-comment/SKILL.md +96 -0
- package/templates/claude/spec-templates/design.template.md +36 -0
- package/templates/claude/spec-templates/requirements.template.md +26 -0
- package/templates/claude/spec-templates/tasks.template.md +37 -0
- package/templates/module/README.md.template +46 -0
- package/templates/root/.gitignore.template +14 -0
- package/templates/root/.prettierrc.template +8 -0
- package/templates/root/CLAUDE.md.template +157 -0
- package/templates/root/README.md.template +58 -0
- package/templates/root/package.json.template +15 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# BlueStep Design System
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Component Source Priority](#component-source-priority)
|
|
7
|
+
- [Design Standards](#design-standards)
|
|
8
|
+
- [Standard Components](#standard-components)
|
|
9
|
+
- [Bootstrap Components](#bootstrap-components)
|
|
10
|
+
- [Using the Design System](#using-the-design-system)
|
|
11
|
+
- [Related Documentation](#related-documentation)
|
|
12
|
+
- [Access](#access)
|
|
13
|
+
|
|
14
|
+
> **Primary Reference**: The complete BlueStep Design System is available as an interactive merge report at:
|
|
15
|
+
> **https://bluestepplatformsupport.bluestep.net/shared/layouts/singleblock.jsp?_event=view&_id=120130___195147**
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
The BlueStep Design System is a comprehensive library of reusable components, design standards, and guidelines for BlueStep.js development. It serves three main purposes:
|
|
20
|
+
|
|
21
|
+
1. **Establish clear standards** - Provide consistent design and development patterns for designers and developers
|
|
22
|
+
2. **Component reference** - Serve as a comprehensive reference for UI components for both developers and AI agents
|
|
23
|
+
3. **Rapid prototyping** - Enable AI agents to quickly create mockups based on user instructions
|
|
24
|
+
|
|
25
|
+
## Component Source Priority
|
|
26
|
+
|
|
27
|
+
When creating mockups and developing software, components should be sourced in the following priority order:
|
|
28
|
+
|
|
29
|
+
1. **BlueStep Generic Components** - Use components from the genericComponents library first (e.g., `moduleF`, `headF`, `svgF`, `tableF`)
|
|
30
|
+
2. **Bootstrap 3.3.7 Components** - Use standard Bootstrap components when no generic component exists (e.g., buttons, alerts, modals, panels)
|
|
31
|
+
3. **Custom Components** - Create from scratch only when necessary, inferring styles from the above sources to maintain consistency
|
|
32
|
+
|
|
33
|
+
This priority ensures consistency across BlueStep applications and leverages existing, tested components whenever possible.
|
|
34
|
+
|
|
35
|
+
## Design Standards
|
|
36
|
+
|
|
37
|
+
The design system includes comprehensive documentation for:
|
|
38
|
+
|
|
39
|
+
### Color Palette
|
|
40
|
+
|
|
41
|
+
**Standard Colors (CSS Variables)** - Available to all BlueStep pages:
|
|
42
|
+
- `--bs-red: #c03b2b`
|
|
43
|
+
- `--bs-orange: #F29D1F`
|
|
44
|
+
- `--bs-yellow: #EFC319`
|
|
45
|
+
- `--bs-green: #28AE60`
|
|
46
|
+
- `--bs-blue: #2A81BA`
|
|
47
|
+
- `--bs-logo-blue: #0063A6` (Brand logo color, used in email templates)
|
|
48
|
+
- `--bs-purple: #894C9E`
|
|
49
|
+
- `--bs-gray: #969FA0`
|
|
50
|
+
- `--bs-default: #2D3F50` (Default theme color)
|
|
51
|
+
|
|
52
|
+
**Custom Generated Colors (Style-Based)** - Generated based on currently selected color style:
|
|
53
|
+
- `--bs-primaryColor`
|
|
54
|
+
- `--bs-primaryColorHigh`
|
|
55
|
+
- `--bs-accent1Color`
|
|
56
|
+
- `--bs-accent2Color`
|
|
57
|
+
- `--bs-secondaryColor`
|
|
58
|
+
- `--bs-secondaryColorHigh`
|
|
59
|
+
|
|
60
|
+
> **Note**: Custom generated colors vary based on the organization's color style selection. Always use CSS variables for colors to ensure consistency.
|
|
61
|
+
|
|
62
|
+
### Typography
|
|
63
|
+
|
|
64
|
+
The design system documents standard typography patterns, font sizes, and text styling conventions for BlueStep applications.
|
|
65
|
+
|
|
66
|
+
### Spacing Scale
|
|
67
|
+
|
|
68
|
+
Standard spacing values and guidelines for consistent layout spacing throughout BlueStep applications.
|
|
69
|
+
|
|
70
|
+
## Standard Components
|
|
71
|
+
|
|
72
|
+
The design system provides comprehensive documentation for all BlueStep generic components:
|
|
73
|
+
|
|
74
|
+
- **SVG Icons** (`svgF` / `svgIconF`) - Icon generation with size, color, and modifier options
|
|
75
|
+
- **Headers** (`headF`) - Standard and collapsible section headers
|
|
76
|
+
- **Tables** (`tableF`) - Data table generation
|
|
77
|
+
- **Fields** (`fieldF`) - Form field formatting
|
|
78
|
+
- **Bootstrap Forms** (`bootstrapFormF`) - Complete form generation
|
|
79
|
+
- **Modules** (`moduleF`) - Full page layouts with tabs
|
|
80
|
+
- **Generic HTML Tags** (`tagF`) - Custom HTML element generation
|
|
81
|
+
- **Anchor Tags** (`aF`) - Link generation
|
|
82
|
+
- **Email Templates** (`emailF`) - Responsive email generation
|
|
83
|
+
- **Breadcrumbs** (`breadcrumbF`) - Navigation breadcrumbs
|
|
84
|
+
- **Search Components** (`searchHeadF`, `searchTextF`) - Search interface components
|
|
85
|
+
- **Date Input** (`bsDateInput`) - Date picker components
|
|
86
|
+
- **Date Range** (`jsDateRange`) - Date range selection
|
|
87
|
+
- **Popin Hints** (`popinHintF`) - Contextual help tooltips
|
|
88
|
+
- **SVG with Title** (`svgTitleF`) - Icons with text labels
|
|
89
|
+
- **Horizontal Form Layout** (`bsHorizFormCol2F`, `bsHorizFieldCol2F`) - Two-column form layouts
|
|
90
|
+
|
|
91
|
+
Each component includes:
|
|
92
|
+
- Description and use cases
|
|
93
|
+
- Code examples
|
|
94
|
+
- Parameter documentation
|
|
95
|
+
- Visual examples
|
|
96
|
+
- Usage guidelines
|
|
97
|
+
- Accessibility notes
|
|
98
|
+
|
|
99
|
+
## Bootstrap Components
|
|
100
|
+
|
|
101
|
+
The design system also documents standard Bootstrap 3.3.7 components:
|
|
102
|
+
|
|
103
|
+
- Buttons
|
|
104
|
+
- Alerts
|
|
105
|
+
- Badges
|
|
106
|
+
- Labels
|
|
107
|
+
- Panels
|
|
108
|
+
- Wells
|
|
109
|
+
- Modals
|
|
110
|
+
- Tooltips
|
|
111
|
+
- Popovers
|
|
112
|
+
- Tabs
|
|
113
|
+
- Accordion
|
|
114
|
+
- Pagination
|
|
115
|
+
|
|
116
|
+
## Using the Design System
|
|
117
|
+
|
|
118
|
+
### For AI Agents
|
|
119
|
+
|
|
120
|
+
When creating mockups or building BlueStep.js applications:
|
|
121
|
+
|
|
122
|
+
1. **Reference the design system first** - Check the interactive design system merge report for component examples and patterns
|
|
123
|
+
2. **Follow component priority** - Use BlueStep generic components before Bootstrap, and Bootstrap before custom HTML
|
|
124
|
+
3. **Use design standards** - Apply color palette, typography, and spacing scale consistently
|
|
125
|
+
4. **Check component documentation** - Review parameter lists, examples, and usage guidelines in the design system
|
|
126
|
+
|
|
127
|
+
### For Developers
|
|
128
|
+
|
|
129
|
+
1. **Browse the interactive design system** - Use the merge report to see live examples of all components
|
|
130
|
+
2. **Copy code examples** - Use the provided code examples as starting points
|
|
131
|
+
3. **Follow usage guidelines** - Adhere to accessibility and best practice recommendations
|
|
132
|
+
4. **Reference component API** - Check the design system for parameter documentation
|
|
133
|
+
|
|
134
|
+
### For Designers
|
|
135
|
+
|
|
136
|
+
1. **Review design standards** - Understand color palette, typography, and spacing conventions
|
|
137
|
+
2. **Explore component library** - See all available components and their visual appearance
|
|
138
|
+
3. **Understand component capabilities** - Know what's possible with existing components before requesting custom solutions
|
|
139
|
+
|
|
140
|
+
## Related Documentation
|
|
141
|
+
|
|
142
|
+
- **Component Library Usage** - See `agents-support/component-library.md` for quick reference on common components
|
|
143
|
+
- **Component Source** - See `https://files.bluestep.net/script/530024___41/static/genericComponents.js` for complete API documentation
|
|
144
|
+
- **Icon Reference** - See the BlueStep Icon Reference for complete icon list and categories
|
|
145
|
+
|
|
146
|
+
## Access
|
|
147
|
+
|
|
148
|
+
**Design System URL**: https://bluestepplatformsupport.bluestep.net/shared/layouts/singleblock.jsp?_event=view&_id=120130___195147
|
|
149
|
+
|
|
150
|
+
The design system is also available in the workspace at `U129801/BlueStep.js Components` for local reference.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Parameterized data-endpoint + dashboard framework on rop — endpoint serves per-client/date-range JSON via URL params, dashboard fans out throttled parallel fetches; reference architecture for client-fed analytics dashboards"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
DPN Scoring framework on **rop** — a reference architecture for a dashboard that pulls per-entity, per-date-range data from a parameterized JSON endpoint. Two files, server-thin/client-fat. **Do not modify — reference only.**
|
|
6
|
+
|
|
7
|
+
- **Endpoint** `/b/dpnScoring` (file 1471219, key 363769___463) — the data service.
|
|
8
|
+
- **Merge report** DPN Dashboard (file 1475679, key 530024___1162) — the consumer/UI.
|
|
9
|
+
|
|
10
|
+
## Endpoint: parameterized data service
|
|
11
|
+
`scripts/app.ts` reads four URL params and returns JSON. This is the part Brandon flagged — **same endpoint, many shapes of data via params**:
|
|
12
|
+
|
|
13
|
+
- `clientid` — which entity (accepts short id OR full `Class___Short`; resolved via `allActiveDischarge.optById(clientId)`).
|
|
14
|
+
- `startdate` / `enddate` — ISO `YYYY-MM-DD` window. Per-form entries are filtered with a plain string compare (`dateStr < startDate || dateStr > endDate`) since ISO sorts lexically. Top-level group-notes query instead uses `addSearch('groupNote','date','>=', MM/dd/yyyy)` — note the **format flip to MM/dd/yyyy** for `addSearch` (see [date format](../conventions/date-format.md)), wrapped in try/finally with `clearSearchAndSort()`.
|
|
15
|
+
- `action` — the response mode discriminator: `daily` (default; per-day buckets), `weekly` (Mon–Sun aggregated week rows with phase-weighted scores), or `full` (daily buckets + weeks). One endpoint, three payload shapes.
|
|
16
|
+
|
|
17
|
+
Response envelope is always `{ success, clientId, startDate, endDate, ... }` with `sendError(status, msg)` emitting `{ success:false, error }`. Missing params → 400 with a list of which were missing. Whole body in try/catch → 500. Reads via a query/field manifest (queries `allActiveDischarge` + `allGroupNotes`, each form listing the exact fields to load) — this component predates the move to platform form-import config and still uses a legacy `objects/imports.ts` `require` manifest; configure new components on the platform instead (see [api-patterns](api-patterns.md#query-access-patterns)). `config.json`: `transactionReadonly:false`, `transactionTimeout:3600`, `sandbox:SMALL`. Output via `response.out(JSON.stringify(...))` (see [endpoint output channel](endpoint-output-channel.md)).
|
|
18
|
+
|
|
19
|
+
## Merge report: dashboard consumer
|
|
20
|
+
`scripts/app.ts` (server) emits **only a roster** — a `<script type="application/json">` of `{clientId, firstLast, team}` for every active client — plus the mount div, Chart.js CDN, and `.build/script.js`. No per-client data server-side; `record.id().toString()` (full form) is the wire id the endpoint expects.
|
|
21
|
+
|
|
22
|
+
`static/script.ts` (client) is where the framework lives:
|
|
23
|
+
- On load, default window = **last 4 full Mon–Sun weeks**; date inputs + Apply let the user change it.
|
|
24
|
+
- `loadWeeklyForAll()` builds one fetch task per roster entry and runs them through `runWithConcurrency(tasks, 10)` — a **throttled parallel pool (10 concurrent)** with a progress bar. This is the key pattern: dashboard does N calls to the same endpoint, one per client, varying `clientid` but sharing the date window.
|
|
25
|
+
- `fetchWeeklyForClient` / `fetchFullForClient` build `/b/dpnScoring?clientid=…&startdate=…&enddate=…&action=weekly|full`. Weekly drives the overview (stoplight bar, component lines, phase doughnut, sortable client table with sparklines, flagged bands). Full is **lazy-fetched** only when a client is opened (radar of current-week domains), cached in `state.fullByClient`.
|
|
26
|
+
- Changing the date range clears caches and refetches everything.
|
|
27
|
+
|
|
28
|
+
## The reusable framework, in one line
|
|
29
|
+
A stateless endpoint keyed by `(entity id, date range, action)` returning a `{success,...}` JSON envelope; a thin server that ships only an entity roster; a fat client that fans out throttled-parallel per-entity fetches over a user-chosen window, caches by id, and lazy-loads detail. Build on [merge report memo json](merge-report-memo-json.md)-style server-thin/client-fat but feed the client from a params endpoint instead of a hidden memo.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: B.net.request.method is a method (call it), not a property — calling without parens type-checks but returns the Function reference at runtime
|
|
3
|
+
---
|
|
4
|
+
`B.net.request.method` is typed as `method(): string` in B.d.ts — call it as a function: `B.net.request.method()`.
|
|
5
|
+
|
|
6
|
+
If you write `B.net.request.method` (no parens), TypeScript happily lets it through (the function reference is a truthy value), but at runtime you'll be comparing the Function object against `"GET"` and your method gate will never trigger.
|
|
7
|
+
|
|
8
|
+
Same pattern likely applies to other `request.*` accessors that look property-shaped — check the .d.ts before assuming.
|
|
9
|
+
|
|
10
|
+
Confirmed live on alpineacademy `/b/greatHair` endpoint, May 2026.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "BlueStep endpoints don't route the DELETE (or likely other non-GET/POST/PUT) HTTP method — use a PUT/POST action discriminator instead"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
BlueStep `/b/<alias>` endpoints do **not** dispatch the `DELETE` HTTP method to the script. A `fetch(url, {method:'DELETE'})` never reaches the handler — the platform returns a non-JSON error page, so client-side `r.json()` throws and you see a generic "Network error" (NOT your handler's JSON error). Symptom: delete fails with "Network error — please try again" while POST/PUT/GET actions on the same endpoint work fine.
|
|
6
|
+
|
|
7
|
+
Fix: route mutations through `PUT` (or `POST`) with an `action` discriminator in the JSON body — e.g. `{action:'delete', entryId}` handled by `else if (action === 'delete')` in the `method === 'PUT'` branch. This is the same proven path as complete/reassign/completeWithResult on the WCWF endpoint (havenwoodslc/1523200). Confirmed GET/POST/PUT route; DELETE does not. (Likely PATCH/OPTIONS/etc. also don't — stick to GET/POST/PUT.)
|
|
8
|
+
|
|
9
|
+
Relates to [endpoint method call](endpoint-method-call.md) (request.method() must be called with parens), tracer audit.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "/b/ endpoints write their body via B.net.response.out(string), NOT B.out — B.out is the merge-report channel and yields a blank 'Error' page on an endpoint"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
A BlueStep **`/b/<alias>` endpoint** must write its response body with **`B.net.response.out(stringBody)`** — a method on the Response object. Assigning **`B.out = ...`** does NOT work for an endpoint: `B.out` is the **merge-report** output channel, and using it on a `/b/` endpoint produces BlueStep's generic blank **"Error"** page (the script runs fine, but no endpoint output is ever emitted, so the framework reports failure). The same script type compiles either way — this is a runtime/routing distinction, not a type error.
|
|
6
|
+
|
|
7
|
+
**Endpoint output pattern (confirmed working — alpine RISE endpoint 1515139, and the BEH finance data endpoint /b/financeData):**
|
|
8
|
+
```js
|
|
9
|
+
function writeJson(res, status, body) {
|
|
10
|
+
try { res.status(status); } catch (_e) {} // may throw if already committed
|
|
11
|
+
try { res.contentType("application/json; charset=UTF-8"); } catch (_e) {}
|
|
12
|
+
res.out(JSON.stringify(body)); // <-- the body channel
|
|
13
|
+
}
|
|
14
|
+
// entry:
|
|
15
|
+
const res = B.net.response;
|
|
16
|
+
const method = String(B.net.request.method() || "GET").toUpperCase(); // method() is a function — see endpoint method call
|
|
17
|
+
// ...dispatch on B.net.request.optParameter('x').orElse('')...
|
|
18
|
+
writeJson(res, 200, envelope);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Wrap `status()`/`contentType()` in try/catch (they throw `IllegalStateException` once the response is committed). `res.out()` takes the full string body. Read request via `B.net.request` (`.method()`, `.optParameter(name).orElse(...)`, `.parameter(name)`). Endpoints route GET/POST/PUT but not DELETE ([endpoint no delete method](endpoint-no-delete-method.md)); the friendly `/b/<alias>` is configured per-endpoint, not derivable from the script name ([endpoint urls](endpoint-urls.md)).
|
|
22
|
+
|
|
23
|
+
Contrast: a **merge report** (e.g. the behavioral scorecard) renders via `B.out = htmlString`. So: merge report → `B.out`; `/b/` endpoint → `B.net.response.out(...)`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: BlueStep endpoints are reached at /b/<alias>, not the /files/{id}/draft/ path — the alias is configured per-endpoint and is the URL to give users for testing
|
|
3
|
+
---
|
|
4
|
+
BlueStep endpoint scripts have a configured **friendly URL** at `/b/<alias>` that is what users actually hit in the browser. The `/files/{id}/draft/` path is the editor view, not the runtime endpoint.
|
|
5
|
+
|
|
6
|
+
Example: Sprint Maestro (file id `1434036`, script name "Sprint Maestro") is exposed at `https://beh.bluestep.net/b/sprints?action=team`, not `https://beh.bluestep.net/files/1434036/draft/?action=team`.
|
|
7
|
+
|
|
8
|
+
The alias is set per-endpoint in BlueStep admin and is not derivable from the script name alone — also not necessarily plural-vs-singular consistent (Sprint Maestro → `sprints`).
|
|
9
|
+
|
|
10
|
+
**Symptom of a wrong alias:** the URL returns HTTP 500 with body literally just `Error` (BlueStep's generic page for "no endpoint at that path"). Status 500 + "Error" is NOT necessarily a script-side crash — first verify the alias before debugging the script.
|
|
11
|
+
|
|
12
|
+
**How to apply:**
|
|
13
|
+
- When reporting test URLs to the user, ask what the friendly alias is — do not guess from the script name or fall back to the `/files/.../draft/` URL.
|
|
14
|
+
- The `.b6p_metadata.json` `url` field is the editor URL, not the runtime URL.
|
|
15
|
+
- If the user reports "500 / Error" on an endpoint, ask them to confirm the alias before adding script-side error handling.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Correct runtime API to delete a multi-entry-form entry — entry.entry().delete(), NOT entry.delete() — the typed declaration on the outer interface lies
|
|
3
|
+
---
|
|
4
|
+
When you have a typed multi-entry-form record entry (e.g. `MEFR_sprints`, `MEFR_medicalTasks`), the typed `B.d.ts` claims `delete(): boolean` exists directly on the outer interface (around line 207-210 in current B.d.ts). **It doesn't, at runtime.** Calling it throws:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
TypeError: invokeMember (delete) on myassn.script.relate.FormRecordEntryScript failed due to: Unknown identifier: delete
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The actual API is to first unwrap to the underlying `FormEntry` via `.entry()`, then call `.delete()`:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// Correct
|
|
14
|
+
(entry as any).entry().delete();
|
|
15
|
+
B.commit();
|
|
16
|
+
|
|
17
|
+
// Wrong — runtime "Unknown identifier: delete"
|
|
18
|
+
entry.delete();
|
|
19
|
+
B.commit();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`(entry as any)` is needed because the typed interface doesn't expose `.entry()` consistently across MEF entry types — but it exists at runtime on every form-entry-script object.
|
|
23
|
+
|
|
24
|
+
## Existing usage to confirm pattern
|
|
25
|
+
|
|
26
|
+
Real BlueStep scripts that use this pattern (all snapshotted and live):
|
|
27
|
+
- `alpineacademy/Parent Opportunity API/draft/scripts/app.ts:82` — `entry.entry().delete()`
|
|
28
|
+
- `summitridge/Videra Proxy-Login/draft/scripts/app.ts:206` — `newNote.entry().delete()`
|
|
29
|
+
- `summitridge/1469686/draft/scripts/app.ts:779` — `entryOpt.get().entry().delete()`
|
|
30
|
+
- `alpineacademy/1458534/draft/scripts/app.ts:183` — `appt.entry().delete()`
|
|
31
|
+
- `summitridge/Report System/Report Data Maestro/draft/scripts/app.ts:825`
|
|
32
|
+
|
|
33
|
+
Apparently universal — the typed `entry.delete()` may be aspirational or deprecated.
|
|
34
|
+
|
|
35
|
+
## How to apply
|
|
36
|
+
|
|
37
|
+
- For any "delete this MEF entry" code, write `(entry as any).entry().delete()` (or `entry.entry().delete()` if your entry variable already has FormEntry typing). Call `B.commit()` afterward to persist.
|
|
38
|
+
- Don't trust the `delete(): boolean` signature in `B.d.ts` line ~210 — it lies.
|
|
39
|
+
- The error "Unknown identifier: delete" is the diagnostic sign you've hit this trap.
|
|
40
|
+
- This is for **deleting individual entries** of a multi-entry form. To delete a whole record, use `record.deleteRecord()` (line ~18490 in B.d.ts) — that one IS available at runtime.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# File Execution Model
|
|
2
|
+
|
|
3
|
+
⚠️ **Critical**: understanding which files execute, and when, is essential for BlueStep.js development. The core rule: **a file only runs if it is an automatic entry point or is explicitly imported/referenced.** Files that are neither never execute.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Merge Reports](#merge-reports)
|
|
8
|
+
- [Endpoints and Formulas](#endpoints-and-formulas)
|
|
9
|
+
- [Workspace Sync](#workspace-sync)
|
|
10
|
+
- [Common Mistakes](#common-mistakes)
|
|
11
|
+
|
|
12
|
+
## Merge Reports
|
|
13
|
+
|
|
14
|
+
### Execution entry points
|
|
15
|
+
|
|
16
|
+
Only two files execute automatically:
|
|
17
|
+
|
|
18
|
+
1. **`scripts/app.ts`** — server-side only. Executes on the server, can access `B` and BlueStep APIs, cannot access the DOM, and writes to `B.out` (rendered into the page).
|
|
19
|
+
2. **`static/index.html`** — client-side only. Executes in the browser, can access the DOM, cannot access `B`, and must reference other client files explicitly.
|
|
20
|
+
|
|
21
|
+
### File structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
/scripts/app.ts ← SERVER-SIDE ONLY - executes on the server
|
|
25
|
+
/static/index.html ← CLIENT-SIDE ONLY - executes in the browser
|
|
26
|
+
/static/script.js ← CLIENT-SIDE ONLY - must be referenced by index.html
|
|
27
|
+
/static/styles.css ← CLIENT-SIDE ONLY - for custom HTML only; component-library output needs no extra CSS
|
|
28
|
+
/objects/*.ts ← imported by app.ts or other files
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
All other files must be explicitly imported or referenced. Creating files that aren't imported = wasted code.
|
|
32
|
+
|
|
33
|
+
### HTML structure
|
|
34
|
+
|
|
35
|
+
Merge-report HTML (`static/index.html`) is **embedded into an existing BlueStep page**, so it must NOT include `<!DOCTYPE html>`, `<html>`, `<head>`, or `<body>`. Include only external `<script>`/`<link>` references, the content markup, and the application script at the end.
|
|
36
|
+
|
|
37
|
+
Connect/Manage sites provide the full HTML structure, navigation, branding, user context, and layout framework. Your merge report provides content (via `B.out` from `scripts/app.ts`), client markup (from `static/index.html`), and client scripts/styles. The content is rendered inside the site's content container, not as a standalone page — it is constrained by the site's layout.
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<!-- External libraries (optional) -->
|
|
41
|
+
<link rel="stylesheet" href="styles.css" />
|
|
42
|
+
|
|
43
|
+
<!-- Your content -->
|
|
44
|
+
<div id="app-container"></div>
|
|
45
|
+
|
|
46
|
+
<!-- Application script — use type="module" when script.ts uses ES imports (e.g. genericComponents) -->
|
|
47
|
+
<script type="module" src=".build/script.js"></script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> ⚠️ **`type="module"` is required** whenever `script.ts` uses ES `import` statements (including importing from `genericComponents`). Without it the browser throws `SyntaxError: Cannot use import statement outside a module` and the script will not run.
|
|
51
|
+
|
|
52
|
+
## Endpoints and Formulas
|
|
53
|
+
|
|
54
|
+
### Execution entry points
|
|
55
|
+
|
|
56
|
+
Only one file executes automatically:
|
|
57
|
+
|
|
58
|
+
1. **`scripts/app.ts`** — server-side only. Executes on the server, handles the request/event, can access `B`, cannot access the DOM.
|
|
59
|
+
|
|
60
|
+
### File structure
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
/scripts/app.ts ← SERVER-SIDE ONLY - handles the request/event
|
|
64
|
+
/objects/*.ts ← imported by app.ts
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Query access pattern
|
|
68
|
+
|
|
69
|
+
Which queries are available in `app.ts` is set by the component's **form-import config on the platform**, regenerated into `declarations/index.d.ts` on `b6p pull`. A configured query is a **bare top-level variable** named after the query FID — directly iterable, no `.query()` call:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// app.ts — query is a top-level variable (no .query() needed)
|
|
73
|
+
const unit = topLevelUnit[0];
|
|
74
|
+
const unitName = unit.forms.unitInformation.fields.unitName.opt().orElse('');
|
|
75
|
+
topLevelUnit.forEach((record) => { /* ... */ });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The legacy `objects/imports.ts` `.require()` registration did this in code; it is not used in current modules. For full query patterns (writable context, ad-hoc queries) plus the legacy detail, see [api-patterns](api-patterns.md#query-access-patterns) and [code-patterns](code-patterns.md#query-patterns).
|
|
79
|
+
|
|
80
|
+
## Workspace Sync
|
|
81
|
+
|
|
82
|
+
Each project has a `.b6p_metadata.json` file tracking sync state between the local workspace and the BlueStep server: script metadata (name, org ID, WebDAV ID), per-file last-sync timestamps, and build state. The `draft/` directory holds the working version of the code; the `.build/` directories hold the compiled JavaScript output from the TypeScript source.
|
|
83
|
+
|
|
84
|
+
## Common Mistakes
|
|
85
|
+
|
|
86
|
+
### Creating files that aren't imported
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// ❌ Wrong - objects/helpers.ts exists but is never imported → it never executes
|
|
90
|
+
// ✅ Correct - import it (in app.ts)
|
|
91
|
+
import { helperFunction } from './objects/helpers';
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Mixing server and client code
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// ❌ Wrong - DOM access in server code (scripts/app.ts)
|
|
98
|
+
document.getElementById('myElement'); // ERROR: document is not defined
|
|
99
|
+
// ❌ Wrong - B object in client code (static/script.js)
|
|
100
|
+
const user = B.user; // ERROR: B is not defined
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Incorrect HTML structure
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<!-- ❌ Wrong - full HTML document -->
|
|
107
|
+
<!DOCTYPE html><html><head><title>My Report</title></head><body><div>Content</div></body></html>
|
|
108
|
+
|
|
109
|
+
<!-- ✅ Correct - content and references only -->
|
|
110
|
+
<link rel="stylesheet" href="styles.css" />
|
|
111
|
+
<div>Content</div>
|
|
112
|
+
<script src=".build/script.js"></script>
|
|
113
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Correct method names on B.net.httpRequester for non-GET calls — uses overloaded getter/setter style (.method(), .responseCode()), not Java-bean style (.setMethod, .getResponseCode)
|
|
3
|
+
---
|
|
4
|
+
`B.net.httpRequester(url)` returns an object whose mutators and accessors share the same name (overloaded). The Java-bean-style `setX`/`getX` names do **NOT** exist and are a TypeScript error.
|
|
5
|
+
|
|
6
|
+
```typescript
|
|
7
|
+
// Correct
|
|
8
|
+
const req = B.net.httpRequester(url);
|
|
9
|
+
req.method('POST'); // setter (string arg)
|
|
10
|
+
req.setHeader('Authorization', API_KEY); // setHeader IS the right name (no overload)
|
|
11
|
+
req.setHeader('Accept', 'application/json');
|
|
12
|
+
req.doRequest();
|
|
13
|
+
const status = req.responseCode(); // getter (no args) — returns number | null
|
|
14
|
+
const body = req.getContent('UTF-8'); // getContent IS the right name
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// Wrong — these do NOT exist
|
|
19
|
+
req.setMethod('POST');
|
|
20
|
+
req.getResponseCode();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Methods that follow the bean naming**: `setHeader`, `getContent`, `doRequest`. **Methods that use the overloaded style**: `method`, `responseCode`. The split is inconsistent — when in doubt, look at `B.d.ts` declarations.
|
|
24
|
+
|
|
25
|
+
## Reading POST body in an endpoint
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
const bodyText = B.net.request.content() || ''; // returns string
|
|
29
|
+
const body = JSON.parse(bodyText);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`request.content()` is the canonical accessor — confirmed in declarations, used by the existing Sprint Maestro endpoint.
|
|
33
|
+
|
|
34
|
+
**How to apply:**
|
|
35
|
+
- For DELETE / POST / PUT calls (e.g., ClickUp tag reconciliation, REST writes), use `req.method('POST')` not `req.setMethod('POST')`.
|
|
36
|
+
- For checking response status to handle 404-as-success (idempotent deletes), use `req.responseCode()` not `req.getResponseCode()`.
|
|
37
|
+
- If a TypeScript error says `Property 'setMethod' does not exist`, this memory is why.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Id.toString() returns ClassID___ShortID (full id); shortId() returns just the trailing number. optById needs the full form when round-tripping freshly-created entries.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`entry.id().toString()` returns the **full system id** in `ClassID___ShortID` form (e.g. `1000201__21584230_2178970123`). `entry.id().shortId()` returns just the trailing record number (`2178970123`).
|
|
6
|
+
|
|
7
|
+
Declared at `B.d.ts:14373-14379`: *"Returns the standard System ID. That is ClassID___ShortID."*
|
|
8
|
+
|
|
9
|
+
`query.optById(s)` accepts the full form reliably. Feeding it just a `shortId()` can produce a malformed reconstructed key with an empty ClassID segment (e.g. `1000201___1153030` — three underscores), and `FinderException: Could not load Model for key: ...` is thrown.
|
|
10
|
+
|
|
11
|
+
**Why this matters for endpoint round-trips:** when an endpoint creates a new entry and returns its id to the client for a follow-up call (e.g. document upload after student creation), return `id().toString()`, not `id().shortId()`. If the UI also needs the short form for display, return both.
|
|
12
|
+
|
|
13
|
+
Related: [new entry id](new-entry-id.md) (commit before reading shortId).
|
|
14
|
+
|
|
15
|
+
**How to apply:** any time an id is going to be passed back to a server for `optById` lookup, use the full id. Reserve `shortId()` for human-readable display only.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Endpoint-to-endpoint loopback within an org must use B.net.fetch on a relative path + credentials:true, NOT the external httpRequester"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
For a BlueStep endpoint to call another endpoint in the SAME org (e.g. /b/aiAudio → /b/aiCredits), use `B.net.fetch(relativePath, {...})` — NOT `B.net.httpRequester(absoluteUrl)`.
|
|
6
|
+
|
|
7
|
+
`httpRequester` dials the server's own public hostname over the network; the server can't hairpin back to itself (NAT/firewall), so `doRequest()` never connects: `responseCode()` returns null and `getContent()` throws `Cannot invoke "java.io.InputStream.read(...)" because "this.in" is null`.
|
|
8
|
+
|
|
9
|
+
`B.net.fetch` on a RELATIVE path (`"/b/aiCredits"`) routes through BlueStep's internal dispatcher — no public round-trip — and `credentials: true` carries the caller's session so a login-required target doesn't 403. Read the result via `res.code()` (FetchedResource HTTP status) and `B.io.fromInputStream(res, "UTF-8")` for the body.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const res = B.net.fetch("/b/aiCredits", {
|
|
13
|
+
method: "POST", credentials: true, followRedirects: false, enableErrorStream: true,
|
|
14
|
+
connectionTimeout: 10000, readTimeout: 15000,
|
|
15
|
+
headers: { "Content-Type": "application/json", "Accept": "application/json" },
|
|
16
|
+
body: JSON.stringify({ action: "check" })
|
|
17
|
+
});
|
|
18
|
+
const code = res.code(); // HTTP status
|
|
19
|
+
const text = B.io.fromInputStream(res, "UTF-8"); // body
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`followRedirects:false` makes an auth bounce show as a non-2xx code instead of a login-page 200; `enableErrorStream:true` lets you still read the body on a non-2xx. FetchParams uses `connectionTimeout`/`readTimeout` (note the names). Confirmed working 2026-06-10 on summitridge /b/aiAudio (1471299) → /b/aiCredits (1471659): both the credit `check` gate and the `log` write now succeed and the BlueIQ ledger records spend.
|
|
23
|
+
|
|
24
|
+
Complements [session cookie forwarding](session-cookie-forwarding.md) (the cookie-via-raw-header trick was for httpRequester loopbacks; prefer B.net.fetch + credentials instead). Related: [endpoint output channel](endpoint-output-channel.md), [http requester](http-requester.md) (httpRequester is still correct for EXTERNAL calls like api.openai.com), blueiq credit system.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Java.Time.LocalDate is a TypeScript namespace only; for runtime LocalDate.parse() / now() / etc. use B.time.LocalDate
|
|
3
|
+
---
|
|
4
|
+
For runtime LocalDate operations, use `B.time.LocalDate`:
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
const d = B.time.LocalDate.parse("2025-01-01");
|
|
8
|
+
const today = B.time.LocalDate.now();
|
|
9
|
+
if (d.isAfter(today)) { ... }
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`Java.Time.LocalDate` exists in the .d.ts as a namespace for type annotations, but `Java.Time` is NOT a runtime symbol — calling `Java.Time.LocalDate.parse(...)` throws `Java is not defined` (or returns undefined). Same applies to `Java.Time.ZonedDateTime`, `Java.Time.LocalDateTime`, etc.
|
|
13
|
+
|
|
14
|
+
**Use `Java.Time.X` only as type annotation; use `B.time.X` at runtime.**
|
|
15
|
+
|
|
16
|
+
Confirmed on alpineacademy `/b/greatHair`, May 2026.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The \"merge report + memo field JSON hack\" — core framework for embedding a custom interactive widget on a BlueStep form that persists its state as JSON in a hidden memo field"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The recurring **"merge report + memo field JSON hack"** — how Brandon and I repeatedly build custom interactive UI on a BlueStep form without creating real sub-forms/MEF entries. This is the foundational framework; [multi entry in multi entry](multi-entry-in-multi-entry.md) and the permission layer [staff query permission gating](staff-query-permission-gating.md) are built on top of it.
|
|
6
|
+
|
|
7
|
+
**The idea:** A BlueStep **merge report placed on a form** renders custom HTML/JS. All widget state is stored as a single JSON blob inside one **hidden memo (or JSON-memo) field** on that same form. The client hijacks the form's own Save to persist — no separate records, no MEF plumbing. One memo field = the entire data model.
|
|
8
|
+
|
|
9
|
+
**Architecture (server-thin / client-fat), as separate files in a pulled component:**
|
|
10
|
+
- `draft/scripts/app.ts` (server scriptlet) — thin. Gathers anything the client needs that requires server context (current user, permission flags, reference/query data), emits it as `<script id="server-data" type="application/json">{...}</script>` plus an empty mount `<div id="...-app"></div>`, assigned to `B.out`. Wrap everything in try/catch; on failure emit a safe payload so the client still degrades cleanly.
|
|
11
|
+
- `draft/static/script.ts` (client) — the engine. On boot it:
|
|
12
|
+
1. Finds the memo via `document.querySelector('[data-fid="<fieldName>"]')` (it's a `<textarea>`).
|
|
13
|
+
2. **Hides the memo field's row** (`field.closest('tr')` → `display:none`) so users never see raw JSON.
|
|
14
|
+
3. Hydrates state from the live memo value (`memo.value`), falling back to server-emitted copy, else a default `{ schemaVersion, entries: [] }`.
|
|
15
|
+
4. Monkey-patches `window.submitForm`: wraps the original so it writes `memo.value = JSON.stringify(state)` right before the native save runs. The platform then persists the memo normally.
|
|
16
|
+
5. Renders custom UI into the mount; a single `mutate()` does state → syncToField → re-render.
|
|
17
|
+
- `draft/static/styles.css` — lean on native BlueStep classes (`table table-striped`, `btn`, `sectionHeader sectionRuleB`); custom prefixed classes only for layout + modals.
|
|
18
|
+
|
|
19
|
+
**Critical gotchas (all learned the hard way):**
|
|
20
|
+
- Injected controls must have **NO `name` attribute** — named controls get submitted with the form → "problem storing the data". See [named controls submit](named-controls-submit.md).
|
|
21
|
+
- Mount modals on `document.body` to escape the form's submit scope.
|
|
22
|
+
- Borrow native relate icons (`/static/.../relate-icons/pencil.svg`, `trash.svg`) and call `window.fixAllSVG()` after render to recolor them like native lists.
|
|
23
|
+
- Build pipeline / push rules: [tsc rootdir](../conventions/tsc-rootdir.md), [push inner draft](../conventions/push-inner-draft.md), [separate files](../conventions/separate-files.md), [always snapshot](../conventions/always-snapshot.md).
|
|
24
|
+
|
|
25
|
+
**Reference implementations:** kaizenacademy/1465899 "Treatment Plan Review Comments Display" (original inspo, has Domains + lock); kaizenacademy/1466219 "Protocol Update Table" (notes-only + permission gating). Related JSON-in-memo dashboards: [crm dashboard inspo](crm-dashboard-inspo.md), visit dashboard.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: BlueStep merge report with a static/ bundle renders static/index.html as the page; scripts/app.ts B.out is NOT injected — put the mount + config island in index.html and run the client on DOMContentLoaded
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
For a BlueStep merge report that ships a `static/` bundle (`static/index.html` +
|
|
6
|
+
`static/.build/script.js` + `static/styles.css`), the **served page is
|
|
7
|
+
`static/index.html`** — its `<script src=".build/script.js">` is what loads the
|
|
8
|
+
client. The server `scripts/app.ts` `B.out` is **not injected into that page**, so
|
|
9
|
+
a mount node / server-data island placed in `B.out` never appears in the DOM.
|
|
10
|
+
|
|
11
|
+
Symptom we hit (summitridge Resources Hub merge report 1471799): client JS ran but
|
|
12
|
+
`document.getElementById('rh-root')` and the `#rh-config` blob were both null,
|
|
13
|
+
because the mount + config were emitted from `B.out`. Spinner hung forever.
|
|
14
|
+
|
|
15
|
+
**Do this instead:**
|
|
16
|
+
- Put the root mount (`<div id="rh-root">`) and any static config island
|
|
17
|
+
(`<script id="rh-config" type="application/json">…</script>`) **directly in
|
|
18
|
+
`static/index.html`**, before the `<script src=".build/script.js">` tag. (Mirror
|
|
19
|
+
of the working Treatment Targets widget: `<div id="tt-widget"></div>` lives in
|
|
20
|
+
index.html.)
|
|
21
|
+
- Run the client on DOM-ready: `if (document.readyState==='loading')
|
|
22
|
+
document.addEventListener('DOMContentLoaded', start); else start();`
|
|
23
|
+
- The client should fetch all dynamic data from its endpoint (server-thin/client-fat)
|
|
24
|
+
rather than expecting server-rendered data from `B.out`.
|
|
25
|
+
- Server-side per-user data (e.g. current user for a form prefill) can't ride in via
|
|
26
|
+
`B.out`; defer it, fetch it from the endpoint, or use index.html merge tokens.
|
|
27
|
+
|
|
28
|
+
Builds on [merge report memo json](merge-report-memo-json.md) and
|
|
29
|
+
[separate files](../conventions/separate-files.md); relevant to bluestep resources.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: BSJS equivalents for relatescript form.System.lookupMergeReport / lookupMergeReportScript and how to get viewUrl/printUrl
|
|
3
|
+
---
|
|
4
|
+
# BSJS Merge Report URL Lookup
|
|
5
|
+
|
|
6
|
+
Relatescript provides `form.System.lookupMergeReport("id","x")` and `lookupMergeReportScript("id","x")` returning `{name, label, viewURL, printURL, customProps}`. BSJS splits this across two APIs.
|
|
7
|
+
|
|
8
|
+
## Direct id lookup (no record applicability filter)
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
// Short id + lang ('js' BSJS, 'rs' relatescript):
|
|
12
|
+
B.find.mergeReport("shortId", "js").viewUrl()
|
|
13
|
+
|
|
14
|
+
// Full id, no lang:
|
|
15
|
+
B.find.mergeReport("530024__FID_clientHeader").viewUrl()
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Returns `MergeReportMetaData` (inherits `viewUrl()`, `editUrl()`, `id()`, `displayName()` from `BaseObject`). No `printUrl()` here.
|
|
19
|
+
|
|
20
|
+
## Lookup by custom property, scoped to a record's form (true equivalent of `lookupMergeReport`)
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// BSJS reports (= lookupMergeReportScript):
|
|
24
|
+
formEntry.optApplicableBsJsMergeReport("id", "x").get()
|
|
25
|
+
|
|
26
|
+
// Relatescript reports (= lookupMergeReport):
|
|
27
|
+
formEntry.optApplicableMergeReport("id", "x").get()
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Returns full `MergeReport<T>` with `viewUrl()`, `printUrl()`, `profileUrl()`, `newEntryUrl()`, `copyUrl()`, `editUrl()`, `displayName()`, `id()`, `altIds()`, etc.
|
|
31
|
+
|
|
32
|
+
Also: `applicableBsJsMergeReportResults(...)` returns `{results, message}` for error reporting.
|
|
33
|
+
|
|
34
|
+
## Property mapping
|
|
35
|
+
|
|
36
|
+
| relatescript | BSJS |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `result.viewURL` | `.viewUrl()` |
|
|
39
|
+
| `result.printURL` | `.printUrl()` (only on full `MergeReport`, not metadata) |
|
|
40
|
+
| `result.name` | `.displayName()` (or `.id()` for the id) |
|
|
41
|
+
| `result.label` | `.displayName()` |
|
|
42
|
+
| `result.customProps` | no enumerator — pass as filter args, or walk `.altIds()` |
|
|
43
|
+
|
|
44
|
+
## Gotchas
|
|
45
|
+
|
|
46
|
+
- `viewUrl()` is **relative**. For absolute (emails etc.) wrap with `B.toFullyQualifiedUrl(rel)`.
|
|
47
|
+
- `B.siteNavigation()` returns `SiteNavItem` (pages) — **does NOT** have `optLookupMergeReport`. The `optLookup*MergeReport` methods are on `RecordNavItem`, obtainable via `record.nav()`, but `record.nav()` is documented as expensive (30+ sec on uncached orgs). Prefer `B.find.mergeReport` or `optApplicableBsJsMergeReport`.
|
|
48
|
+
- `B.find.mergeReport(id, 'js')` does NOT filter by record applicability — it's an id lookup. Only `optApplicable*` methods on FormEntry filter by applies-to-this-record.
|
|
49
|
+
- Both APIs work in `MergeReportB` and `CommitableB` contexts (read-only).
|
|
50
|
+
- **The `BsJs` prefix names the TARGET, not the caller.** Pick `optApplicableMergeReport` if the merge report you're looking up is a relatescript merge report — even when you're calling from a BSJS endpoint. Pick `optApplicableBsJsMergeReport` only if the target itself is a BSJS merge report. Confirmed in production on summitridge Report Viewer (Apr 2026): looking up the relatescript "unsignedForms" merge report from a BSJS CommitableB endpoint required `optApplicableMergeReport`, not the BsJs variant.
|
|
51
|
+
|
|
52
|
+
## Existing examples in pulled code
|
|
53
|
+
|
|
54
|
+
- `summitridge/Settings Events/draft/scripts/app.ts:52-54, 81, 276` — `B.find.mergeReport(shortId, 'rs').id()` / `.displayName()`
|
|
55
|
+
- `alpineacademy/1463039/draft/scripts/medFunctions.ts:171` — `optApplicableMergeReport("id","medPrescribed").get().viewUrl()`
|
|
56
|
+
- `beh/1433876/draft/scripts/app.ts:167` — `BaseObject.viewUrl()` on a record
|
|
57
|
+
|
|
58
|
+
## Declarations source
|
|
59
|
+
|
|
60
|
+
All in `declarations/B.d.ts`:
|
|
61
|
+
- `B.find.mergeReport`: lines 23984-24001
|
|
62
|
+
- `MergeReportMetaData`: line 11373
|
|
63
|
+
- `MergeReport<T>` (printUrl 8398, profileUrl 8408, newEntryUrl 8413, copyUrl 8423): line 8389
|
|
64
|
+
- `BaseObject.viewUrl()`: line 24613
|
|
65
|
+
- `FormEntry.optApplicableBsJsMergeReport`: line 7585
|
|
66
|
+
- `FormEntry.optApplicableMergeReport`: line 7573
|
|
67
|
+
- `RecordNavItem.optLookupBsJsMergeReport`: line 10292
|