Envie
Project

Changelog

What changed in the Envie API, SDK, and components — including deprecation notices, at least 90 days ahead.

Release notes for the API, @getenvie/client, and @getenvie/components. Deprecations appear here at least 90 days before taking effect.

@getenvie/client

0.1.0

Minor Changes

  • 6e91d55: Wishlist reads no longer pretend a list exists before the first save.

    getWishlist() now returns EnvieList with id and updatedAt nullable — the API creates no row on a read, so a shopper with nothing saved gets an honest empty representation instead of a freshly minted list. identify() returns the new EnvieMergedList, whose id is always present because a merge always ends with a list.

    Record ids also carry a type prefix on the wire (lst_, itm_), so an id is readable in a log line and a list id sent to an item endpoint is a 400 rather than a silent miss.

  • 9b599b7: Add <envie-list> and <envie-share>.

    <envie-list> renders saved items into the light DOM by cloning a <template slot="item"> you supply, so your theme's own product-card CSS applies with no shadow piercing. Envie returns identity only, so title and image come from the shop's own /products/{handle}.js — nothing here can go stale, and price formatting stays with the theme, which has Liquid's money filters. Hooks: data-envie-link, data-envie-image, data-envie-title, data-envie-toggle. A product the shop 404s gets data-envie-unavailable; a failed fetch falls back to the stored snapshot instead, because a network blip is not a discontinued product.

    Pass share-token, or land on a URL carrying ?list=<token>, and the same element renders that shared list read-only.

    <envie-share> mints a link and copies it, and stays hidden unless the shop allows sharing and the shopper is a logged-in customer — anonymous lists cannot be shared, and a button that always 403s is worse than no button.

    The client bridge gained share() and shared(), and EnvieList now carries owner.

  • f75efb2: Merge the anonymous wishlist into the customer's on login, automatically.

    createEnvie({ customerId }) fires a one-shot identify() when a customer is present, so a shopper who saved items logged out keeps them after signing in without the theme wiring anything up. The theme app embed passes Liquid's customer.id.

    It runs once per (browser, anonymous list): the marker is keyed on both, and a spent anonymous id is rotated after a successful merge — otherwise a shopper who logs out, saves more, and logs back in would have that second list stranded forever. Failures never reject and never write the marker, so the next page view retries.

    EnvieStore.refresh() is new: the client calls it after a merge so an already-rendered page shows the merged list without a reload.

  • e61a7e8: Add the import methods to the private surface: createUploadTarget, createImport and import.

    createUploadTarget returns a presigned PUT plus the fileUrl to hand back to createImport, so a file goes from the browser to object storage directly and never travels through the API.

    createImport takes dryRun — run it that way first on a competitor export. The report tells you which rows would be skipped and why while it is still free to fix. Real runs are idempotent: re-importing the same file imports nothing twice and never rewrites an existing item's addedAt.

  • 865f9d8: New DOM events on document (bubbling to window): envie:ready fires once per page when the store's first load completes (detail: { count, config, list }), and envie:merged fires when a guest list is merged into the customer's at login (detail: the merged list).

  • e61a7e8: Add the settings and webhook methods to the private surface: updateSettings, webhooks, createWebhook, deleteWebhook and testWebhook.

    updateSettings patches rather than replaces, so a caller sends only what it changed. Its klaviyoKey field is write-only — the API stores it encrypted and never returns it, and null disconnects the integration.

    createWebhook is the only call that ever returns a signing secret; there is no way to read it back afterwards.

Patch Changes

  • b8f2e46: Clean public-facing descriptions: curated OpenAPI operation ids and summaries (raw controller ids no longer leak into the SDK or docs), and internal spec references removed from API descriptions and component JSDoc.

@getenvie/components

0.1.0

