@microsoft/rayfin-guide 1.35.0-alpha.1374 → 1.35.0-alpha.1412

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.
@@ -0,0 +1,188 @@
1
+ ---
2
+ sidebar_position: 50
3
+ ---
4
+
5
+ # Deep linking and shareable URLs
6
+
7
+ Deep linking lets a user copy the browser URL and send someone the exact view they were looking at.
8
+
9
+ A Fabric data app runs inside a cross-origin iframe and has no addressable URL of its own, so it cannot write to the address bar directly.
10
+ The `@microsoft/rayfin-app-state-fabric` package bridges that gap: your app hands the portal a plain JSON object, and the portal encodes it into the URL it owns.
11
+
12
+ ## Availability
13
+
14
+ Deep linking is rolling out per tenant, so it is not available everywhere yet.
15
+
16
+ Always call `isSupported()` before showing a share button or any affordance that depends on the URL carrying state.
17
+ It resolves to `undefined` when the host does not support deep linking, and your app should continue to work normally in that case.
18
+
19
+ ## How it works
20
+
21
+ 1. A user opens a link whose portal URL carries encoded state.
22
+ 2. The portal seeds that state onto your app's iframe URL before your app loads.
23
+ 3. Your app reads it synchronously on startup, so the shared view renders first and no default view flashes.
24
+ 4. As the user navigates, your app writes state back through the SDK and the portal updates its address bar.
25
+ 5. When the user presses Back or Forward, the portal notifies your app so it can restore the matching view.
26
+
27
+ The portal owns encoding, versioning, size limits, and browser history.
28
+ Your app owns the shape of the state object.
29
+
30
+ ## Install the package
31
+
32
+ ```bash
33
+ npm install @microsoft/rayfin-app-state-fabric
34
+ ```
35
+
36
+ ## Create the client
37
+
38
+ Create one client for the lifetime of your app and share it, rather than constructing one per component.
39
+
40
+ ```typescript
41
+ import { createFabricAppStateClient } from '@microsoft/rayfin-app-state-fabric';
42
+
43
+ const appState = createFabricAppStateClient({
44
+ targetOrigin: 'https://app.fabric.microsoft.com',
45
+ });
46
+ ```
47
+
48
+ Pass `targetOrigin` whenever you know it.
49
+ Without it, messages are posted with `"*"` and inbound events are not origin-checked.
50
+
51
+ ## Read launch state before first render
52
+
53
+ Read the launch state before your first render so a default view never flashes before the shared one.
54
+
55
+ ```typescript
56
+ const launch = appState.getLaunchStateSync();
57
+ renderApp(launch ?? defaultView);
58
+ ```
59
+
60
+ `getLaunchStateSync()` returns the state without awaiting, which is what you want on the first render path.
61
+ It returns `undefined` when the URL carries no state, which is the normal case for a fresh navigation.
62
+
63
+ Use `await appState.getLaunchState()` when your startup code can be asynchronous.
64
+ It resolves from the seeded URL on hosts that support it, so awaiting it does not delay first paint.
65
+
66
+ Treat launch state as untrusted input.
67
+ Anyone can edit a link before sharing it, so validate it exactly as you would a query parameter before using it to drive queries.
68
+
69
+ ## Write state as the user navigates
70
+
71
+ Choose between the two writers by asking who caused the change.
72
+
73
+ Use `setState()` when the *user* caused it, such as a click, a filter change, or opening a record.
74
+ It creates a history entry, so Back returns the user to where they were.
75
+
76
+ ```typescript
77
+ await appState.setState({ view: 'sales', region: 'AT' });
78
+ ```
79
+
80
+ Use `replaceState()` when the *app* caused it, or for high-frequency updates such as a slider drag where one history entry per update would make Back unusable.
81
+
82
+ ```typescript
83
+ await appState.replaceState({ view: 'sales', threshold: value });
84
+ ```
85
+
86
+ The whole object is replaced on every write.
87
+ There is no partial or namespaced update, so merge your state before writing it.
88
+
89
+ ## React to Back and Forward
90
+
91
+ Subscribe to observe changes your app did not initiate, such as the browser Back and Forward buttons or a deep link opened in the current tab.
92
+
93
+ ```typescript
94
+ const unsubscribe = appState.onStateChange((state) => {
95
+ restore(state ?? defaultView);
96
+ });
97
+
98
+ // On teardown
99
+ unsubscribe();
100
+ appState.dispose();
101
+ ```
102
+
103
+ The listener receives `undefined` when navigation reaches a URL that carries no state, which means your app should restore its own defaults.
104
+
105
+ ## Check support before showing a share button
106
+
107
+ ```typescript
108
+ const capabilities = await appState.isSupported();
109
+
110
+ if (capabilities) {
111
+ showShareButton();
112
+
113
+ // Some hosts can update the URL but cannot add history entries.
114
+ if (!capabilities.canPush) {
115
+ hideBackForwardHints();
116
+ }
117
+ }
118
+ ```
119
+
120
+ The host is authoritative.
121
+ It reports its own `maxEncodedBytes`, `maxDepth`, and `canPush`, and those values are the real contract.
122
+
123
+ ## Running standalone
124
+
125
+ Many apps ship standalone as well as embedded.
126
+ Standalone, your app owns its own address bar and there is no host to talk to, so deep-link state is unavailable by design.
127
+
128
+ The client is safe to construct either way.
129
+ It never throws at construction and never rewrites a URL it does not own.
130
+
131
+ Use the same `isSupported()` check to select your standalone path:
132
+
133
+ ```typescript
134
+ const capabilities = await appState.isSupported();
135
+
136
+ if (capabilities) {
137
+ await appState.setState({ view: 'sales', region: 'AT' });
138
+ } else {
139
+ router.push({ path: '/sales', query: { region: 'AT' } });
140
+ }
141
+ ```
142
+
143
+ Standalone, reads return `undefined` and writes reject with `NO_HOST_WINDOW`.
144
+ Reads degrade quietly so startup needs no branching, while writes fail loudly so a missing branch shows up during development.
145
+
146
+ ## Limits and rules
147
+
148
+ - State must be a plain JSON object. `Date`, `Map`, `Set`, class instances, functions, and `undefined` are rejected rather than silently degraded.
149
+ - State is capped at 4 KiB encoded and 20 levels deep, so shared links survive corporate proxies, mail gateways, and chat clients.
150
+ - For anything larger, store the data yourself and put an identifier in the state.
151
+ - The encoding is versioned but your payload is not, so a link shared before a shape change still arrives in the old shape and your app must tolerate it.
152
+
153
+ ## Security
154
+
155
+ State travels in a URL, so treat it accordingly.
156
+
157
+ **It is visible to the user.**
158
+ It appears in the address bar, browser history, bookmarks, screenshots, copied links, and corporate proxy logs.
159
+ Never put secrets, access tokens, or personal data in it.
160
+ When the state is sensitive, use an opaque identifier that maps to server-side data.
161
+
162
+ **It is untrusted input.**
163
+ Validate launch state before using it, and tolerate state written by a different version of your own app.
164
+
165
+ ## Troubleshooting
166
+
167
+ **`isSupported()` resolves to `undefined`.**
168
+ Either the app is not embedded in the Fabric portal, or deep linking has not reached this tenant yet.
169
+ Both are expected, and your app should fall back to its own routing.
170
+
171
+ **Writes reject with `STATE_TOO_LARGE`.**
172
+ The state exceeds the encoded budget the host reported.
173
+ Move the bulk of the data server-side and keep an identifier in the state.
174
+
175
+ **Writes reject with `INVALID_STATE`.**
176
+ The state contains something JSON cannot round-trip, such as a `Date`, a class instance, `undefined`, or a circular reference.
177
+ The error message names the offending path.
178
+
179
+ **Writes reject with `NO_HOST_WINDOW`.**
180
+ The app is not running embedded, so there is no portal to write to.
181
+
182
+ Errors are `FabricAppStateError` values with a stable `code`.
183
+ Branch on the code, never on the message.
184
+
185
+ ## Next steps
186
+
187
+ - [Fabric Brokered Auth](../auth/fabric.md) — How Fabric SSO authentication works.
188
+ - [Deploy to Microsoft Fabric](./deploy.md) — Detailed deployment commands and troubleshooting.
@@ -266,7 +266,7 @@ services:
266
266
 
