firstly 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/esm/core/FF_Filter.d.ts +2 -2
  3. package/esm/core/FF_Filter.js +2 -2
  4. package/esm/core/FF_Validators.d.ts +2 -0
  5. package/esm/core/FF_Validators.js +8 -10
  6. package/esm/core/containsWords.d.ts +2 -2
  7. package/esm/core/containsWords.js +2 -2
  8. package/esm/core/tailwind.d.ts +3 -4
  9. package/esm/core/tailwind.js +3 -4
  10. package/esm/svelte/DemoForm.svelte +121 -0
  11. package/esm/svelte/DemoForm.svelte.d.ts +42 -0
  12. package/esm/svelte/DemoGrid.svelte +258 -0
  13. package/esm/svelte/DemoGrid.svelte.d.ts +49 -0
  14. package/esm/svelte/DialogOpenTest.svelte +10 -0
  15. package/esm/svelte/DialogOpenTest.svelte.d.ts +8 -0
  16. package/esm/svelte/FF_Config.svelte +13 -0
  17. package/esm/svelte/FF_Config.svelte.d.ts +3 -0
  18. package/esm/svelte/FF_Config.svelte.js +38 -0
  19. package/esm/svelte/FF_DialogManager.svelte +251 -0
  20. package/esm/svelte/FF_DialogManager.svelte.d.ts +13 -0
  21. package/esm/svelte/FF_PromptDefault.svelte +85 -0
  22. package/esm/svelte/FF_PromptDefault.svelte.d.ts +9 -0
  23. package/esm/svelte/FF_ToastHtml.svelte +9 -0
  24. package/esm/svelte/FF_ToastHtml.svelte.d.ts +6 -0
  25. package/esm/svelte/FF_ToastManager.svelte +22 -0
  26. package/esm/svelte/FF_ToastManager.svelte.d.ts +4 -0
  27. package/esm/svelte/dialog.svelte.d.ts +209 -0
  28. package/esm/svelte/dialog.svelte.js +243 -0
  29. package/esm/svelte/ff.svelte.d.ts +294 -0
  30. package/esm/svelte/ff.svelte.js +599 -0
  31. package/esm/svelte/index.d.ts +14 -2
  32. package/esm/svelte/index.js +9 -1
  33. package/esm/svelte/infiniteScroll.d.ts +1 -1
  34. package/esm/svelte/infiniteScroll.js +1 -1
  35. package/esm/svelte/toast.d.ts +59 -0
  36. package/esm/svelte/toast.js +92 -0
  37. package/esm/virtual/StateDemoEnum.js +1 -1
  38. package/package.json +4 -3
  39. package/esm/svelte/FF_Repo.svelte.d.ts +0 -191
  40. package/esm/svelte/FF_Repo.svelte.js +0 -312
