@bloomscorp/blooms-ai-embed 0.1.1 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,104 @@
2
2
 
3
3
  All notable changes to `@bloomscorp/blooms-ai-embed`.
4
4
 
5
+ ## 0.2.0
6
+
7
+ Twelve new components, and the bundle is now code-split so adopting one does not
8
+ cost you all of them.
9
+
10
+ Upgrading is a drop-in **unless you branch on `customElements.get()`** or stage
11
+ `dist/` by hand — see *Action if you are upgrading* below.
12
+
13
+ ### Added
14
+
15
+ - **Twelve new elements.** `<blooms-login>` is no longer the only thing you can
16
+ embed:
17
+
18
+ | | | |
19
+ |---|---|---|
20
+ | `<blooms-crawl-insights>` | `<blooms-sitemap-crawl>` | `<blooms-sitemap-crawl-detail>` |
21
+ | `<blooms-keywords>` | `<blooms-keyword-detail>` | `<blooms-reports>` |
22
+ | `<blooms-gmc-analytics>` | `<blooms-social-media-calendar>` | `<blooms-smc-day-detail>` |
23
+ | `<blooms-influencer-dashboard>` | `<blooms-influencer-detail>` | `<blooms-blog-strategy>` |
24
+
25
+ Each takes its state through attributes or properties rather than a URL, since
26
+ a custom element cannot read the host page's address bar: `project-id` on most,
27
+ plus `crawl-id`, `keyword-id`, `influencer-id`, `strategy-id`, `event-id`,
28
+ `date-iso` where the component addresses one record. Components that used to
29
+ navigate now emit a `bloomsnavigate` event describing where they would have
30
+ gone — your app decides what that means.
31
+
32
+ Declared in `HTMLElementTagNameMap`, so TypeScript knows all thirteen tags.
33
+
34
+ - **`preload(...features)`.** Fetches a component's code before it is needed:
35
+
36
+ ```js
37
+ await BloomsEmbed.preload('crawl-insights', 'keywords');
38
+ ```
39
+
40
+ Optional and never throws — a failed preload just means the chunk is fetched
41
+ later, when the component is actually mounted.
42
+
43
+ ### Changed
44
+
45
+ - **The bundle is code-split: one lazily-fetched chunk per component.** Shipping
46
+ thirteen components eagerly had grown the download to 162 kB gzipped, with the
47
+ three largest still unconverted — so every host paid for every component to use
48
+ one. Now:
49
+
50
+ | | Transfer (gzipped) |
51
+ |---|---|
52
+ | Loader + shared core | **~65 kB** |
53
+ | …plus one component | ~69 kB |
54
+ | …plus all thirteen | ~180 kB |
55
+
56
+ `dist/blooms-embed.js` is a ~3 kB loader; the components live in sibling
57
+ `dist/chunk-*.js` files that it imports by relative path on first use.
58
+
59
+ - **Elements are defined on demand, not at import.** A tag is registered the
60
+ first time an instance of it appears, and the browser upgrades the element that
61
+ was already there. Tags in your HTML behave exactly as before and there is
62
+ nothing to await. One observable difference:
63
+
64
+ ```js
65
+ import { BloomsEmbed } from '@bloomscorp/blooms-ai-embed';
66
+ customElements.get('blooms-crawl-insights'); // undefined until one is used
67
+ ```
68
+
69
+ Setting a **property** before a component's chunk arrives is safe — the value
70
+ is applied once the component is defined. Attributes were never affected.
71
+
72
+ - **Write actions are enabled** for Social Media Calendar, Influencers and Blog
73
+ Strategy — create, edit, reschedule, delete, and the AI generation steps. They
74
+ were read-only in `0.1.x`, with controls that rendered and then returned 403.
75
+
76
+ ### Action if you are upgrading
77
+
78
+ 1. **If you bundle the package** (the recommended setup, and what the Electron
79
+ guide describes) — nothing to do. Webpack, Vite, esbuild and Rollup all follow
80
+ the dynamic imports and emit their own chunks.
81
+
82
+ 2. **If you stage `dist/` by hand**, copy **all** of it. `chunk-*.js` must sit in
83
+ the same directory as `blooms-embed.js`. Copying only the entry leaves you with
84
+ a package that installs cleanly and renders every component as a blank
85
+ element, with one console error per component as the only clue.
86
+
87
+ 3. **Remove any `customElements.get()` guard** around showing a component. It was
88
+ reliable in `0.1.x` because every tag was defined at import; it is not now.
89
+ Place the tag, or call `mount()`, and let the loader do its work.
90
+
91
+ ### Known gaps, stated so they are not surprises
92
+
93
+ - `<blooms-keyword-detail>`'s Search Console section is **empty when embedded**.
94
+ The underlying route takes its project id as a query parameter and carries no
95
+ authorization of its own, and our allowlist pins project ids in the path — so
96
+ exposing it would have handed every embedded session cross-project Search
97
+ Console data. The route is being fixed rather than the allowlist widened.
98
+ - Report generation (Crawl Insights) and bulk delete (Reports) are hidden when
99
+ embedded: their output is delivered to the blooms.ai portal, which a host app
100
+ cannot reach. Starting a job whose result you cannot collect is worse than not
101
+ offering it.
102
+
5
103
  ## 0.1.1