Minor Changes

  • e28aff5: autoInject can place the header badge, not only buttons.

    Rules take an element (envie-button by default, or envie-badge), verbatim attributes, and once to stop after the first match. A badge needs no product, so the handle lookup is skipped for it.

    This matters because a managed store that can save products but gives shoppers no way back to their wishlist is worse than one with neither, and plenty of themes' header sections accept no app blocks at all — so the badge block cannot always be placed by hand.

    once is tracked per rule rather than per container: a re-run after any DOM change would otherwise find the first match already marked and inject a second badge into the next one.

  • 00beb8b: Add the managed integration tier: automatic button placement.

    autoInject(rules) puts an <envie-button> on every product page and collection card matching a set of CSS selectors, so a merchant can go live from the theme editor without touching Liquid. The theme app embed writes its rules to window.__envieConfig.autoInject and the bundle acts on them; with no rules — the default — nothing runs.

    Injection is idempotent per container, keeps up with DOM changes through a MutationObserver batched to one frame (themes paginate, filter and quick-view long after load), and marks containers with no product link so they are not re-examined forever.

    <envie-button> now accepts a handle with no product-id. That is what makes this cheap on a collection page: a card links to /products/{handle} and knows no id, so the id is resolved from the shop on first click — one request per interaction instead of one per card — and saved state comes from the new EnvieState.savedHandles.

  • 9b599b7: Add <envie-list> and <envie-share>.

    <envie-list> renders saved items into the light DOM by cloning a <template slot="item"> you supply, so your theme's own product-card CSS applies with no shadow piercing. Envie returns identity only, so title and image come from the shop's own /products/{handle}.js — nothing here can go stale, and price formatting stays with the theme, which has Liquid's money filters. Hooks: data-envie-link, data-envie-image, data-envie-title, data-envie-toggle. A product the shop 404s gets data-envie-unavailable; a failed fetch falls back to the stored snapshot instead, because a network blip is not a discontinued product.

    Pass share-token, or land on a URL carrying ?list=<token>, and the same element renders that shared list read-only.

    <envie-share> mints a link and copies it, and stays hidden unless the shop allows sharing and the shopper is a logged-in customer — anonymous lists cannot be shared, and a button that always 403s is worse than no button.

    The client bridge gained share() and shared(), and EnvieList now carries owner.

  • f75efb2: Merge the anonymous wishlist into the customer's on login, automatically.

    createEnvie({ customerId }) fires a one-shot identify() when a customer is present, so a shopper who saved items logged out keeps them after signing in without the theme wiring anything up. The theme app embed passes Liquid's customer.id.

    It runs once per (browser, anonymous list): the marker is keyed on both, and a spent anonymous id is rotated after a successful merge — otherwise a shopper who logs out, saves more, and logs back in would have that second list stranded forever. Failures never reject and never write the marker, so the next page view retries.

    EnvieStore.refresh() is new: the client calls it after a merge so an already-rendered page shows the merged list without a reload.

  • fc6d8bd: <envie-list> can render cards with the theme's own Liquid.

    Set card-section="<section handle>" and each card is fetched from the shop through Shopify's Section Rendering API (/products/{handle}?section_id=…), so the wishlist matches the rest of the store with nothing to style. The <template slot="item"> stays supported and is still the right choice when you want full control — the section wins when both are present, and any product the section cannot render falls back to the template rather than blanking the list.

    This exists because the theme app block ships fixed Liquid: a merchant working in the theme editor cannot edit a <template>, so cards had to become something they can configure rather than code.

  • 865f9d8: New DOM events on document (bubbling to window): envie:ready fires once per page when the store's first load completes (detail: { count, config, list }), and envie:merged fires when a guest list is merged into the customer's at login (detail: the merged list).

Patch Changes

  • b8f2e46: Clean public-facing descriptions: curated OpenAPI operation ids and summaries (raw controller ids no longer leak into the SDK or docs), and internal spec references removed from API descriptions and component JSDoc.

  • 8a3ccc9: Fix automatic placement, which injected nothing on a real storefront.

    Five bugs, found by running the managed tier against a live Horizon store:

    • The config was read at module evaluation, before the app embed had written it. This bundle is one module script among several and does not choose its position, so the read now waits for DOMContentLoaded.
    • The MutationObserver batched on requestAnimationFrame, which never fires in a background tab — a middle-clicked product got no buttons until focused, and anything the theme rendered after load was never picked up. Batches on setTimeout now; nothing here paints.
    • once meant "one ever", so when a theme re-rendered the container and took our badge with it, nothing replaced it. It now means one live element.
    • once took the first match in the DOM, which on themes with a hidden mobile drawer put the only badge where nobody could see it. It prefers the first visible match.
    • A rule carrying productId had no way to carry handle, which the API requires — so automatically placed product-page buttons failed on click.

    <envie-button> also stops its click from propagating: product cards wrap themselves in links, and saving must never also navigate.

On this page