@aglyn/tenant-feature-instance 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +3 -0
- package/jest.config.d.ts +36 -0
- package/package.json +45 -0
- package/src/index.d.ts +17 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/duplicate-resource-dialog.d.ts +77 -0
- package/src/lib/components/duplicate-resource-dialog.js +217 -0
- package/src/lib/components/duplicate-resource-dialog.js.map +1 -0
- package/src/lib/constants/firebase-config.d.ts +68 -0
- package/src/lib/constants/firebase-config.js +155 -0
- package/src/lib/constants/firebase-config.js.map +1 -0
- package/src/lib/constants/firebase-emulator-hosts.d.ts +60 -0
- package/src/lib/constants/firebase-emulator-hosts.js +81 -0
- package/src/lib/constants/firebase-emulator-hosts.js.map +1 -0
- package/src/lib/constants/mock-data.d.ts +24 -0
- package/src/lib/constants/mock-data.js +49 -0
- package/src/lib/constants/mock-data.js.map +1 -0
- package/src/lib/constants/site-paths.d.ts +47 -0
- package/src/lib/constants/site-paths.js +66 -0
- package/src/lib/constants/site-paths.js.map +1 -0
- package/src/lib/hooks/firebase/auth-persistence.d.ts +74 -0
- package/src/lib/hooks/firebase/auth-persistence.js +117 -0
- package/src/lib/hooks/firebase/auth-persistence.js.map +1 -0
- package/src/lib/hooks/firebase/firebase-services.d.ts +172 -0
- package/src/lib/hooks/firebase/firebase-services.js +522 -0
- package/src/lib/hooks/firebase/firebase-services.js.map +1 -0
- package/src/lib/hooks/firebase/firestore-cache.d.ts +125 -0
- package/src/lib/hooks/firebase/firestore-cache.js +152 -0
- package/src/lib/hooks/firebase/firestore-cache.js.map +1 -0
- package/src/lib/hooks/firebase/firestore-shared-client-state.d.ts +169 -0
- package/src/lib/hooks/firebase/firestore-shared-client-state.js +316 -0
- package/src/lib/hooks/firebase/firestore-shared-client-state.js.map +1 -0
- package/src/lib/hooks/firestore-denial-reporter.d.ts +192 -0
- package/src/lib/hooks/firestore-denial-reporter.js +293 -0
- package/src/lib/hooks/firestore-denial-reporter.js.map +1 -0
- package/src/lib/hooks/helpers/besigner-nodes-converter.d.ts +62 -0
- package/src/lib/hooks/helpers/besigner-nodes-converter.js +91 -0
- package/src/lib/hooks/helpers/besigner-nodes-converter.js.map +1 -0
- package/src/lib/hooks/helpers/guarded-nodes-save.d.ts +80 -0
- package/src/lib/hooks/helpers/guarded-nodes-save.js +89 -0
- package/src/lib/hooks/helpers/guarded-nodes-save.js.map +1 -0
- package/src/lib/hooks/helpers/guarded-seed-write.d.ts +125 -0
- package/src/lib/hooks/helpers/guarded-seed-write.js +193 -0
- package/src/lib/hooks/helpers/guarded-seed-write.js.map +1 -0
- package/src/lib/hooks/helpers/listen-options.d.ts +91 -0
- package/src/lib/hooks/helpers/listen-options.js +92 -0
- package/src/lib/hooks/helpers/listen-options.js.map +1 -0
- package/src/lib/hooks/helpers/use-doc.d.ts +38 -0
- package/src/lib/hooks/helpers/use-doc.js +213 -0
- package/src/lib/hooks/helpers/use-doc.js.map +1 -0
- package/src/lib/hooks/helpers/use-modify-doc-callback.d.ts +28 -0
- package/src/lib/hooks/helpers/use-modify-doc-callback.js +110 -0
- package/src/lib/hooks/helpers/use-modify-doc-callback.js.map +1 -0
- package/src/lib/hooks/host-collection-queries.d.ts +125 -0
- package/src/lib/hooks/host-collection-queries.js +126 -0
- package/src/lib/hooks/host-collection-queries.js.map +1 -0
- package/src/lib/hooks/list-filter-constraints.d.ts +64 -0
- package/src/lib/hooks/list-filter-constraints.js +288 -0
- package/src/lib/hooks/list-filter-constraints.js.map +1 -0
- package/src/lib/hooks/sorted-collection-window.d.ts +207 -0
- package/src/lib/hooks/sorted-collection-window.js +150 -0
- package/src/lib/hooks/sorted-collection-window.js.map +1 -0
- package/src/lib/hooks/use-component-version.d.ts +43 -0
- package/src/lib/hooks/use-component-version.js +73 -0
- package/src/lib/hooks/use-component-version.js.map +1 -0
- package/src/lib/hooks/use-component.d.ts +39 -0
- package/src/lib/hooks/use-component.js +55 -0
- package/src/lib/hooks/use-component.js.map +1 -0
- package/src/lib/hooks/use-console-host-route.d.ts +24 -0
- package/src/lib/hooks/use-console-host-route.js +77 -0
- package/src/lib/hooks/use-console-host-route.js.map +1 -0
- package/src/lib/hooks/use-duplicate-resource-api.d.ts +43 -0
- package/src/lib/hooks/use-duplicate-resource-api.js +61 -0
- package/src/lib/hooks/use-duplicate-resource-api.js.map +1 -0
- package/src/lib/hooks/use-firestore-collection.d.ts +62 -0
- package/src/lib/hooks/use-firestore-collection.js +307 -0
- package/src/lib/hooks/use-firestore-collection.js.map +1 -0
- package/src/lib/hooks/use-firestore-doc.d.ts +42 -0
- package/src/lib/hooks/use-firestore-doc.js +192 -0
- package/src/lib/hooks/use-firestore-doc.js.map +1 -0
- package/src/lib/hooks/use-form-version.d.ts +44 -0
- package/src/lib/hooks/use-form-version.js +73 -0
- package/src/lib/hooks/use-form-version.js.map +1 -0
- package/src/lib/hooks/use-host-activity-logger.d.ts +35 -0
- package/src/lib/hooks/use-host-activity-logger.js +96 -0
- package/src/lib/hooks/use-host-activity-logger.js.map +1 -0
- package/src/lib/hooks/use-host-campaigns.d.ts +53 -0
- package/src/lib/hooks/use-host-campaigns.js +93 -0
- package/src/lib/hooks/use-host-campaigns.js.map +1 -0
- package/src/lib/hooks/use-host-org-id.d.ts +96 -0
- package/src/lib/hooks/use-host-org-id.js +154 -0
- package/src/lib/hooks/use-host-org-id.js.map +1 -0
- package/src/lib/hooks/use-host-resource-api.d.ts +43 -0
- package/src/lib/hooks/use-host-resource-api.js +60 -0
- package/src/lib/hooks/use-host-resource-api.js.map +1 -0
- package/src/lib/hooks/use-host-template.d.ts +44 -0
- package/src/lib/hooks/use-host-template.js +82 -0
- package/src/lib/hooks/use-host-template.js.map +1 -0
- package/src/lib/hooks/use-host-version-api.d.ts +59 -0
- package/src/lib/hooks/use-host-version-api.js +69 -0
- package/src/lib/hooks/use-host-version-api.js.map +1 -0
- package/src/lib/hooks/use-host.d.ts +101 -0
- package/src/lib/hooks/use-host.js +45 -0
- package/src/lib/hooks/use-host.js.map +1 -0
- package/src/lib/hooks/use-layout-version.d.ts +33 -0
- package/src/lib/hooks/use-layout-version.js +64 -0
- package/src/lib/hooks/use-layout-version.js.map +1 -0
- package/src/lib/hooks/use-layout.d.ts +41 -0
- package/src/lib/hooks/use-layout.js +49 -0
- package/src/lib/hooks/use-layout.js.map +1 -0
- package/src/lib/hooks/use-live-artifact-count.d.ts +50 -0
- package/src/lib/hooks/use-live-artifact-count.js +102 -0
- package/src/lib/hooks/use-live-artifact-count.js.map +1 -0
- package/src/lib/hooks/use-org-member-options.d.ts +57 -0
- package/src/lib/hooks/use-org-member-options.js +112 -0
- package/src/lib/hooks/use-org-member-options.js.map +1 -0
- package/src/lib/hooks/use-org-plan.d.ts +46 -0
- package/src/lib/hooks/use-org-plan.js +86 -0
- package/src/lib/hooks/use-org-plan.js.map +1 -0
- package/src/lib/hooks/use-paged-collection.d.ts +95 -0
- package/src/lib/hooks/use-paged-collection.js +130 -0
- package/src/lib/hooks/use-paged-collection.js.map +1 -0
- package/src/lib/hooks/use-plugin-config.d.ts +38 -0
- package/src/lib/hooks/use-plugin-config.js +120 -0
- package/src/lib/hooks/use-plugin-config.js.map +1 -0
- package/src/lib/hooks/use-scope-tokens.d.ts +28 -0
- package/src/lib/hooks/use-scope-tokens.js +100 -0
- package/src/lib/hooks/use-scope-tokens.js.map +1 -0
- package/src/lib/hooks/use-screen-version.d.ts +33 -0
- package/src/lib/hooks/use-screen-version.js +65 -0
- package/src/lib/hooks/use-screen-version.js.map +1 -0
- package/src/lib/hooks/use-screen.d.ts +68 -0
- package/src/lib/hooks/use-screen.js +50 -0
- package/src/lib/hooks/use-screen.js.map +1 -0
- package/src/lib/hooks/use-sending-identity-api.d.ts +197 -0
- package/src/lib/hooks/use-sending-identity-api.js +95 -0
- package/src/lib/hooks/use-sending-identity-api.js.map +1 -0
- package/src/lib/hooks/use-sorted-paged-collection.d.ts +48 -0
- package/src/lib/hooks/use-sorted-paged-collection.js +213 -0
- package/src/lib/hooks/use-sorted-paged-collection.js.map +1 -0
- package/src/lib/hooks/use-switcher-collection.d.ts +105 -0
- package/src/lib/hooks/use-switcher-collection.js +275 -0
- package/src/lib/hooks/use-switcher-collection.js.map +1 -0
- package/src/lib/hooks/use-user-name.d.ts +51 -0
- package/src/lib/hooks/use-user-name.js +90 -0
- package/src/lib/hooks/use-user-name.js.map +1 -0
- package/src/lib/hooks/use-user-photo.d.ts +50 -0
- package/src/lib/hooks/use-user-photo.js +87 -0
- package/src/lib/hooks/use-user-photo.js.map +1 -0
- package/src/lib/tenant.d.ts +61 -0
- package/src/lib/tenant.js +62 -0
- package/src/lib/tenant.js.map +1 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
18
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
19
|
+
import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
20
|
+
import { useFirestoreCollection } from "./use-firestore-collection.js";
|
|
21
|
+
/**
|
|
22
|
+
* A paged window over a live collection, instead of a big read sliced small.
|
|
23
|
+
*
|
|
24
|
+
* ## The shape this replaces
|
|
25
|
+
*
|
|
26
|
+
* A card would listen with `limit(300)` and render `rows.slice(0, 50)`. That
|
|
27
|
+
* is wrong twice over. Two hundred and fifty documents are read, billed and
|
|
28
|
+
* discarded on every mount — and the rows past the slice are not merely
|
|
29
|
+
* wasted, they are UNREACHABLE, because nothing renders them and no control
|
|
30
|
+
* asks for more. A merchant with sixty gift cards sees fifty and no
|
|
31
|
+
* indication that ten more exist; past three hundred, a card issued this
|
|
32
|
+
* morning cannot be reached at all.
|
|
33
|
+
*
|
|
34
|
+
* Here the window IS the query, and it is expressed in PAGES so the same
|
|
35
|
+
* `ListPagination` footer serves this and a cursor feed. The listener is
|
|
36
|
+
* widened to cover every page up to the one being read, so paging BACK costs
|
|
37
|
+
* nothing — the rows are already in the snapshot — and paging forward costs
|
|
38
|
+
* one page more.
|
|
39
|
+
*
|
|
40
|
+
* ## Why the window covers page 0..n rather than page n alone
|
|
41
|
+
*
|
|
42
|
+
* This is a live `onSnapshot`, not a fetch. Firestore cursors need a document
|
|
43
|
+
* snapshot to resume from, which a listener that has never read page n - 1
|
|
44
|
+
* does not have. Widening the limit is what makes forward and backward
|
|
45
|
+
* symmetrical without a second read path, and the cost is bounded by how deep
|
|
46
|
+
* the reader actually goes — with the shared default of ten, page three is
|
|
47
|
+
* thirty-one documents, and most readers never leave page one.
|
|
48
|
+
*
|
|
49
|
+
* ## Why over-fetch by one
|
|
50
|
+
*
|
|
51
|
+
* `hasMore` has to be a fact. Comparing `length === limit` is wrong exactly
|
|
52
|
+
* when the count is an even multiple of the page size — the case that offers
|
|
53
|
+
* a "next page" leading nowhere, or hides one that leads somewhere. The probe
|
|
54
|
+
* row answers it outright and is never rendered.
|
|
55
|
+
*
|
|
56
|
+
* ## Why the window resets on `deps`
|
|
57
|
+
*
|
|
58
|
+
* `deps` identify the SUBJECT being listened to. Without the reset, a card
|
|
59
|
+
* paged three deep on one site opens the next site three pages in — a read
|
|
60
|
+
* the reader never asked for, charged to whoever they switched to.
|
|
61
|
+
*
|
|
62
|
+
* `buildQuery` receives the limit to apply, so the caller keeps ownership of
|
|
63
|
+
* ordering and predicates:
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* usePagedCollection<Card>(
|
|
67
|
+
* (pageLimit) =>
|
|
68
|
+
* hostId
|
|
69
|
+
* ? query(collection(firestore, 'hosts', hostId, 'giftCards'),
|
|
70
|
+
* orderBy('createdAt', 'desc'), limit(pageLimit))
|
|
71
|
+
* : null,
|
|
72
|
+
* [firestore, hostId],
|
|
73
|
+
* { idField: '$id' },
|
|
74
|
+
* )
|
|
75
|
+
* ```
|
|
76
|
+
*/ export function usePagedCollection(buildQuery, deps, options = {}) {
|
|
77
|
+
var _ref;
|
|
78
|
+
var _result_data;
|
|
79
|
+
const { pageSize: initialPageSize = TABLE_PAGE_SIZE_DEFAULT } = options, collectionOptions = _object_without_properties_loose(options, [
|
|
80
|
+
"pageSize"
|
|
81
|
+
]);
|
|
82
|
+
const [pageSize, setPageSizeState] = useState(initialPageSize);
|
|
83
|
+
const [page, setPage] = useState(0);
|
|
84
|
+
const buildQueryRef = useRef(buildQuery);
|
|
85
|
+
buildQueryRef.current = buildQuery;
|
|
86
|
+
/*
|
|
87
|
+
* A new subject starts at page one. Spreading `deps` is the same contract
|
|
88
|
+
* `useFirestoreCollection` documents — pass the primitive ids, not the
|
|
89
|
+
* Query.
|
|
90
|
+
*/ useEffect(()=>{
|
|
91
|
+
setPage(0);
|
|
92
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
93
|
+
}, [
|
|
94
|
+
...deps
|
|
95
|
+
]);
|
|
96
|
+
// Everything up to and including the page being read, plus the probe row.
|
|
97
|
+
const windowSize = pageSize * (page + 1);
|
|
98
|
+
const result = useFirestoreCollection(()=>buildQueryRef.current(windowSize + 1), // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
99
|
+
[
|
|
100
|
+
...deps,
|
|
101
|
+
windowSize
|
|
102
|
+
], collectionOptions);
|
|
103
|
+
const hasMore = ((_ref = (_result_data = result.data) == null ? void 0 : _result_data.length) != null ? _ref : 0) > windowSize;
|
|
104
|
+
const rows = useMemo(()=>{
|
|
105
|
+
var _result_data;
|
|
106
|
+
return ((_result_data = result.data) != null ? _result_data : []).slice(page * pageSize, windowSize);
|
|
107
|
+
}, [
|
|
108
|
+
result.data,
|
|
109
|
+
page,
|
|
110
|
+
pageSize,
|
|
111
|
+
windowSize
|
|
112
|
+
]);
|
|
113
|
+
const setPageSize = useCallback((next)=>{
|
|
114
|
+
setPageSizeState(next);
|
|
115
|
+
// Page four of a ten-row list does not exist once the reader asks for
|
|
116
|
+
// fifty at a time, and an out-of-range page renders as an empty list
|
|
117
|
+
// with no explanation — which reads as the data having gone.
|
|
118
|
+
setPage(0);
|
|
119
|
+
}, []);
|
|
120
|
+
return _extends({}, result, {
|
|
121
|
+
rows,
|
|
122
|
+
hasMore,
|
|
123
|
+
page,
|
|
124
|
+
setPage,
|
|
125
|
+
pageSize,
|
|
126
|
+
setPageSize
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=use-paged-collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/feature/instance/src/lib/hooks/use-paged-collection.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { DocumentData, Query } from 'firebase/firestore'\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type DependencyList,\n} from 'react'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport {\n useFirestoreCollection,\n type UseFirestoreCollectionOptions,\n type UseFirestoreCollectionResult,\n} from './use-firestore-collection'\n\nexport interface UsePagedCollectionOptions\n extends UseFirestoreCollectionOptions {\n /**\n * Rows per page. Defaults to the console-wide smallest option — a list\n * that picks its own number is how five different ones appear, and on a\n * query bounded by its page size the smallest page is the smallest bill.\n */\n pageSize?: number\n}\n\nexport interface UsePagedCollectionResult<T>\n extends UseFirestoreCollectionResult<T> {\n /** The current page's rows. `data` holds the probe row; this never does. */\n rows: T[]\n /** A further page exists. A FACT from the probe row, not a guess. */\n hasMore: boolean\n /** Zero-based, to match `ListPagination` and MUI. */\n page: number\n setPage: (page: number) => void\n pageSize: number\n setPageSize: (pageSize: number) => void\n}\n\n/**\n * A paged window over a live collection, instead of a big read sliced small.\n *\n * ## The shape this replaces\n *\n * A card would listen with `limit(300)` and render `rows.slice(0, 50)`. That\n * is wrong twice over. Two hundred and fifty documents are read, billed and\n * discarded on every mount — and the rows past the slice are not merely\n * wasted, they are UNREACHABLE, because nothing renders them and no control\n * asks for more. A merchant with sixty gift cards sees fifty and no\n * indication that ten more exist; past three hundred, a card issued this\n * morning cannot be reached at all.\n *\n * Here the window IS the query, and it is expressed in PAGES so the same\n * `ListPagination` footer serves this and a cursor feed. The listener is\n * widened to cover every page up to the one being read, so paging BACK costs\n * nothing — the rows are already in the snapshot — and paging forward costs\n * one page more.\n *\n * ## Why the window covers page 0..n rather than page n alone\n *\n * This is a live `onSnapshot`, not a fetch. Firestore cursors need a document\n * snapshot to resume from, which a listener that has never read page n - 1\n * does not have. Widening the limit is what makes forward and backward\n * symmetrical without a second read path, and the cost is bounded by how deep\n * the reader actually goes — with the shared default of ten, page three is\n * thirty-one documents, and most readers never leave page one.\n *\n * ## Why over-fetch by one\n *\n * `hasMore` has to be a fact. Comparing `length === limit` is wrong exactly\n * when the count is an even multiple of the page size — the case that offers\n * a \"next page\" leading nowhere, or hides one that leads somewhere. The probe\n * row answers it outright and is never rendered.\n *\n * ## Why the window resets on `deps`\n *\n * `deps` identify the SUBJECT being listened to. Without the reset, a card\n * paged three deep on one site opens the next site three pages in — a read\n * the reader never asked for, charged to whoever they switched to.\n *\n * `buildQuery` receives the limit to apply, so the caller keeps ownership of\n * ordering and predicates:\n *\n * ```ts\n * usePagedCollection<Card>(\n * (pageLimit) =>\n * hostId\n * ? query(collection(firestore, 'hosts', hostId, 'giftCards'),\n * orderBy('createdAt', 'desc'), limit(pageLimit))\n * : null,\n * [firestore, hostId],\n * { idField: '$id' },\n * )\n * ```\n */\nexport function usePagedCollection<T = DocumentData>(\n buildQuery: (pageLimit: number) => Query<DocumentData> | null | undefined,\n deps: DependencyList,\n options: UsePagedCollectionOptions = {},\n): UsePagedCollectionResult<T> {\n const { pageSize: initialPageSize = TABLE_PAGE_SIZE_DEFAULT, ...collectionOptions } =\n options\n const [pageSize, setPageSizeState] = useState(initialPageSize)\n const [page, setPage] = useState(0)\n const buildQueryRef = useRef(buildQuery)\n buildQueryRef.current = buildQuery\n\n /*\n * A new subject starts at page one. Spreading `deps` is the same contract\n * `useFirestoreCollection` documents — pass the primitive ids, not the\n * Query.\n */\n useEffect(() => {\n setPage(0)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...deps])\n\n // Everything up to and including the page being read, plus the probe row.\n const windowSize = pageSize * (page + 1)\n const result = useFirestoreCollection<T>(\n () => buildQueryRef.current(windowSize + 1),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [...deps, windowSize],\n collectionOptions,\n )\n\n const hasMore = (result.data?.length ?? 0) > windowSize\n const rows = useMemo(\n () => (result.data ?? []).slice(page * pageSize, windowSize),\n [result.data, page, pageSize, windowSize],\n )\n const setPageSize = useCallback((next: number) => {\n setPageSizeState(next)\n // Page four of a ten-row list does not exist once the reader asks for\n // fifty at a time, and an out-of-range page renders as an empty list\n // with no explanation — which reads as the data having gone.\n setPage(0)\n }, [])\n\n return { ...result, rows, hasMore, page, setPage, pageSize, setPageSize }\n}\n"],"names":["useCallback","useEffect","useMemo","useRef","useState","TABLE_PAGE_SIZE_DEFAULT","useFirestoreCollection","usePagedCollection","buildQuery","deps","options","result","pageSize","initialPageSize","collectionOptions","setPageSizeState","page","setPage","buildQueryRef","current","windowSize","hasMore","data","length","rows","slice","setPageSize","next"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;;AAGD,SACEA,WAAW,EACXC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNC,QAAQ,QAEH,QAAO;AACd,SAASC,uBAAuB,QAAQ,8CAA6C;AACrF,SACEC,sBAAsB,QAGjB,gCAA4B;AAyBnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDC,GACD,OAAO,SAASC,mBACdC,UAAyE,EACzEC,IAAoB,EACpBC,UAAqC,CAAC,CAAC;;QA4BtBC;IA1BjB,MAAM,EAAEC,UAAUC,kBAAkBR,uBAAuB,EAAwB,GACjFK,SAD8DI,qDAC9DJ;;;IACF,MAAM,CAACE,UAAUG,iBAAiB,GAAGX,SAASS;IAC9C,MAAM,CAACG,MAAMC,QAAQ,GAAGb,SAAS;IACjC,MAAMc,gBAAgBf,OAAOK;IAC7BU,cAAcC,OAAO,GAAGX;IAExB;;;;GAIC,GACDP,UAAU;QACRgB,QAAQ;IACR,uDAAuD;IACzD,GAAG;WAAIR;KAAK;IAEZ,0EAA0E;IAC1E,MAAMW,aAAaR,WAAYI,CAAAA,OAAO,CAAA;IACtC,MAAML,SAASL,uBACb,IAAMY,cAAcC,OAAO,CAACC,aAAa,IACzC,uDAAuD;IACvD;WAAIX;QAAMW;KAAW,EACrBN;IAGF,MAAMO,UAAU,UAACV,eAAAA,OAAOW,IAAI,qBAAXX,aAAaY,MAAM,mBAAI,KAAKH;IAC7C,MAAMI,OAAOtB,QACX;YAAOS;eAAD,EAACA,eAAAA,OAAOW,IAAI,YAAXX,eAAe,EAAE,EAAEc,KAAK,CAACT,OAAOJ,UAAUQ;OACjD;QAACT,OAAOW,IAAI;QAAEN;QAAMJ;QAAUQ;KAAW;IAE3C,MAAMM,cAAc1B,YAAY,CAAC2B;QAC/BZ,iBAAiBY;QACjB,sEAAsE;QACtE,qEAAqE;QACrE,6DAA6D;QAC7DV,QAAQ;IACV,GAAG,EAAE;IAEL,OAAO,aAAKN;QAAQa;QAAMH;QAASL;QAAMC;QAASL;QAAUc;;AAC9D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live per-plugin config for a workspace (AGL-428): the org's
|
|
3
|
+
* `pluginSettings/{pluginId}` overrides merged over the plugin's declared
|
|
4
|
+
* schema defaults (type-coerced). Plugins call this from console pages/
|
|
5
|
+
* widgets with the orgId the shell resolves; `ready` turns true once the
|
|
6
|
+
* doc read settles either way.
|
|
7
|
+
*/
|
|
8
|
+
export declare function usePluginConfig(orgId: string | null | undefined, pluginId: string): {
|
|
9
|
+
config: Record<string, unknown>;
|
|
10
|
+
ready: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The same config, resolved for ONE SITE (AGL-428, AGL-1014): the
|
|
14
|
+
* workspace's stored overrides, then only the keys this site answers for
|
|
15
|
+
* itself, over the plugin's declared schema defaults. `overrides` names those keys, so a
|
|
16
|
+
* surface can say which answers are the site's own rather than inferring it
|
|
17
|
+
* by comparing values — two levels that happen to agree are still two levels.
|
|
18
|
+
*
|
|
19
|
+
* BOTH refs are held at null until their id is known, never addressed as
|
|
20
|
+
* `orgs/-pending-` or `hosts/-pending-` (AGL-1440). `pluginSettings` is
|
|
21
|
+
* member-gated at both scopes, so a sentinel id is a guaranteed permission
|
|
22
|
+
* denial on every mount rather than a read that might succeed — the
|
|
23
|
+
* anti-pattern `use-org-plan.ts` documents removing (AGL-1064). A null ref
|
|
24
|
+
* issues nothing and holds `status` at `'loading'`, which is already this
|
|
25
|
+
* hook's contract while the answer is unknown.
|
|
26
|
+
*
|
|
27
|
+
* `ready` needs BOTH reads, and needs both to have SUCCEEDED (AGL-1066).
|
|
28
|
+
* Answering ready off the org read alone would hand a caller the workspace's
|
|
29
|
+
* value as though it were this site's, which is the exact failure the site
|
|
30
|
+
* layer exists to prevent — and answering it off a refused read would hand
|
|
31
|
+
* them schema defaults as though they were stored settings.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useSitePluginConfig(orgId: string | null | undefined, hostId: string | null | undefined, pluginId: string): {
|
|
34
|
+
config: Record<string, unknown>;
|
|
35
|
+
overrides: string[];
|
|
36
|
+
ready: boolean;
|
|
37
|
+
};
|
|
38
|
+
export default usePluginConfig;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
import { getPluginConfigSchema, mergePluginConfig, pluginConfigOverrides, resolvePluginConfig } from "@aglyn/aglyn";
|
|
19
|
+
import { doc } from "firebase/firestore";
|
|
20
|
+
import { useMemo } from "react";
|
|
21
|
+
import { useFirestore } from "./firebase/firebase-services.js";
|
|
22
|
+
import { useFirestoreDoc } from "./use-firestore-doc.js";
|
|
23
|
+
/**
|
|
24
|
+
* Live per-plugin config for a workspace (AGL-428): the org's
|
|
25
|
+
* `pluginSettings/{pluginId}` overrides merged over the plugin's declared
|
|
26
|
+
* schema defaults (type-coerced). Plugins call this from console pages/
|
|
27
|
+
* widgets with the orgId the shell resolves; `ready` turns true once the
|
|
28
|
+
* doc read settles either way.
|
|
29
|
+
*/ export function usePluginConfig(orgId, pluginId) {
|
|
30
|
+
const firestore = useFirestore();
|
|
31
|
+
// Held at null while the org is unknown, never addressed as
|
|
32
|
+
// `orgs/-pending-` (AGL-1440): `pluginSettings` is member-gated, so the
|
|
33
|
+
// sentinel was a guaranteed-denied read on every mount of every plugin
|
|
34
|
+
// surface — the exact anti-pattern `use-org-plan.ts` documents removing
|
|
35
|
+
// (AGL-1064). A null ref issues nothing, `status` stays `'loading'`, and
|
|
36
|
+
// `ready` below stays false, which is already this hook's contract while
|
|
37
|
+
// the answer is unknown.
|
|
38
|
+
const { data, status } = useFirestoreDoc(()=>orgId ? doc(firestore, 'orgs', orgId, 'pluginSettings', pluginId) : null, [
|
|
39
|
+
firestore,
|
|
40
|
+
orgId,
|
|
41
|
+
pluginId
|
|
42
|
+
]);
|
|
43
|
+
const config = useMemo(()=>{
|
|
44
|
+
const schema = getPluginConfigSchema(pluginId);
|
|
45
|
+
if (!schema) return data != null ? data : {};
|
|
46
|
+
return mergePluginConfig(schema, data != null ? data : null);
|
|
47
|
+
}, [
|
|
48
|
+
pluginId,
|
|
49
|
+
data
|
|
50
|
+
]);
|
|
51
|
+
// `'success'` rather than `!== 'loading'` (AGL-1066): a refused listen can
|
|
52
|
+
// now reach `'error'`, and answering "ready" with an empty config would let
|
|
53
|
+
// a caller act on schema defaults as though they were the stored settings.
|
|
54
|
+
return {
|
|
55
|
+
config,
|
|
56
|
+
ready: status === 'success'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The same config, resolved for ONE SITE (AGL-428, AGL-1014): the
|
|
61
|
+
* workspace's stored overrides, then only the keys this site answers for
|
|
62
|
+
* itself, over the plugin's declared schema defaults. `overrides` names those keys, so a
|
|
63
|
+
* surface can say which answers are the site's own rather than inferring it
|
|
64
|
+
* by comparing values — two levels that happen to agree are still two levels.
|
|
65
|
+
*
|
|
66
|
+
* BOTH refs are held at null until their id is known, never addressed as
|
|
67
|
+
* `orgs/-pending-` or `hosts/-pending-` (AGL-1440). `pluginSettings` is
|
|
68
|
+
* member-gated at both scopes, so a sentinel id is a guaranteed permission
|
|
69
|
+
* denial on every mount rather than a read that might succeed — the
|
|
70
|
+
* anti-pattern `use-org-plan.ts` documents removing (AGL-1064). A null ref
|
|
71
|
+
* issues nothing and holds `status` at `'loading'`, which is already this
|
|
72
|
+
* hook's contract while the answer is unknown.
|
|
73
|
+
*
|
|
74
|
+
* `ready` needs BOTH reads, and needs both to have SUCCEEDED (AGL-1066).
|
|
75
|
+
* Answering ready off the org read alone would hand a caller the workspace's
|
|
76
|
+
* value as though it were this site's, which is the exact failure the site
|
|
77
|
+
* layer exists to prevent — and answering it off a refused read would hand
|
|
78
|
+
* them schema defaults as though they were stored settings.
|
|
79
|
+
*/ export function useSitePluginConfig(orgId, hostId, pluginId) {
|
|
80
|
+
const firestore = useFirestore();
|
|
81
|
+
const { data: org, status: orgStatus } = useFirestoreDoc(()=>orgId ? doc(firestore, 'orgs', orgId, 'pluginSettings', pluginId) : null, [
|
|
82
|
+
firestore,
|
|
83
|
+
orgId,
|
|
84
|
+
pluginId
|
|
85
|
+
]);
|
|
86
|
+
const { data: host, status: hostStatus } = useFirestoreDoc(()=>hostId ? doc(firestore, 'hosts', hostId, 'pluginSettings', pluginId) : null, [
|
|
87
|
+
firestore,
|
|
88
|
+
hostId,
|
|
89
|
+
pluginId
|
|
90
|
+
]);
|
|
91
|
+
const resolved = useMemo(()=>{
|
|
92
|
+
const schema = getPluginConfigSchema(pluginId);
|
|
93
|
+
// Without a registered schema there is no field list to layer by, so the
|
|
94
|
+
// site's raw document simply narrows the workspace's — the same degrade
|
|
95
|
+
// `usePluginConfig` makes, one level deeper.
|
|
96
|
+
if (!schema) {
|
|
97
|
+
return {
|
|
98
|
+
config: _extends({}, org != null ? org : {}, host != null ? host : {}),
|
|
99
|
+
overrides: Object.keys(host != null ? host : {})
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
config: resolvePluginConfig(schema, {
|
|
104
|
+
org: org != null ? org : null,
|
|
105
|
+
host: host != null ? host : null
|
|
106
|
+
}),
|
|
107
|
+
overrides: pluginConfigOverrides(schema, host != null ? host : null)
|
|
108
|
+
};
|
|
109
|
+
}, [
|
|
110
|
+
pluginId,
|
|
111
|
+
org,
|
|
112
|
+
host
|
|
113
|
+
]);
|
|
114
|
+
return _extends({}, resolved, {
|
|
115
|
+
ready: orgStatus === 'success' && hostStatus === 'success'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export default usePluginConfig;
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=use-plugin-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/feature/instance/src/lib/hooks/use-plugin-config.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n getPluginConfigSchema,\n mergePluginConfig,\n pluginConfigOverrides,\n resolvePluginConfig,\n} from '@aglyn/aglyn'\nimport { doc } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { useFirestore } from './firebase/firebase-services'\nimport { useFirestoreDoc } from './use-firestore-doc'\n\n/**\n * Live per-plugin config for a workspace (AGL-428): the org's\n * `pluginSettings/{pluginId}` overrides merged over the plugin's declared\n * schema defaults (type-coerced). Plugins call this from console pages/\n * widgets with the orgId the shell resolves; `ready` turns true once the\n * doc read settles either way.\n */\nexport function usePluginConfig(\n orgId: string | null | undefined,\n pluginId: string,\n): { config: Record<string, unknown>; ready: boolean } {\n const firestore = useFirestore()\n // Held at null while the org is unknown, never addressed as\n // `orgs/-pending-` (AGL-1440): `pluginSettings` is member-gated, so the\n // sentinel was a guaranteed-denied read on every mount of every plugin\n // surface — the exact anti-pattern `use-org-plan.ts` documents removing\n // (AGL-1064). A null ref issues nothing, `status` stays `'loading'`, and\n // `ready` below stays false, which is already this hook's contract while\n // the answer is unknown.\n const { data, status } = useFirestoreDoc<Record<string, unknown>>(\n () =>\n orgId\n ? doc(firestore, 'orgs', orgId, 'pluginSettings', pluginId)\n : null,\n [firestore, orgId, pluginId],\n )\n const config = useMemo(() => {\n const schema = getPluginConfigSchema(pluginId)\n if (!schema) return (data as Record<string, unknown>) ?? {}\n return mergePluginConfig(schema, data ?? null)\n }, [pluginId, data])\n // `'success'` rather than `!== 'loading'` (AGL-1066): a refused listen can\n // now reach `'error'`, and answering \"ready\" with an empty config would let\n // a caller act on schema defaults as though they were the stored settings.\n return { config, ready: status === 'success' }\n}\n\n/**\n * The same config, resolved for ONE SITE (AGL-428, AGL-1014): the\n * workspace's stored overrides, then only the keys this site answers for\n * itself, over the plugin's declared schema defaults. `overrides` names those keys, so a\n * surface can say which answers are the site's own rather than inferring it\n * by comparing values — two levels that happen to agree are still two levels.\n *\n * BOTH refs are held at null until their id is known, never addressed as\n * `orgs/-pending-` or `hosts/-pending-` (AGL-1440). `pluginSettings` is\n * member-gated at both scopes, so a sentinel id is a guaranteed permission\n * denial on every mount rather than a read that might succeed — the\n * anti-pattern `use-org-plan.ts` documents removing (AGL-1064). A null ref\n * issues nothing and holds `status` at `'loading'`, which is already this\n * hook's contract while the answer is unknown.\n *\n * `ready` needs BOTH reads, and needs both to have SUCCEEDED (AGL-1066).\n * Answering ready off the org read alone would hand a caller the workspace's\n * value as though it were this site's, which is the exact failure the site\n * layer exists to prevent — and answering it off a refused read would hand\n * them schema defaults as though they were stored settings.\n */\nexport function useSitePluginConfig(\n orgId: string | null | undefined,\n hostId: string | null | undefined,\n pluginId: string,\n): { config: Record<string, unknown>; overrides: string[]; ready: boolean } {\n const firestore = useFirestore()\n const { data: org, status: orgStatus } = useFirestoreDoc<\n Record<string, unknown>\n >(\n () =>\n orgId ? doc(firestore, 'orgs', orgId, 'pluginSettings', pluginId) : null,\n [firestore, orgId, pluginId],\n )\n const { data: host, status: hostStatus } = useFirestoreDoc<\n Record<string, unknown>\n >(\n () =>\n hostId\n ? doc(firestore, 'hosts', hostId, 'pluginSettings', pluginId)\n : null,\n [firestore, hostId, pluginId],\n )\n const resolved = useMemo(() => {\n const schema = getPluginConfigSchema(pluginId)\n // Without a registered schema there is no field list to layer by, so the\n // site's raw document simply narrows the workspace's — the same degrade\n // `usePluginConfig` makes, one level deeper.\n if (!schema) {\n return {\n config: { ...(org ?? {}), ...(host ?? {}) },\n overrides: Object.keys(host ?? {}),\n }\n }\n return {\n config: resolvePluginConfig(schema, {\n org: org ?? null,\n host: host ?? null,\n }),\n overrides: pluginConfigOverrides(schema, host ?? null),\n }\n }, [pluginId, org, host])\n return {\n ...resolved,\n ready: orgStatus === 'success' && hostStatus === 'success',\n }\n}\n\nexport default usePluginConfig\n"],"names":["getPluginConfigSchema","mergePluginConfig","pluginConfigOverrides","resolvePluginConfig","doc","useMemo","useFirestore","useFirestoreDoc","usePluginConfig","orgId","pluginId","firestore","data","status","config","schema","ready","useSitePluginConfig","hostId","org","orgStatus","host","hostStatus","resolved","overrides","Object","keys"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SACEA,qBAAqB,EACrBC,iBAAiB,EACjBC,qBAAqB,EACrBC,mBAAmB,QACd,eAAc;AACrB,SAASC,GAAG,QAAQ,qBAAoB;AACxC,SAASC,OAAO,QAAQ,QAAO;AAC/B,SAASC,YAAY,QAAQ,kCAA8B;AAC3D,SAASC,eAAe,QAAQ,yBAAqB;AAErD;;;;;;CAMC,GACD,OAAO,SAASC,gBACdC,KAAgC,EAChCC,QAAgB;IAEhB,MAAMC,YAAYL;IAClB,4DAA4D;IAC5D,wEAAwE;IACxE,uEAAuE;IACvE,wEAAwE;IACxE,yEAAyE;IACzE,yEAAyE;IACzE,yBAAyB;IACzB,MAAM,EAAEM,IAAI,EAAEC,MAAM,EAAE,GAAGN,gBACvB,IACEE,QACIL,IAAIO,WAAW,QAAQF,OAAO,kBAAkBC,YAChD,MACN;QAACC;QAAWF;QAAOC;KAAS;IAE9B,MAAMI,SAAST,QAAQ;QACrB,MAAMU,SAASf,sBAAsBU;QACrC,IAAI,CAACK,QAAQ,OAAQH,eAAAA,OAAoC,CAAC;QAC1D,OAAOX,kBAAkBc,QAAQH,eAAAA,OAAQ;IAC3C,GAAG;QAACF;QAAUE;KAAK;IACnB,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,OAAO;QAAEE;QAAQE,OAAOH,WAAW;IAAU;AAC/C;AAEA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASI,oBACdR,KAAgC,EAChCS,MAAiC,EACjCR,QAAgB;IAEhB,MAAMC,YAAYL;IAClB,MAAM,EAAEM,MAAMO,GAAG,EAAEN,QAAQO,SAAS,EAAE,GAAGb,gBAGvC,IACEE,QAAQL,IAAIO,WAAW,QAAQF,OAAO,kBAAkBC,YAAY,MACtE;QAACC;QAAWF;QAAOC;KAAS;IAE9B,MAAM,EAAEE,MAAMS,IAAI,EAAER,QAAQS,UAAU,EAAE,GAAGf,gBAGzC,IACEW,SACId,IAAIO,WAAW,SAASO,QAAQ,kBAAkBR,YAClD,MACN;QAACC;QAAWO;QAAQR;KAAS;IAE/B,MAAMa,WAAWlB,QAAQ;QACvB,MAAMU,SAASf,sBAAsBU;QACrC,yEAAyE;QACzE,wEAAwE;QACxE,6CAA6C;QAC7C,IAAI,CAACK,QAAQ;YACX,OAAO;gBACLD,QAAQ,aAAMK,cAAAA,MAAO,CAAC,GAAQE,eAAAA,OAAQ,CAAC;gBACvCG,WAAWC,OAAOC,IAAI,CAACL,eAAAA,OAAQ,CAAC;YAClC;QACF;QACA,OAAO;YACLP,QAAQX,oBAAoBY,QAAQ;gBAClCI,GAAG,EAAEA,cAAAA,MAAO;gBACZE,IAAI,EAAEA,eAAAA,OAAQ;YAChB;YACAG,WAAWtB,sBAAsBa,QAAQM,eAAAA,OAAQ;QACnD;IACF,GAAG;QAACX;QAAUS;QAAKE;KAAK;IACxB,OAAO,aACFE;QACHP,OAAOI,cAAc,aAAaE,eAAe;;AAErD;AAEA,eAAed,gBAAe"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface ScopeTokensState {
|
|
2
|
+
/** The caller's read set, for `array-contains-any` (AGL-1037). */
|
|
3
|
+
tokens: string[];
|
|
4
|
+
/** Org-wide members read everything and need no query filter. */
|
|
5
|
+
orgWide: boolean;
|
|
6
|
+
loaded: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The signed-in user's scope tokens in an org (AGL-1044/1045).
|
|
10
|
+
*
|
|
11
|
+
* This is NOT a UI-gating convenience — it is required for correctness.
|
|
12
|
+
* Under AGL-1041/1042 the rules evaluate per document on a LIST, and
|
|
13
|
+
* Firestore rejects the WHOLE query if any candidate would fail. So a
|
|
14
|
+
* scoped collaborator's unfiltered `collection(...)` read comes back
|
|
15
|
+
* `permission-denied`, not filtered: without the matching
|
|
16
|
+
* `array-contains-any` constraint the page does not show less, it shows
|
|
17
|
+
* an error.
|
|
18
|
+
*
|
|
19
|
+
* Org-wide members deliberately get `orgWide: true` and no filter. Adding
|
|
20
|
+
* one would cost them a composite index for no benefit, and would silently
|
|
21
|
+
* hide any doc whose `visibleTo` the backfill has not reached.
|
|
22
|
+
*
|
|
23
|
+
* Defaults to org-wide while loading: the rules are the enforcement point,
|
|
24
|
+
* and guessing "scoped" before the member doc arrives would flash an empty
|
|
25
|
+
* library at people who can see everything.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useScopeTokens(orgId: string | undefined): ScopeTokensState;
|
|
28
|
+
export default useScopeTokens;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Aglyn LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/ import { isOrgWideMember, memberScopeTokens, ORG_SCOPE_TOKEN } from "@aglyn/aglyn";
|
|
19
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
20
|
+
import { useEffect, useState } from "react";
|
|
21
|
+
import { useFirestore, useUser } from "./firebase/firebase-services.js";
|
|
22
|
+
/**
|
|
23
|
+
* The signed-in user's scope tokens in an org (AGL-1044/1045).
|
|
24
|
+
*
|
|
25
|
+
* This is NOT a UI-gating convenience — it is required for correctness.
|
|
26
|
+
* Under AGL-1041/1042 the rules evaluate per document on a LIST, and
|
|
27
|
+
* Firestore rejects the WHOLE query if any candidate would fail. So a
|
|
28
|
+
* scoped collaborator's unfiltered `collection(...)` read comes back
|
|
29
|
+
* `permission-denied`, not filtered: without the matching
|
|
30
|
+
* `array-contains-any` constraint the page does not show less, it shows
|
|
31
|
+
* an error.
|
|
32
|
+
*
|
|
33
|
+
* Org-wide members deliberately get `orgWide: true` and no filter. Adding
|
|
34
|
+
* one would cost them a composite index for no benefit, and would silently
|
|
35
|
+
* hide any doc whose `visibleTo` the backfill has not reached.
|
|
36
|
+
*
|
|
37
|
+
* Defaults to org-wide while loading: the rules are the enforcement point,
|
|
38
|
+
* and guessing "scoped" before the member doc arrives would flash an empty
|
|
39
|
+
* library at people who can see everything.
|
|
40
|
+
*/ export function useScopeTokens(orgId) {
|
|
41
|
+
const { data: user } = useUser();
|
|
42
|
+
const firestore = useFirestore();
|
|
43
|
+
const [state, setState] = useState({
|
|
44
|
+
tokens: [
|
|
45
|
+
ORG_SCOPE_TOKEN
|
|
46
|
+
],
|
|
47
|
+
orgWide: true,
|
|
48
|
+
loaded: false
|
|
49
|
+
});
|
|
50
|
+
useEffect(()=>{
|
|
51
|
+
const uid = user == null ? void 0 : user.uid;
|
|
52
|
+
if (!uid || !orgId) {
|
|
53
|
+
setState({
|
|
54
|
+
tokens: [
|
|
55
|
+
ORG_SCOPE_TOKEN
|
|
56
|
+
],
|
|
57
|
+
orgWide: true,
|
|
58
|
+
loaded: Boolean(uid)
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let cancelled = false;
|
|
63
|
+
getDoc(doc(firestore, 'orgs', orgId, 'members', uid)).then((snapshot)=>{
|
|
64
|
+
if (cancelled) return;
|
|
65
|
+
const member = snapshot.exists() ? _extends({
|
|
66
|
+
$id: uid
|
|
67
|
+
}, snapshot.data()) : undefined;
|
|
68
|
+
// `memberScopeTokens` owns the "stored projection, else recompute"
|
|
69
|
+
// rule so the client and every Admin-SDK gate answer it the same
|
|
70
|
+
// way — a member doc the AGL-1040 backfill has not reached must
|
|
71
|
+
// still resolve to what `grantHostAccess` would have stamped.
|
|
72
|
+
setState({
|
|
73
|
+
tokens: memberScopeTokens(member),
|
|
74
|
+
orgWide: isOrgWideMember(member),
|
|
75
|
+
loaded: true
|
|
76
|
+
});
|
|
77
|
+
}).catch(()=>{
|
|
78
|
+
if (!cancelled) {
|
|
79
|
+
setState({
|
|
80
|
+
tokens: [
|
|
81
|
+
ORG_SCOPE_TOKEN
|
|
82
|
+
],
|
|
83
|
+
orgWide: true,
|
|
84
|
+
loaded: true
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return ()=>{
|
|
89
|
+
cancelled = true;
|
|
90
|
+
};
|
|
91
|
+
}, [
|
|
92
|
+
firestore,
|
|
93
|
+
orgId,
|
|
94
|
+
user
|
|
95
|
+
]);
|
|
96
|
+
return state;
|
|
97
|
+
}
|
|
98
|
+
export default useScopeTokens;
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=use-scope-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/feature/instance/src/lib/hooks/use-scope-tokens.ts"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n isOrgWideMember,\n memberScopeTokens,\n ORG_SCOPE_TOKEN,\n type AglynOrgMember,\n} from '@aglyn/aglyn'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport { useFirestore, useUser } from './firebase/firebase-services'\n\nexport interface ScopeTokensState {\n /** The caller's read set, for `array-contains-any` (AGL-1037). */\n tokens: string[]\n /** Org-wide members read everything and need no query filter. */\n orgWide: boolean\n loaded: boolean\n}\n\n/**\n * The signed-in user's scope tokens in an org (AGL-1044/1045).\n *\n * This is NOT a UI-gating convenience — it is required for correctness.\n * Under AGL-1041/1042 the rules evaluate per document on a LIST, and\n * Firestore rejects the WHOLE query if any candidate would fail. So a\n * scoped collaborator's unfiltered `collection(...)` read comes back\n * `permission-denied`, not filtered: without the matching\n * `array-contains-any` constraint the page does not show less, it shows\n * an error.\n *\n * Org-wide members deliberately get `orgWide: true` and no filter. Adding\n * one would cost them a composite index for no benefit, and would silently\n * hide any doc whose `visibleTo` the backfill has not reached.\n *\n * Defaults to org-wide while loading: the rules are the enforcement point,\n * and guessing \"scoped\" before the member doc arrives would flash an empty\n * library at people who can see everything.\n */\nexport function useScopeTokens(orgId: string | undefined): ScopeTokensState {\n const { data: user } = useUser()\n const firestore = useFirestore()\n const [state, setState] = useState<ScopeTokensState>({\n tokens: [ORG_SCOPE_TOKEN],\n orgWide: true,\n loaded: false,\n })\n\n useEffect(() => {\n const uid = (user as { uid?: string } | undefined)?.uid\n if (!uid || !orgId) {\n setState({ tokens: [ORG_SCOPE_TOKEN], orgWide: true, loaded: Boolean(uid) })\n return\n }\n let cancelled = false\n getDoc(doc(firestore, 'orgs', orgId, 'members', uid))\n .then((snapshot) => {\n if (cancelled) return\n const member = snapshot.exists()\n ? ({ $id: uid, ...snapshot.data() } as Partial<AglynOrgMember>)\n : undefined\n // `memberScopeTokens` owns the \"stored projection, else recompute\"\n // rule so the client and every Admin-SDK gate answer it the same\n // way — a member doc the AGL-1040 backfill has not reached must\n // still resolve to what `grantHostAccess` would have stamped.\n setState({\n tokens: memberScopeTokens(member),\n orgWide: isOrgWideMember(member),\n loaded: true,\n })\n })\n .catch(() => {\n if (!cancelled) {\n setState({ tokens: [ORG_SCOPE_TOKEN], orgWide: true, loaded: true })\n }\n })\n return () => {\n cancelled = true\n }\n }, [firestore, orgId, user])\n\n return state\n}\n\nexport default useScopeTokens\n"],"names":["isOrgWideMember","memberScopeTokens","ORG_SCOPE_TOKEN","doc","getDoc","useEffect","useState","useFirestore","useUser","useScopeTokens","orgId","data","user","firestore","state","setState","tokens","orgWide","loaded","uid","Boolean","cancelled","then","snapshot","member","exists","$id","undefined","catch"],"mappings":"AAAA;;AAEA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,eAAe,EACfC,iBAAiB,EACjBC,eAAe,QAEV,eAAc;AACrB,SAASC,GAAG,EAAEC,MAAM,QAAQ,qBAAoB;AAChD,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAC3C,SAASC,YAAY,EAAEC,OAAO,QAAQ,kCAA8B;AAUpE;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,eAAeC,KAAyB;IACtD,MAAM,EAAEC,MAAMC,IAAI,EAAE,GAAGJ;IACvB,MAAMK,YAAYN;IAClB,MAAM,CAACO,OAAOC,SAAS,GAAGT,SAA2B;QACnDU,QAAQ;YAACd;SAAgB;QACzBe,SAAS;QACTC,QAAQ;IACV;IAEAb,UAAU;QACR,MAAMc,MAAOP,wBAAD,AAACA,KAAuCO,GAAG;QACvD,IAAI,CAACA,OAAO,CAACT,OAAO;YAClBK,SAAS;gBAAEC,QAAQ;oBAACd;iBAAgB;gBAAEe,SAAS;gBAAMC,QAAQE,QAAQD;YAAK;YAC1E;QACF;QACA,IAAIE,YAAY;QAChBjB,OAAOD,IAAIU,WAAW,QAAQH,OAAO,WAAWS,MAC7CG,IAAI,CAAC,CAACC;YACL,IAAIF,WAAW;YACf,MAAMG,SAASD,SAASE,MAAM,KACzB;gBAAEC,KAAKP;eAAQI,SAASZ,IAAI,MAC7BgB;YACJ,mEAAmE;YACnE,iEAAiE;YACjE,gEAAgE;YAChE,8DAA8D;YAC9DZ,SAAS;gBACPC,QAAQf,kBAAkBuB;gBAC1BP,SAASjB,gBAAgBwB;gBACzBN,QAAQ;YACV;QACF,GACCU,KAAK,CAAC;YACL,IAAI,CAACP,WAAW;gBACdN,SAAS;oBAAEC,QAAQ;wBAACd;qBAAgB;oBAAEe,SAAS;oBAAMC,QAAQ;gBAAK;YACpE;QACF;QACF,OAAO;YACLG,YAAY;QACd;IACF,GAAG;QAACR;QAAWH;QAAOE;KAAK;IAE3B,OAAOE;AACT;AAEA,eAAeL,eAAc"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type * as Aglyn from '@aglyn/aglyn';
|
|
18
|
+
import type { DocumentReference } from 'firebase/firestore';
|
|
19
|
+
import { type FirestoreDocOptions } from './firebase/firebase-services';
|
|
20
|
+
export declare const useScreenVersionRef: ({ hostId, screenId, versionId }: {
|
|
21
|
+
hostId: string;
|
|
22
|
+
screenId: string;
|
|
23
|
+
versionId: string;
|
|
24
|
+
}) => DocumentReference<Aglyn.AglynScreenVersion>;
|
|
25
|
+
export declare const useScreenVersion: (data: {
|
|
26
|
+
screenId: string;
|
|
27
|
+
versionId: string;
|
|
28
|
+
hostId: string;
|
|
29
|
+
}, options?: FirestoreDocOptions<Aglyn.AglynScreenVersion>) => {
|
|
30
|
+
doc: import("./firebase/firebase-services").ObservableStatus<Aglyn.AglynScreenVersion>;
|
|
31
|
+
setDoc: import("./helpers/use-modify-doc-callback").ModifyDocCallback<Aglyn.AglynScreenVersion>;
|
|
32
|
+
};
|
|
33
|
+
export default useScreenVersion;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ // A TYPE-only namespace: every `Aglyn.` reference below is a type, and held
|
|
17
|
+
// as a VALUE the namespace pins every module the barrel reaches into the
|
|
18
|
+
// console shell that renders these hooks. `import type` erases instead.
|
|
19
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
20
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
21
|
+
import { compress, decompress } from "@aglyn/aglyn";
|
|
22
|
+
import { Timestamp } from "@aglyn/shared-util-timestamp";
|
|
23
|
+
import { Bytes, doc } from "firebase/firestore";
|
|
24
|
+
import { useFirestore } from "./firebase/firebase-services.js";
|
|
25
|
+
import useDoc from "./helpers/use-doc.js";
|
|
26
|
+
export const useScreenVersionRef = ({ hostId, screenId, versionId })=>{
|
|
27
|
+
const firestore = useFirestore();
|
|
28
|
+
const ref = doc(firestore, 'hosts', hostId, 'screens', screenId, 'versions', versionId);
|
|
29
|
+
return ref.withConverter({
|
|
30
|
+
toFirestore (data) {
|
|
31
|
+
const { $id } = data, rest = _object_without_properties_loose(data, [
|
|
32
|
+
"$id"
|
|
33
|
+
]);
|
|
34
|
+
// Only emit `nodes` when the write actually carries them (AGL-1250).
|
|
35
|
+
// Compressing `rest?.nodes || {}` unconditionally meant a partial
|
|
36
|
+
// `setDoc(…, {merge: true})` — one that set some other field —
|
|
37
|
+
// shipped an EMPTY compressed map, and merge faithfully merged that
|
|
38
|
+
// emptiness over the real tree, silently destroying the document.
|
|
39
|
+
if ((rest == null ? void 0 : rest.nodes) === undefined) return _extends({}, rest, {
|
|
40
|
+
updatedAt: Timestamp.now()
|
|
41
|
+
});
|
|
42
|
+
const nodes = rest.nodes instanceof Bytes ? rest.nodes : Bytes.fromUint8Array(compress(rest.nodes));
|
|
43
|
+
return _extends({}, rest, {
|
|
44
|
+
nodes,
|
|
45
|
+
updatedAt: Timestamp.now()
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
fromFirestore (snapshot, options) {
|
|
49
|
+
if (!snapshot.exists()) return undefined;
|
|
50
|
+
const data = snapshot.data(options);
|
|
51
|
+
if ((data == null ? void 0 : data.nodes) instanceof Bytes) {
|
|
52
|
+
return _extends({}, data, {
|
|
53
|
+
nodes: decompress(data.nodes)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
export const useScreenVersion = (data, options)=>{
|
|
61
|
+
return useDoc(useScreenVersionRef(data), options);
|
|
62
|
+
};
|
|
63
|
+
export default useScreenVersion;
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=use-screen-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/feature/instance/src/lib/hooks/use-screen-version.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2022 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// A TYPE-only namespace: every `Aglyn.` reference below is a type, and held\n// as a VALUE the namespace pins every module the barrel reaches into the\n// console shell that renders these hooks. `import type` erases instead.\nimport type * as Aglyn from '@aglyn/aglyn'\nimport { compress, decompress } from '@aglyn/aglyn'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport type { DocumentReference } from 'firebase/firestore'\nimport { Bytes, doc } from 'firebase/firestore'\nimport { useFirestore, type FirestoreDocOptions } from './firebase/firebase-services'\nimport useDoc from './helpers/use-doc'\n\nexport const useScreenVersionRef = ({ hostId, screenId, versionId }: { hostId: string; screenId: string; versionId: string }) => {\n const firestore = useFirestore()\n const ref = doc(\n firestore,\n 'hosts',\n hostId,\n 'screens',\n screenId,\n 'versions',\n versionId,\n )\n return ref.withConverter({\n toFirestore(data) {\n const { $id, ...rest } = data\n // Only emit `nodes` when the write actually carries them (AGL-1250).\n // Compressing `rest?.nodes || {}` unconditionally meant a partial\n // `setDoc(…, {merge: true})` — one that set some other field —\n // shipped an EMPTY compressed map, and merge faithfully merged that\n // emptiness over the real tree, silently destroying the document.\n if (rest?.nodes === undefined) return { ...rest, updatedAt: Timestamp.now() }\n const nodes = rest.nodes instanceof Bytes\n ? rest.nodes\n : Bytes.fromUint8Array(compress(rest.nodes))\n return { ...rest, nodes, updatedAt: Timestamp.now() }\n },\n fromFirestore(snapshot, options) {\n if (!snapshot.exists()) return undefined\n const data = snapshot.data(options)\n if (data?.nodes instanceof Bytes) {\n return { ...data, nodes: decompress(data.nodes) } as Aglyn.AglynScreenVersion\n }\n return data as Aglyn.AglynScreenVersion\n },\n }) as DocumentReference<Aglyn.AglynScreenVersion>\n}\n\nexport const useScreenVersion = (\n data: {\n screenId: string\n versionId: string\n hostId: string\n },\n options?: FirestoreDocOptions<Aglyn.AglynScreenVersion>,\n) => {\n return useDoc(useScreenVersionRef(data), options)\n}\n\nexport default useScreenVersion\n"],"names":["compress","decompress","Timestamp","Bytes","doc","useFirestore","useDoc","useScreenVersionRef","hostId","screenId","versionId","firestore","ref","withConverter","toFirestore","data","$id","rest","nodes","undefined","updatedAt","now","fromUint8Array","fromFirestore","snapshot","options","exists","useScreenVersion"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,4EAA4E;AAC5E,yEAAyE;AACzE,wEAAwE;;;AAExE,SAASA,QAAQ,EAAEC,UAAU,QAAQ,eAAc;AACnD,SAASC,SAAS,QAAQ,+BAA8B;AAExD,SAASC,KAAK,EAAEC,GAAG,QAAQ,qBAAoB;AAC/C,SAASC,YAAY,QAAkC,kCAA8B;AACrF,OAAOC,YAAY,uBAAmB;AAEtC,OAAO,MAAMC,sBAAsB,CAAC,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,SAAS,EAA2D;IAC1H,MAAMC,YAAYN;IAClB,MAAMO,MAAMR,IACVO,WACA,SACAH,QACA,WACAC,UACA,YACAC;IAEF,OAAOE,IAAIC,aAAa,CAAC;QACvBC,aAAYC,IAAI;YACd,MAAM,EAAEC,GAAG,EAAW,GAAGD,MAATE,wCAASF;;;YACzB,qEAAqE;YACrE,kEAAkE;YAClE,+DAA+D;YAC/D,oEAAoE;YACpE,kEAAkE;YAClE,IAAIE,CAAAA,wBAAAA,KAAMC,KAAK,MAAKC,WAAW,OAAO,aAAKF;gBAAMG,WAAWlB,UAAUmB,GAAG;;YACzE,MAAMH,QAAQD,KAAKC,KAAK,YAAYf,QAChCc,KAAKC,KAAK,GACVf,MAAMmB,cAAc,CAACtB,SAASiB,KAAKC,KAAK;YAC5C,OAAO,aAAKD;gBAAMC;gBAAOE,WAAWlB,UAAUmB,GAAG;;QACnD;QACAE,eAAcC,QAAQ,EAAEC,OAAO;YAC7B,IAAI,CAACD,SAASE,MAAM,IAAI,OAAOP;YAC/B,MAAMJ,OAAOS,SAAST,IAAI,CAACU;YAC3B,IAAIV,CAAAA,wBAAAA,KAAMG,KAAK,aAAYf,OAAO;gBAChC,OAAO,aAAKY;oBAAMG,OAAOjB,WAAWc,KAAKG,KAAK;;YAChD;YACA,OAAOH;QACT;IACF;AACF,EAAC;AAED,OAAO,MAAMY,mBAAmB,CAC9BZ,MAKAU;IAEA,OAAOnB,OAAOC,oBAAoBQ,OAAOU;AAC3C,EAAC;AAED,eAAeE,iBAAgB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type * as Aglyn from '@aglyn/aglyn';
|
|
18
|
+
import { Timestamp } from '@aglyn/shared-util-timestamp';
|
|
19
|
+
import { type FirestoreDocOptions } from './firebase/firebase-services';
|
|
20
|
+
export declare const useScreenRef: ({ hostId, screenId }: {
|
|
21
|
+
hostId: Aglyn.HostUid;
|
|
22
|
+
screenId: Aglyn.ScreenUid;
|
|
23
|
+
}) => import("@firebase/firestore").DocumentReference<Aglyn.AglynScreen, {
|
|
24
|
+
updatedAt: Timestamp;
|
|
25
|
+
hostId?: Aglyn.HostUid;
|
|
26
|
+
parentId?: Aglyn.ScreenUid;
|
|
27
|
+
slug?: Aglyn.ScreenSlug;
|
|
28
|
+
order?: number;
|
|
29
|
+
versionId?: Aglyn.VersionUid;
|
|
30
|
+
publishSchedule?: Aglyn.PublishSchedule;
|
|
31
|
+
protection?: {
|
|
32
|
+
passwordHash?: string;
|
|
33
|
+
};
|
|
34
|
+
locale?: string;
|
|
35
|
+
localeVariants?: Record<string, Aglyn.ScreenUid>;
|
|
36
|
+
status?: Aglyn.HostScreenStatus;
|
|
37
|
+
createdAt?: import("@aglyn/shared-util-timestamp").ITimestamp;
|
|
38
|
+
publishedAt?: import("@aglyn/shared-util-timestamp").ITimestamp;
|
|
39
|
+
deletedAt?: import("@aglyn/shared-util-timestamp").ITimestamp;
|
|
40
|
+
displayName?: string;
|
|
41
|
+
nameLower?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
seo?: {
|
|
44
|
+
title?: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
breadcrumb?: string;
|
|
47
|
+
image?: string;
|
|
48
|
+
imageWidth?: number;
|
|
49
|
+
imageHeight?: number;
|
|
50
|
+
imageAlt?: string;
|
|
51
|
+
};
|
|
52
|
+
schedule?: {
|
|
53
|
+
startAt?: import("@aglyn/shared-util-timestamp").ITimestamp;
|
|
54
|
+
endAt?: import("@aglyn/shared-util-timestamp").ITimestamp;
|
|
55
|
+
next?: Aglyn.VersionUid;
|
|
56
|
+
previous?: Aglyn.VersionUid;
|
|
57
|
+
};
|
|
58
|
+
visibility?: Aglyn.HostScreenVisibility;
|
|
59
|
+
layoutId?: Aglyn.LayoutUid;
|
|
60
|
+
}>;
|
|
61
|
+
export declare const useScreen: (data: {
|
|
62
|
+
hostId: Aglyn.HostUid;
|
|
63
|
+
screenId: Aglyn.ScreenUid;
|
|
64
|
+
}, options?: FirestoreDocOptions<Aglyn.AglynScreen>) => {
|
|
65
|
+
doc: import("./firebase/firebase-services").ObservableStatus<Aglyn.AglynScreen>;
|
|
66
|
+
setDoc: import("./helpers/use-modify-doc-callback").ModifyDocCallback<Aglyn.AglynScreen>;
|
|
67
|
+
};
|
|
68
|
+
export default useScreen;
|