6
104
 
7
105
  Everything here came from the first production Electron integration reporting
package/README.md CHANGED
@@ -59,11 +59,49 @@ That is the whole integration. Importing the package defines the elements and in
59
59
  `<blooms-login>` renders the sign-in form, remembers the session across reloads, and revalidates
60
60
  it on the next load.
61
61
 
62
+ ### Elements in this bundle
63
+
64
+ | Tag | What it renders | Token ceiling it needs |
65
+ |---|---|---|
66
+ | `<blooms-login>` | Sign-in form, session persistence, precise error states | — |
67
+ | `<blooms-crawl-insights>` | Crawl results: pages crawled, broken links, page issues, missing meta, duplicate titles, health score. Starts crawls and re-scans single URLs. | `crawl-insights` (or `crawl`) |
68
+ | `<blooms-sitemap-crawl>` | Crawl from a sitemap: start a run, watch progress, browse history. | `sitemap` (or `crawl`) |
69
+ | `<blooms-sitemap-crawl-detail>` | One sitemap run in detail. Takes `crawl-id`. | `sitemap` (or `crawl`) |
70
+ | `<blooms-keywords>` | Tracked keywords: rank, volume, difficulty, trend. Adds keywords, triggers a rank sync. | `manager` |
71
+ | `<blooms-keyword-detail>` | One keyword in detail. Takes `keyword-id`. Its Search Console section is empty when embedded — see below. | `manager` |
72
+ | `<blooms-reports>` | Report list and downloads. Generation and bulk delete are hidden when embedded. | `manager` |
73
+ | `<blooms-gmc-analytics>` | Google Merchant Centre overview: feed health, disapprovals, account issues. | `manager` |
74
+ | `<blooms-social-media-calendar>` | Content calendar: month and list views, statuses, attachments, assignees. Create, edit, reschedule, delete. Takes `event-id` to deep-link a drawer. | `social-media-calendar` |
75
+ | `<blooms-smc-day-detail>` | One day of the calendar. Takes `date-iso` (`YYYY-MM-DD`), required. | `social-media-calendar` |
76
+ | `<blooms-influencer-dashboard>` | Tracked influencers with tiers, platforms, latest metrics. Create and edit. | `influencer-management` |
77
+ | `<blooms-influencer-detail>` | One influencer: profile, platforms, snapshots. Takes `influencer-id`. | `influencer-management` |
78
+ | `<blooms-blog-strategy>` | The blog authoring workflow: topic → SEO research → outline → draft → visuals → metadata → review → publish → promotion → performance. Takes `strategy-id` and `step`. | `blog-publishing` |
79
+
80
+ A `manager` or `owner` ceiling covers all of them, so one token needs no edit as
81
+ you adopt more. The keys above matter only for a deliberately narrow ceiling —
82
+ and Keywords, Reports and GMC are gated on `manager` itself, so a narrow ceiling
83
+ cannot include them without including everything. A ceiling is never a grant: the
84
+ signed-in user's own per-project permissions are evaluated on every request.
85
+
86
+ Components that would navigate emit `bloomsnavigate` instead of routing — the
87
+ detail is in the Electron guide, and the payload is typed.
88
+
89
+ **Two deliberate gaps.** `<blooms-keyword-detail>`'s Search Console section is
90
+ empty when embedded: that route takes its project id as a query parameter and has
91
+ no authorization of its own, so exposing it would leak cross-project data — being
92
+ fixed server-side. Report generation and bulk delete are hidden because their
93
+ output is delivered to the blooms.ai portal, which your app cannot reach.
94
+
95
+ Feature elements render a sign-in prompt until a session exists, so you can place them
96
+ unconditionally. Each takes an optional `project-id`; omit it and the element follows the
97
+ session's active project, so `BloomsEmbed.setProject()` moves it.
98
+
62
99
  Three things worth knowing:
