diff --git a/permix/src/solid/components.test.tsx b/permix/src/solid/components.test.tsx
index bbe8f56a..484a98e7 100644
--- a/permix/src/solid/components.test.tsx
+++ b/permix/src/solid/components.test.tsx
@@ -41,6 +41,43 @@ describe('components', () => {
expect(container.firstChild).toHaveTextContent('true')
})
+ it('uses dehydrated rules on the first render', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const TestComponent = () => {
+ const { check, isReady } = usePermix(permixClient)
+ return (
+
+ {check('post.create').toString()}:{isReady().toString()}
+
+ )
+ }
+
+ const { container } = render(() => (
+
+
+
+
+
+ ))
+
+ expect(container.firstChild).toHaveTextContent('true:false')
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/solid/components.tsx b/permix/src/solid/components.tsx
index cf12e87a..1d5b2cd5 100644
--- a/permix/src/solid/components.tsx
+++ b/permix/src/solid/components.tsx
@@ -1,10 +1,5 @@
import type { JSX } from 'solid-js'
-import {
- createEffect,
- createMemo,
- createRenderEffect,
- onCleanup,
-} from 'solid-js'
+import { createMemo, createRenderEffect, onCleanup } from 'solid-js'
import { createStore } from 'solid-js/store'
import type {
@@ -33,7 +28,7 @@ export function PermixProvider(props: {
rules: props.permix.getRules(),
})
- createEffect(() => {
+ createRenderEffect(() => {
const setup = props.permix.hook('setup', () => {
setContext('rules', props.permix.getRules())
})
diff --git a/permix/src/solid/hooks.test.tsx b/permix/src/solid/hooks.test.tsx
index fb84133b..ab1b63f6 100644
--- a/permix/src/solid/hooks.test.tsx
+++ b/permix/src/solid/hooks.test.tsx
@@ -30,6 +30,35 @@ describe('permix solid', () => {
expect(result.check('post.read')).toBe(false)
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ },
+ })
+
+ const TestComponent = () => {
+ const { isReady, check } = usePermix(permix)
+ return (
+
+ {isReady().toString()}:{check('post.create').toString()}
+
+ )
+ }
+
+ const { container } = render(() => , {
+ wrapper: (props) => (
+ {props.children}
+ ),
+ })
+
+ expect(container.firstChild).toHaveTextContent('true:true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']
diff --git a/permix/src/svelte/__fixtures__/HydrateConsumer.svelte b/permix/src/svelte/__fixtures__/HydrateConsumer.svelte
index d00a05bb..90e92694 100644
--- a/permix/src/svelte/__fixtures__/HydrateConsumer.svelte
+++ b/permix/src/svelte/__fixtures__/HydrateConsumer.svelte
@@ -9,3 +9,4 @@ const permissions = usePermix(permix)
{permissions.check('post.create').toString()}
+{permissions.isReady.toString()}
diff --git a/permix/src/svelte/components.test.ts b/permix/src/svelte/components.test.ts
index a999f398..366b15a1 100644
--- a/permix/src/svelte/components.test.ts
+++ b/permix/src/svelte/components.test.ts
@@ -35,6 +35,31 @@ describe('components', () => {
expect(getByTestId('create')).toHaveTextContent('true')
})
+ it('uses dehydrated rules on the first render', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const { getByTestId } = render(HydrateApp, {
+ props: { permix: permixClient, state: dehydrated },
+ })
+
+ expect(getByTestId('create')).toHaveTextContent('true')
+ expect(getByTestId('ready')).toHaveTextContent('false')
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/svelte/context.svelte.ts b/permix/src/svelte/context.svelte.ts
index 14ab311f..bf98e819 100644
--- a/permix/src/svelte/context.svelte.ts
+++ b/permix/src/svelte/context.svelte.ts
@@ -1,4 +1,4 @@
-import { getContext, setContext } from 'svelte'
+import { getContext, onDestroy, setContext } from 'svelte'
import type { Definition, Permix, Rules } from '../core'
import { createCheck } from '../core'
@@ -27,18 +27,16 @@ export function providePermix(permix: Permix): void {
setContext(PERMIX_CONTEXT_KEY, context)
- $effect(() => {
- const setup = permix.hook('setup', () => {
- context.rules = permix.getRules()
- })
- const ready = permix.hook('ready', () => {
- context.isReady = permix.isReady()
- })
+ const setup = permix.hook('setup', () => {
+ context.rules = permix.getRules()
+ })
+ const ready = permix.hook('ready', () => {
+ context.isReady = permix.isReady()
+ })
- return () => {
- setup()
- ready()
- }
+ onDestroy(() => {
+ setup()
+ ready()
})
}
diff --git a/permix/src/svelte/hooks.test.ts b/permix/src/svelte/hooks.test.ts
index 8e37ca67..88f53366 100644
--- a/permix/src/svelte/hooks.test.ts
+++ b/permix/src/svelte/hooks.test.ts
@@ -25,6 +25,24 @@ describe('permix svelte', () => {
expect(getByTestId('read')).toHaveTextContent('false')
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: [{ name: 'create'; type: { id: string } }, 'read']
+ }>()
+
+ permix.setup({
+ post: {
+ create: () => true,
+ read: true,
+ },
+ })
+
+ const { getByTestId } = render(HookApp, { props: { permix } })
+
+ expect(getByTestId('ready')).toHaveTextContent('true')
+ expect(getByTestId('read')).toHaveTextContent('true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']
diff --git a/permix/src/vue/components.test.ts b/permix/src/vue/components.test.ts
index 83508d29..769e61e6 100644
--- a/permix/src/vue/components.test.ts
+++ b/permix/src/vue/components.test.ts
@@ -50,6 +50,48 @@ describe('components', () => {
expect(wrapper.text()).toBe('true')
})
+ it('uses dehydrated rules on the first render', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const TestComponent = {
+ template: "{{ check('post.create') }}:{{ isReady }}
",
+ setup() {
+ const { check, isReady } = usePermix(permixClient)
+ return { check, isReady }
+ },
+ }
+
+ const wrapper = mount({
+ template: `
+
+
+
+
+
+ `,
+ components: { PermixProvider, PermixHydrate, TestComponent },
+ setup() {
+ return { permix: permixClient, dehydrated }
+ },
+ })
+
+ expect(wrapper.text()).toBe('true:false')
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/vue/composables.test.ts b/permix/src/vue/composables.test.ts
index e9807aaf..341fb560 100644
--- a/permix/src/vue/composables.test.ts
+++ b/permix/src/vue/composables.test.ts
@@ -56,6 +56,30 @@ describe('composables', () => {
expect(wrapper.get('[data-testid="read"]').text()).toBe('false')
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ },
+ })
+
+ const TestWrapper = defineComponent({
+ template: '{{ isReady }}:{{ check("post.create") }}
',
+ setup() {
+ const { check, isReady } = usePermix(permix)
+ return { check, isReady }
+ },
+ })
+
+ const wrapper = mountWithPermix(TestWrapper, permix)
+
+ expect(wrapper.get('div').text()).toBe('true:true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']