@@ -0,0 +1,59 @@
1
+ import type { LocalizedMessage } from '../core/FF_Validators.js';
2
+ export type ToastKind = 'success' | 'error' | 'info' | 'warning';
3
+ export type ToastOptions = {
4
+ /**
5
+ * Bold heading shown above the description. Defaults per `kind`
6
+ * (e.g. `error` → "Error"). Pass to override.
7
+ */
8
+ title?: LocalizedMessage;
9
+ /** ms before auto-dismiss (passed through to svelte-sonner). */
10
+ duration?: number;
11
+ /** An action button. */
12
+ action?: {
13
+ label: LocalizedMessage;
14
+ onClick: () => void;
15
+ };
16
+ };
17
+ /** @internal Wired by `<FF_ToastManager>` to bridge `<FF_Config>` titles into this module. */
18
+ export declare function _setToastTitleResolver(fn: (kind: ToastKind) => string): void;
19
+ /** A svelte-sonner toast id. */
20
+ type ToastId = string | number;
21
+ /**
22
+ * firstly's toast - a `LocalizedMessage`-aware wrapper over svelte-sonner. Mount
23
+ * `<FF_ToastManager>` once (it renders sonner's `<Toaster>`).
24
+ *
25
+ * The first argument is the **description** (the body) and **may contain HTML**.
26
+ * A bold **title** sits above it; it defaults per kind (e.g. `error` → "Error") and
27
+ * can be overridden via `opts.title`. The per-kind defaults are localizable through
28
+ * `<FF_Config messages.toast>`. Labels accept a string OR a message function
29
+ * (paraglide/i18next), resolved at call time.
30
+ *
31
+ * ⚠️ **Security:** the description is rendered as **HTML**. Pass only trusted/sanitized
32
+ * content - never raw user input or network/error text, or you risk XSS. Use `toast.fromError`
33
+ * for thrown values: it HTML-escapes the extracted message for you. (The title is always plain text.)
34
+ *
35
+ * ```ts
36
+ * toast.error('Could not save the quote') // title: "Error"
37
+ * toast.success('Saved <b>3</b> rows', { title: 'Done 🎉' })
38
+ * toast.fromError(err) // error toast from any thrown value
39
+ * ```
40
+ */
41
+ export declare const toast: {
42
+ success: (description: LocalizedMessage, opts?: ToastOptions) => ToastId;
43
+ error: (description: LocalizedMessage, opts?: ToastOptions) => ToastId;
44
+ info: (description: LocalizedMessage, opts?: ToastOptions) => ToastId;
45
+ warning: (description: LocalizedMessage, opts?: ToastOptions) => ToastId;
46
+ /** Dispatch on `kind` (default `'info'`). */
47
+ show: (description: LocalizedMessage, opts?: ToastOptions & {
48
+ kind?: ToastKind;
49
+ }) => ToastId;
50
+ /**
51
+ * Pull a message out of any thrown value and show an error toast. The extracted message is
52
+ * HTML-escaped (error text is often server- or user-controlled), so it renders as plain text -
53
+ * unlike `toast.error`, which treats its argument as HTML.
54
+ */
55
+ fromError: (err: unknown, opts?: ToastOptions) => ToastId;
56
+ /** Dismiss a toast by id (or all, if omitted). */
57
+ dismiss: (id?: ToastId) => void;
58
+ };
59
+ export {};
@@ -0,0 +1,92 @@
1
+ import { toast as sonner } from 'svelte-sonner';
2
+ import { resolveMessage } from '../core/FF_Validators.js';
3
+ import FF_ToastHtml from './FF_ToastHtml.svelte';
4
+ /** Per-kind default heading, used when `opts.title` is omitted (and no manager wired one). */
5
+ const DEFAULT_TITLE = {
6
+ success: 'Success',
7
+ error: 'Error',
8
+ info: 'Info',
9
+ warning: 'Warning',
10
+ };
11
+ /**
12
+ * Resolves the per-kind default title. `<FF_ToastManager>` overrides this (client-side) to read
13
+ * your `<FF_Config>` `messages.toast` titles, so defaults become locale-aware. Until then (or with
14
+ * no manager mounted) the English `DEFAULT_TITLE` above is used.
15
+ */
16
+ let resolveDefaultTitle = (kind) => DEFAULT_TITLE[kind];
17
+ /** @internal Wired by `<FF_ToastManager>` to bridge `<FF_Config>` titles into this module. */
18
+ export function _setToastTitleResolver(fn) {
19
+ resolveDefaultTitle = fn;
20
+ }
21
+ function errorMessage(err) {
22
+ if (err instanceof Error)
23
+ return err.message;
24
+ if (typeof err === 'string')
25
+ return err;
26
+ if (err !== null && typeof err === 'object' && 'message' in err)
27
+ return String(err.message);
28
+ return String(err);
29
+ }
30
+ /** Entity-encode a string so it renders as literal text through the HTML description renderer. */
31
+ function escapeHtml(s) {
32
+ return s
33
+ .replace(/&/g, '&amp;')
34
+ .replace(/</g, '&lt;')
35
+ .replace(/>/g, '&gt;')
36
+ .replace(/"/g, '&quot;')
37
+ .replace(/'/g, '&#39;');
38
+ }
39
+ /**
40
+ * Build a toast: the bold `title` (from `opts.title`, else the per-kind default)
41
+ * is svelte-sonner's main message; `description` is the body, rendered as HTML.
42
+ */
43
+ function build(kind, description, opts) {
44
+ const title = opts?.title !== undefined ? resolveMessage(opts.title) : resolveDefaultTitle(kind);
45
+ return sonner[kind](title, {
46
+ // svelte-sonner renders a component description as `<Description {...componentProps} />`
47
+ description: FF_ToastHtml,
48
+ componentProps: { html: resolveMessage(description) },
49
+ duration: opts?.duration,
50
+ action: opts?.action
51
+ ? { label: resolveMessage(opts.action.label), onClick: opts.action.onClick }
52
+ : undefined,
53
+ });
54
+ }
55
+ /**
56
+ * firstly's toast - a `LocalizedMessage`-aware wrapper over svelte-sonner. Mount
57
+ * `<FF_ToastManager>` once (it renders sonner's `<Toaster>`).
58
+ *
59
+ * The first argument is the **description** (the body) and **may contain HTML**.
60
+ * A bold **title** sits above it; it defaults per kind (e.g. `error` → "Error") and
61
+ * can be overridden via `opts.title`. The per-kind defaults are localizable through
62
+ * `<FF_Config messages.toast>`. Labels accept a string OR a message function
63
+ * (paraglide/i18next), resolved at call time.
64
+ *
65
+ * ⚠️ **Security:** the description is rendered as **HTML**. Pass only trusted/sanitized
66
+ * content - never raw user input or network/error text, or you risk XSS. Use `toast.fromError`
67
+ * for thrown values: it HTML-escapes the extracted message for you. (The title is always plain text.)
68
+ *
69
+ * ```ts
70
+ * toast.error('Could not save the quote') // title: "Error"
71
+ * toast.success('Saved <b>3</b> rows', { title: 'Done 🎉' })
72
+ * toast.fromError(err) // error toast from any thrown value
73
+ * ```
74
+ */
75
+ export const toast = {
76
+ success: (description, opts) => build('success', description, opts),
77
+ error: (description, opts) => build('error', description, opts),
78
+ info: (description, opts) => build('info', description, opts),
79
+ warning: (description, opts) => build('warning', description, opts),
80
+ /** Dispatch on `kind` (default `'info'`). */
81
+ show: (description, opts) => build(opts?.kind ?? 'info', description, opts),
82
+ /**
83
+ * Pull a message out of any thrown value and show an error toast. The extracted message is
84
+ * HTML-escaped (error text is often server- or user-controlled), so it renders as plain text -
85
+ * unlike `toast.error`, which treats its argument as HTML.
86
+ */
87
+ fromError: (err, opts) => build('error', escapeHtml(errorMessage(err)), opts),
88
+ /** Dismiss a toast by id (or all, if omitted). */
89
+ dismiss: (id) => {
90
+ sonner.dismiss(id);
91
+ },
92
+ };
@@ -29,7 +29,7 @@ let StateDemoEnum = class StateDemoEnum extends BaseEnum {
29
29
  caption: 'Delete',
30
30
  icon: {
31
31
  data: LibIcon_Delete,
32
- class: 'text-error',
32
+ class: 'text-destructive',
33
33
  },
34
34
  });
35
35
  constructor(id, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firstly",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Firstly, an opinionated Remult setup!",
6
6
  "funding": "https://github.com/sponsors/jycouet",
@@ -38,10 +38,11 @@
38
38
  "cron": "4.4.0",
39
39
  "esm-env": "1.2.2",
40
40
  "nodemailer": "8.0.5",
41
+ "svelte-sonner": "1.1.1",
41
42
  "tailwind-merge": "3.5.0",
42
43
  "tailwindcss": "4.2.2",
43
- "vite-plugin-kit-routes": "1.0.5",
44
- "vite-plugin-stripper": "0.10.3"
44
+ "vite-plugin-kit-routes": "1.0.6",
45
+ "vite-plugin-stripper": "0.10.4"
45
46
  },
46
47
  "sideEffects": false,
47
48
  "exports": {
@@ -1,191 +0,0 @@
1
- import { type ClassType, type EntityFilter, type EntityMetadata, type EntityOrderBy, type GroupByOptions, type GroupByResult, type MembersOnly, type MembersToInclude, type NumericKeys, type QueryOptions, type Repository } from 'remult';
2
- /**
3
- * `ffRepo` - thin reactive wrapper around a Remult `repo`, exposing its results as
4
- * Svelte runes. Pick the mode with a verb:
5
- *
6
- * ffRepo(E).load(() => ({ where })) // load - one-shot list + refresh()
7
- * ffRepo(E).listen(() => ({ where })) // live - liveQuery, auto-updates
8
- * ffRepo(E).paginate(() => ({ where })) // paginate - more() / hasNextPage / aggregates
9
- * ffRepo(E).one(() => ({ where })) // one - reactive single record in `item`
10
- *
11
- * Two surfaces, one rule: anything NOT under `.repo` is reactive (a verb returns a
12
- * runes handle; that handle's writes sync its own state). Anything under `.repo` is
13
- * the plain remult repo - imperative, returns Promises, touches no runes state.
14
- *
15
- * The options getter is reactive: change `where` (e.g. a search box), `orderBy`,
16
- * `enabled`, etc. and the query re-runs - `listen` re-subscribes (the old
17
- * subscription is torn down), `load`/`paginate`/`one` re-fetch and ignore any
18
- * stale in-flight response. `orderBy` defaults to the entity's `defaultOrderBy`.
19
- *
20
- * Getter hygiene: read SvelteKit load `data` through a `$derived`
21
- * (`const did = $derived(data.targetDid)`), never raw inside the getter. A derived
22
- * only propagates on value change, so a parent layout load revalidating - e.g. an
23
- * SP URL write re-running url-dependent loads on every filter - hands you a new
24
- * `data` object but does NOT re-fetch unless the value actually changed; a real
25
- * change (route param switch) still does. Reading `data.x` raw re-fetches on every
26
- * revalidation (a duplicate request per filter).
27
- *
28
- * `enabled: false` skips the query entirely (keeps the last result) and runs it
29
- * the moment it flips true - use it for search-min-length, tab visibility,
30
- * dependent queries, or a manual button trigger.
31
- *
32
- * Mutations on a handle (`insert`/`update`/`save`/`delete`) flip `loading` and keep
33
- * the result in sync - in `live` mode the liveQuery does it; in `load`/`paginate` a
34
- * delete is removed locally and an insert/update triggers a keep-count re-fetch; in
35
- * `one` any write re-fetches the single record. `save()`/`delete()` with no argument
36
- * target the current `item` (pairs with `one`/`create()`). For a raw write that syncs
37
- * nothing, use `.repo` (e.g. `ffRepo(E).repo.insert(...)`).
38
- *
39
- * The factory's return type is mode-specific, so e.g. `.more()` doesn't exist on
40
- * a `listen()` handle. Methods also throw if reached via a cast in the wrong mode.
41
- *
42
- * Reactive vs imperative: the reactive verbs take a *getter* (`() => ({ ... })`) and
43
- * return a runes handle; they build an `$effect`, so they must be created during
44
- * component init. For a one-off read/write in a click handler / async fn (no runes
45
- * context) go through `.repo` (plain remult): `ffRepo(E).repo.findFirst(where)`,
46
- * `ffRepo(E).repo.findId(id)`, `ffRepo(E).repo.find(...)`.
47
- *
48
- * Tip: prefer `.paginate()` whenever you want a total - it returns `aggregates.$count`
49
- * for free in the same request. `load`/`listen`/`one` don't count; for a one-off count
50
- * use `ffRepo(E).repo.count(where)`.
51
- */
52
- /** The aggregate request shape - remult's `GroupByOptions` minus the grouping/paging keys. */
53
- export type AggregateOptions<Entity> = Omit<GroupByOptions<Entity, never, NumericKeys<Entity>[], NumericKeys<Entity>[], (keyof MembersOnly<Entity>)[], (keyof MembersOnly<Entity>)[], (keyof MembersOnly<Entity>)[]>, 'group' | 'orderBy' | 'where' | 'limit' | 'page'>;
54
- /**
55
- * Remult `QueryOptions` plus the `aggregate` request shape. Exported for callers
56
- * that build query options outside `ffRepo` (e.g. a generic table component).
57
- */
58
- export type QueryOptionsHelper<Entity> = QueryOptions<Entity> & {
59
- aggregate?: AggregateOptions<Entity>;
60
- };
61
- export type FF_RepoOptions<Entity> = {
62
- where?: EntityFilter<Entity>;
63
- orderBy?: EntityOrderBy<Entity>;
64
- /** paginate: rows per page (default 25). */
65
- pageSize?: number;
66
- /** find/one/live: cap the rows returned. No default - returns every matching row. */
67
- limit?: number;
68
- include?: MembersToInclude<Entity>;
69
- /** When false, the query is skipped (last result kept) until it flips true. */
70
- enabled?: boolean;
71
- /** Aggregations to compute alongside the page (paginate mode only). `$count` is always returned. */
72
- aggregate?: AggregateOptions<Entity>;
73
- };
74
- type Getter<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> = () => O;
75
- /** `$count` is always present; richer keys appear only for the requested `aggregate`. */
76
- type EmptyAggregateResult = {
77
- $count: number;
78
- };
79
- /** The typed aggregate result for a given options object (paginate mode). */
80
- type ExtractAggregateResult<Entity, O extends FF_RepoOptions<Entity>> = O extends {
81
- aggregate: infer A;
82
- } ? GroupByResult<Entity, never, A extends {
83
- sum?: infer S;
84
- } ? (S extends NumericKeys<Entity>[] ? S : never) : never, A extends {
85
- avg?: infer V;
86
- } ? (V extends NumericKeys<Entity>[] ? V : never) : never, A extends {
87
- min?: infer M;
88
- } ? (M extends NumericKeys<Entity>[] ? M : never) : never, A extends {
89
- max?: infer X;
90
- } ? (X extends NumericKeys<Entity>[] ? X : never) : never, A extends {
91
- distinctCount?: infer D;
92
- } ? D extends (keyof MembersOnly<Entity>)[] ? D : never : never> : EmptyAggregateResult;
93
- export type FF_RepoLoading = {
94
- init: boolean;
95
- fetching: boolean;
96
- more: boolean;
97
- saving: boolean;
98
- deleting: boolean;
99
- };
100
- type Mode = 'live' | 'load' | 'paginate' | 'one';
101
- /**
102
- * The reactive handle implementation. Not exported directly - consumers use a per-mode
103
- * alias (`FF_RepoLoad`/`FF_RepoLive`/`FF_RepoPaginate`/`FF_RepoOne`) or the umbrella
104
- * union `FF_Repo` (any mode). Each verb returns the Omit'd per-mode view of this.
105
- */
106
- declare class FF_RepoHandle<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> {
107
- #private;
108
- items: Entity[];
109
- /** Single-record slot: the loaded row in `one` mode, or a create/edit draft (see `create`). */
110
- item: Entity | undefined;
111
- loading: FF_RepoLoading;
112
- error: string | undefined;
113
- hasNextPage: boolean;
114
- /** Aggregations for the whole query (paginate mode). `aggregates.$count` is the total row count. */
115
- aggregates: ExtractAggregateResult<Entity, O> | undefined;
116
- first: Entity | null;
117
- constructor(r: Repository<Entity>, opts: Getter<Entity, O>, mode: Mode);
118
- /** Re-run the current query (load/paginate/one), back to the first page. */
119
- refresh(): Promise<void>;
120
- /** Load and append the next page (paginate mode). */
121
- more(): Promise<void>;
122
- /**
123
- * Run `fn` once, the first time a row is known (first non-null `first`).
124
- * Never fires while the result is empty - so it's robust to liveQuery emitting
125
- * an empty snapshot before the data lands. Nothing to seed from an empty result.
126
- */
127
- firstOnce(fn: (latest: Entity) => void): void;
128
- /** Reactive, bindable editor state seeded once from the latest row. */
129
- draft<D extends Record<string, unknown>>(seed: (latest: Entity | null) => D): D;
130
- /** Create a new unsaved entity into the `item` slot (for an edit form). */
131
- create(...args: Parameters<Repository<Entity>['create']>): Entity;
132
- insert(...args: Parameters<Repository<Entity>['insert']>): Promise<Entity>;
133
- update(...args: Parameters<Repository<Entity>['update']>): Promise<Entity>;
134
- /** Save. With no argument, saves the current `item` (e.g. a bound `one`/`create` form). */
135
- save(...args: [] | Parameters<Repository<Entity>['save']>): Promise<Entity>;
136
- /** Delete. With no argument, deletes the current `item`. */
137
- delete(...args: [] | Parameters<Repository<Entity>['delete']>): Promise<void>;
138
- deleteMany(...args: Parameters<Repository<Entity>['deleteMany']>): Promise<number>;
139
- /** Insert into `items` at `top` (default) / `bottom` / an index (`-1` = last). +1 to `$count`. */
140
- addItem(item: Entity, options?: {
141
- at?: 'top' | 'bottom' | number;
142
- }): void;
143
- /** Replace the row whose id matches `item`'s id (no `$count` change). */
144
- updateItem(item: Entity): void;
145
- /** Drop the matching row (pass an id or the item). -1 to `$count`. */
146
- removeItem(idOrItem: Parameters<Repository<Entity>['delete']>[0]): void;
147
- /**
148
- * The entity's remult metadata - the single escape hatch for everything not on
149
- * this handle: permissions (`apiInsertAllowed()`, `apiUpdateAllowed(item)`,
150
- * `apiDeleteAllowed(item)`, `apiReadAllowed`), `fields`, `idMetadata`, `options`,
151
- * `key`. Reflects the current `remult.user`.
152
- */
153
- get meta(): EntityMetadata<Entity>;
154
- /** Escape hatch to the underlying repo (count, findId, upsert, projections, ...). */
155
- get repo(): Repository<Entity>;
156
- }
157
- /** load: one-shot list; `refresh()` to re-run. No paging / aggregates. */
158
- export type FF_RepoLoad<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> = Omit<FF_RepoHandle<Entity, O>, 'more' | 'hasNextPage' | 'aggregates'>;
159
- /** live: reactive subscription, auto-updates. No manual refresh / paging / aggregates / list reconcilers (the liveQuery does it). */
160
- export type FF_RepoLive<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> = Omit<FF_RepoHandle<Entity, O>, 'refresh' | 'more' | 'hasNextPage' | 'aggregates' | 'addItem' | 'updateItem' | 'removeItem'>;
161
- /** paginate: `more()` / `hasNextPage` / `aggregates`. No `first`/`firstOnce`/`draft` (paged ≠ latest). */
162
- export type FF_RepoPaginate<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> = Omit<FF_RepoHandle<Entity, O>, 'first' | 'firstOnce' | 'draft'>;
163
- /** one: a single reactive record in `item` (+ `first`). No paging / aggregates / list reconcilers. */
164
- export type FF_RepoOne<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> = Omit<FF_RepoHandle<Entity, O>, 'more' | 'hasNextPage' | 'aggregates' | 'addItem' | 'updateItem' | 'removeItem'>;
165
- /**
166
- * Umbrella handle type - any mode. Use for a component prop that accepts a
167
- * `load`/`listen`/`paginate`/`one` handle (`r: FF_Repo<T>`). It exposes the surface
168
- * common to every mode (`items`/`item`/`loading`/`error`/`meta`/`repo` + the writes);
169
- * mode-specific members (`more`/`hasNextPage`/`aggregates`/`refresh`/`first`/`firstOnce`/
170
- * `draft`/`addItem`/`updateItem`/`removeItem`) require the matching per-mode type.
171
- */
172
- export type FF_Repo<Entity, O extends FF_RepoOptions<Entity> = FF_RepoOptions<Entity>> = FF_RepoLoad<Entity, O> | FF_RepoLive<Entity, O> | FF_RepoPaginate<Entity, O> | FF_RepoOne<Entity, O>;
173
- type StrictGetter<Entity, O extends FF_RepoOptions<Entity>> = () => O & Record<Exclude<keyof O, keyof FF_RepoOptions<Entity>>, never>;
174
- /**
175
- * The builder returned by `ffRepo(E)`. Two surfaces only: the reactive verbs
176
- * (`load`/`listen`/`paginate`/`one`), and `.repo` - the plain remult repo for every
177
- * imperative read/write (`findFirst`, `findId`, `find`, `insert`, `update`, `save`,
178
- * `delete`, `count`, `upsert`, ...). `.meta` is a shortcut to `repo.metadata`.
179
- */
180
- export type FF_RepoBuilder<Entity> = {
181
- load: <O extends FF_RepoOptions<Entity>>(opts: StrictGetter<Entity, O>) => FF_RepoLoad<Entity, O>;
182
- listen: <O extends FF_RepoOptions<Entity>>(opts: StrictGetter<Entity, O>) => FF_RepoLive<Entity, O>;
183
- paginate: <O extends FF_RepoOptions<Entity>>(opts: StrictGetter<Entity, O>) => FF_RepoPaginate<Entity, O>;
184
- one: <O extends FF_RepoOptions<Entity>>(opts: StrictGetter<Entity, O>) => FF_RepoOne<Entity, O>;
185
- /** The entity's remult metadata (permissions, fields, key). Shortcut to `repo.metadata`. */
186
- readonly meta: EntityMetadata<Entity>;
187
- /** The underlying remult repo - every imperative read/write lives here. */
188
- readonly repo: Repository<Entity>;
189
- };
190
- export declare function ffRepo<Entity>(entity: ClassType<Entity>): FF_RepoBuilder<Entity>;
191
- export {};