63
100
 
64
- - **`configure()` once, early.** Elements placed in your HTML upgrade the moment the package
65
- evaluates, which is before your `configure()` call runs. They wait for it rather than failing,
66
- showing a skeleton meanwhile — so a slow `configure()` is a slow first paint, not an error.
101
+ - **`configure()` once, early.** An element placed in your HTML is upgraded when its chunk
102
+ arrives, which may be before or after your `configure()` call. Either order is fine: it waits
103
+ for configuration rather than failing, showing a skeleton meanwhile — so a slow `configure()`
104
+ is a slow first paint, not an error.
67
105
  - **`apiBase` is optional** when you load the bundle from blooms.ai, because it defaults to the
68
106
  bundle's own origin. When you install from npm the bundle's origin is *your* app, so **pass
69
107
  `apiBase` explicitly**.
@@ -79,6 +117,7 @@ Three things worth knowing:
79
117
  | `version` | `string` | Bundle version. |
80
118
  | `configure` | `(config: BloomsEmbedConfig) => void` | Call first. Throws on a missing token or a token that is not `blm_embed_…`. |
81
119
  | `mode` | `() => 'element' \| 'iframe' \| null` | The resolved rendering mode; `null` before `configure()`. |
120
+ | `preload` | `(...features: string[]) => Promise<void>` | Warms a component's lazy chunk before it is used. Optional, never throws. |
82
121
  | `manifest` | `() => Promise<EmbedManifest>` | Pre-login bootstrap: project name, enabled features, theme, branding flag, session TTL. |
83
122
  | `login` | `({ email, password }) => Promise<EmbedSessionState>` | Programmatic alternative to `<blooms-login>`, for hosts with their own form. |
84
123
  | `session` | `() => EmbedSessionState \| null` | The current session, restored from storage by `configure()`. Synchronous. |
@@ -409,6 +448,40 @@ In Electron, that also means: import it in the **renderer**, never in the main p
409
448
 
410
449
  ---
411
450
 
451
+ ## Lazy loading
452
+
453
+ The bundle is code-split: a small loader plus one chunk per component, fetched the first time that
454
+ component is used. A page embedding one component downloads one component (~69 kB gzipped, against
455
+ ~180 kB for the full set).
456
+
457
+ Nothing in your markup changes. A tag in your HTML is defined when its chunk arrives and the
458
+ browser upgrades the element that was already there. You never await anything.
459
+
460
+ Three things worth knowing:
461
+
462
+ - **Do not gate your UI on `customElements.get(tag)`.** It returns `undefined` until that
463
+ component has been used once. Before code splitting it was defined immediately, so a check like
464
+ this used to pass:
465
+
466
+ ```js
467
+ import { BloomsEmbed } from '@bloomscorp/blooms-ai-embed';
468
+ if (customElements.get('blooms-keywords')) { /* no longer true up front */ }
469
+ ```
470
+
471
+ - **Setting properties early is safe.** `el.projectId = '...'` on an element whose chunk has not
472
+ arrived is preserved and applied once the component is defined. Attributes were never affected.
473
+
474
+ - **Warm a chunk if you know what is next:**
475
+
476
+ ```js
477
+ await BloomsEmbed.preload('crawl-insights', 'keywords');
478
+ ```
479
+
480
+ If you stage `dist/` by hand instead of importing the package, copy **all** of it — `chunk-*.js`
481
+ must stay beside `blooms-embed.js`, and `media/*.woff2` beside the stylesheet.
482
+
483
+ ---
484
+
412
485
  ## Security notes worth reading once
413
486
 
414
487
  - The embed token is publishable, but it is not a secret to be careless with: it is what ties