267
267
  ### Static deploy exceeds size limit
268
268
 
269
- The compressed archive must not exceed 100 MB. Optimize your build output by excluding source maps and large development assets, or move binary files to Rayfin storage.
269
+ The compressed archive must not exceed 100 MB. Optimize your build output by excluding source maps and large development assets.
270
270
 
271
271
  ### GraphQL returns "Internal server error" after deploy
272
272
 
@@ -67,7 +67,6 @@ The endpoint exposes paths for each service:
67
67
  | --- | --- |
68
68
  | `/api/graphql` | Data API (GraphQL) — used by `RayfinClient` for CRUD operations |
69
69
  | `/auth` | Authentication service |
70
- | `/storage` | File storage |
71
70
 
72
71
  Your frontend application uses this endpoint via the `VITE_RAYFIN_API_URL` environment variable, which is generated into `.env.local` from `rayfin/.env` after deployment.
73
72
 
@@ -123,4 +122,5 @@ Learn more about [Workspace roles](https://learn.microsoft.com/fabric/fundamenta
123
122
  - [Pricing and capacity usage](./pricing.md) — Understand what consumes Fabric capacity and how billing works.
124
123
  - [Create a Fabric data app](../getting-started/create-rayfin-item.md) — Step-by-step guide to creating your first item in the Fabric portal.
125
124
  - [Deploy to Microsoft Fabric](./deploy.md) — Detailed deployment commands and troubleshooting.
125
+ - [Deep linking and shareable URLs](./deep-linking.md) — Let users share a link that reopens the exact view they were looking at.
126
126
  - [Fabric Brokered Auth](../auth/fabric.md) — How Fabric SSO authentication works.
@@ -145,10 +145,6 @@ services:
145
145
  enabled: ${AUTH_ENABLED:-false}
146
146
  issuer: ${AUTH_ISSUER}
147
147
  audience: ${AUTH_AUDIENCE:-https://api.example.com}
148
-
149
- storage:
150
- enabled: ${STORAGE_ENABLED:-true}
151
- accountName: ${STORAGE_ACCOUNT:-devstoreaccount1}
152
148
  ```
153
149
 
154
150
  ```bash
@@ -159,7 +155,6 @@ DB_PORT=5432
159
155
  DB_NAME=todos_prod
160
156
  AUTH_ENABLED=true
161
157
  AUTH_ISSUER=https://auth.example.com
162
- STORAGE_ACCOUNT=prodstorageaccount
163
158
  ```
164
159
 
165
160
  ## Security Best Practices
@@ -171,7 +171,6 @@ Read by the Rayfin WebService container via the ASP.NET Core configuration syste
171
171
  | --- | --- | --- |
172
172
  | `Auth__Enabled` | `services.auth.enabled` | `true` / `false` |
173
173
  | `Data__Enabled` | `services.data.enabled` | `true` / `false` |
174
- | `Storage__Enabled` | `services.storage.enabled` | `true` / `false` |
175
174
 
176
175
  The following signing-key variables are set to dev-mode defaults by `rayfin up` and are not typically edited:
177
176
 
@@ -190,7 +189,7 @@ These variables are read from the shell environment and are never written to fil
190
189
  | `RAYFIN_WORKSPACE_ID` | Fabric workspace ID for non-interactive setup. Used with `RAYFIN_TOKEN`. |
191
190
  | `RAYFIN_TENANT_ID` | Entra ID tenant used by `rayfin up` for portal URLs and the `ctid` query parameter. Equivalent to the `-t, --tenant <id>` flag (precedence: flag > env var > signed-in tenant). |
192
191
  | `RAYFIN_ENCRYPTION_FALLBACK_ENABLED` | Set to `true` to allow plaintext token cache on systems without OS credential storage. Development only. |
193
- | `RAYFIN_FEATURE_FLAGS` | Comma-separated list of experimental feature names to enable (case-insensitive). Recognized values include `docker-local-dev`, `storage`, `functions`, and `postgresql`. |
192
+ | `RAYFIN_FEATURE_FLAGS` | Comma-separated list of experimental feature names to enable (case-insensitive). Recognized values include `docker-local-dev`, `functions`, and `postgresql`. |
194
193
  | `RAYFIN_WEBSERVICE_IMAGE_NAME` | **Experimental.** Override the webservice container image used by `rayfin dev --provider docker` and Docker Compose. Defaults to `ghcr.io/microsoft/project-rayfin/webservice:cli-<version>`. |
195
194
  | `RAYFIN_APPINSIGHTS_CONNECTION_STRING` | Override the telemetry endpoint for the CLI and VS Code extension. |
196
195
 
@@ -199,7 +198,6 @@ These variables are read from the shell environment and are never written to fil
199
198
  | Flag | Effect |
200
199
  | --- | --- |
201
200
  | `docker-local-dev` | Allows `rayfin dev --provider docker` and the Docker maintenance commands. Bare `rayfin dev` remains available without this flag and defaults to Fabric. |
202
- | `storage` | Exposes storage commands (`rayfin dev storage *`) and storage prompts during `rayfin init`. |
203
201
  | `functions` | Exposes Functions service prompts during `rayfin init`. |
204
202
  | `postgresql` | Adds PostgreSQL as a selectable dialect during `rayfin init` and `rayfin init` with bundled templates. |
205
203
 
@@ -53,6 +53,10 @@ If you omit it, the server generates a UUID automatically.
53
53
  - You may supply your own UUID at creation time if you prefer client-generated identifiers.
54
54
  - Composite or non-`id` primary keys are not supported.
55
55
 
56
+ **Scope:** the rules above apply to entities you own under `rayfin/data/`.
57
+ Connector entities under `rayfin/connectors/` model an existing, external Fabric SQL source and follow different rules — the primary key is whatever the source actually declares, of any name and any datatype, including composite keys or no key at all.
58
+ See the connector skill's [Entity Generation Contract](pathname://../../../../tools/cli/assets/agent-files/skills/rayfin-connectors/SKILL.md#entity-generation-contract) section for the connector-path key rules.
59
+
56
60
  ```typescript
57
61
  @entity()
58
62
  export class Todo {
@@ -145,23 +145,6 @@ export class SecureDocument {
145
145
  }
146
146
  ```
147
147
 
148
- ## Storage permissions
149
-
150
- The same `@role` decorator works with storage entities.
151
- When applied to a `@blob()` class, Rayfin generates a storage policy instead of a database policy:
152
-
153
- ```typescript
154
- import { blob, role } from '@microsoft/rayfin-core';
155
-
156
- @blob()
157
- @role('authenticated', '*', {
158
- policy: (claims, item) => claims.sub.eq(item.owner_id),
159
- })
160
- export class ProfileImage {
161
- owner_id!: string;
162
- }
163
- ```
164
-
165
148
  ## How it works
166
149
 
167
150
  - The `@role` decorator collects permission metadata at class definition time.
@@ -78,8 +78,6 @@ services:
78
78
  data:
79
79
  enabled: true
80
80
  dialect: mssql
81
- storage:
82
- enabled: false
83
81
  staticHosting:
84
82
  enabled: true
85
83
  root: .
@@ -160,12 +158,6 @@ Configure an email provider for magic links, password resets, and email verifica
160
158
  | `useStartTls` | `boolean` | `false` | Use STARTTLS for the SMTP connection. |
161
159
  | `webPort` | `number` | `1080` | MailDev web UI port (local development only). |
162
160
 
163
- #### `services.storage`
164
-
165
- | Field | Type | Default | Description |
166
- | --- | --- | --- | --- |
167
- | `enabled` | `boolean` | `false` | Enable the storage service. |
168
-
169
161
  #### `services.staticHosting`
170
162
 
171
163
  | Field | Type | Default | Description |
@@ -133,7 +133,7 @@ The deploy tool updates the configuration and pushes it to the backend during de
133
133
 
134
134
  - The compressed ZIP archive must not exceed **100 MB**.
135
135
  - The CLI uses maximum compression to minimize upload size.
136
- - If your build output exceeds the limit, consider excluding large assets or using the storage service for binary files.
136
+ - If your build output exceeds the limit, consider excluding large assets from the deployed bundle.
137
137
 
138
138
  ## Complete example
139
139
 
@@ -182,7 +182,6 @@ If the ZIP exceeds 100 MB:
182
182
 
183
183
  - Review your build output for unnecessary files (source maps, unoptimized images).
184
184
  - Configure your bundler to exclude development artifacts from the production build.
185
- - Move large binary assets to Rayfin storage instead of bundling them as static content.
186
185
 
187
186
  ### No remote endpoint configured
188
187
 
@@ -24,9 +24,9 @@ This command:
24
24
  - Validates that Docker and Docker Compose are available.
25
25
  - Generates `rayfin/.temp/docker-compose.yml` from your project configuration.
26
26
  - Allocates ports for each service.
27
- - Starts containers for enabled services (WebService, database, and optional storage).
27
+ - Starts containers for enabled services (WebService and database).
28
28
  - Runs health checks and waits for all services to be healthy.
29
- - Applies the project's declared data and storage configuration to the local backend.
29
+ - Applies the project's declared data configuration to the local backend.
30
30
  - Starts the frontend with `npm run dev:frontend` when that script exists, falling back to `npm run dev` for existing projects.
31
31
  - Resolves that script from `services.staticHosting.path` when the frontend lives in a nested package, otherwise from the project root.
32
32
  - Builds and starts the configured local Functions host when `services.functions.enabled` is `true`.
@@ -100,14 +100,6 @@ npx rayfin dev db apply --force
100
100
  Run this after making changes to entities in `rayfin/data/`.
101
101
  Use `--force` to regenerate configuration even if no changes are detected.
102
102
 
103
- ### `rayfin dev storage apply`
104
-
105
- Generate and apply storage configuration to the local development server.
106
-
107
- ```bash
108
- npx rayfin dev storage apply
109
- ```
110
-
111
103
  ### `rayfin dev status`
112
104
 
113
105
  Display the status of the local development environment.
@@ -120,7 +112,7 @@ Shows container health, port assignments, and service readiness.
120
112
 
121
113
  ### `rayfin dev watch`
122
114
 
123
- Watch `./rayfin/data` or `./rayfin/storage` and auto-apply configuration changes.
115
+ Watch `./rayfin/data` and auto-apply configuration changes.
124
116
 
125
117
  ```bash
126
118
  npx rayfin dev watch
@@ -153,11 +145,5 @@ Set the feature flag in your environment:
153
145
  export RAYFIN_FEATURE_FLAGS=docker-local-dev
154
146
  ```
155
147
 
156
- Or combine with other flags:
157
-
158
- ```bash
159
- export RAYFIN_FEATURE_FLAGS=docker-local-dev,storage
160
- ```
161
-
162
148
  This feature flag gates only Docker provider selection and Docker maintenance commands.
163
149
  Bare `rayfin dev` is available without preview flags and uses Fabric.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/rayfin-guide",
3
- "version": "1.35.0-alpha.1374",
3
+ "version": "1.35.0-alpha.1412",
4
4
  "description": "Cross-cutting Builder guides for the Rayfin platform — discovered by `@microsoft/rayfin-docs` via the `rayfinDocs` package.json field convention.",
5
5
  "type": "module",
6
6
  "files": [