@fayz-ai/plugin-automations 0.15.3 → 0.15.4
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/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const MIGRATION_000_BASELINE = "-- ============================================================================\n-- plugins/plugin-automations/migrations/000_baseline.sql \u2014 what installing this provisions, as one file.\n--\n-- GENERATED by scripts/emit-unit-baselines.mjs (#338). Do not edit by hand:\n-- change the schema, regenerate, and let scripts/check-chain-equivalence.mjs\n-- prove the result still matches.\n--\n-- This replaced 4 migration files. They are kept, unapplied, in\n-- migrations.archive/ \u2014 a year of decisions is worth reading even when it is no\n-- longer worth replaying.\n--\n-- Object order is fixed rather than dependency-sorted: schemas, types,\n-- sequences, tables, defaults, functions, constraints, views, indexes, foreign\n-- keys, triggers, row security, policies, grants. Dependencies BETWEEN units\n-- are carried by the order packages/db/chain.json declares.\n-- ============================================================================\n\n-- A LANGUAGE sql function binds its body at CREATE time, and 570 functions\n-- sorted by name are not sorted by who calls whom. This is what lets them load\n-- in any order; every body is still compiled on first call, so a genuinely\n-- broken reference surfaces then rather than never. Session-scoped.\nSET check_function_bodies = false;\n\n\n\n-- \u2500\u2500 default privileges: reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- Cleared BEFORE anything is created, so every object below gets the ACL the\n-- dump describes rather than the image's defaults on top of it. Restored at\n-- the end of this file.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\n\n-- \u2500\u2500 table \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE TABLE public.plg_automations_provider_templates (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid,\n event_key text NOT NULL,\n channel text DEFAULT 'whatsapp'::text NOT NULL,\n provider text DEFAULT 'tyxter'::text NOT NULL,\n provider_template_id text,\n template_name text NOT NULL,\n language text DEFAULT 'pt_BR'::text NOT NULL,\n variable_map jsonb DEFAULT '[]'::jsonb NOT NULL,\n actions_map jsonb DEFAULT '[]'::jsonb NOT NULL,\n status text DEFAULT 'draft'::text NOT NULL,\n rejection_reason text,\n body_snapshot text,\n submitted_at timestamp with time zone,\n approved_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_automations_provider_templates_status_check CHECK ((status = ANY (ARRAY['draft'::text, 'submitted'::text, 'approved'::text, 'rejected'::text, 'paused'::text, 'disabled'::text])))\n);\n\nCREATE TABLE public.plg_automations_templates (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n event_key text NOT NULL,\n channel text DEFAULT 'email'::text NOT NULL,\n enabled boolean,\n subject text,\n body text,\n name text,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n actions jsonb\n);\n\n\n-- \u2500\u2500 function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE FUNCTION public.automations_set_updated_at() RETURNS trigger\n LANGUAGE plpgsql\n AS $$\nBEGIN\n NEW.updated_at = now();\n RETURN NEW;\nEND $$;\n\n\n-- \u2500\u2500 constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE ONLY public.plg_automations_provider_templates\n ADD CONSTRAINT plg_automations_provider_templates_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_automations_templates\n ADD CONSTRAINT plg_automations_templates_pkey PRIMARY KEY (id);\n\n\n-- \u2500\u2500 index \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE INDEX idx_plg_automations_provider_templates_lookup ON public.plg_automations_provider_templates USING btree (event_key, channel, provider, status);\n\nCREATE INDEX plg_automations_templates_tenant_idx ON public.plg_automations_templates USING btree (tenant_id);\n\nCREATE UNIQUE INDEX plg_automations_templates_unique_idx ON public.plg_automations_templates USING btree (tenant_id, event_key, channel);\n\nCREATE UNIQUE INDEX uq_plg_automations_provider_templates_owner ON public.plg_automations_provider_templates USING btree (COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::uuid), event_key, channel, provider);\n\nCREATE UNIQUE INDEX uq_plg_automations_provider_templates_provider_id ON public.plg_automations_provider_templates USING btree (provider, provider_template_id) WHERE (provider_template_id IS NOT NULL);\n\n\n-- \u2500\u2500 fk constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE ONLY public.plg_automations_provider_templates\n ADD CONSTRAINT plg_automations_provider_templates_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\n\n-- \u2500\u2500 trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE TRIGGER plg_automations_provider_templates_set_updated_at BEFORE UPDATE ON public.plg_automations_provider_templates FOR EACH ROW EXECUTE FUNCTION public.automations_set_updated_at();\n\nCREATE TRIGGER plg_automations_templates_set_updated_at BEFORE UPDATE ON public.plg_automations_templates FOR EACH ROW EXECUTE FUNCTION public.automations_set_updated_at();\n\n\n-- \u2500\u2500 row security \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE public.plg_automations_provider_templates ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_automations_templates ENABLE ROW LEVEL SECURITY;\n\n\n-- \u2500\u2500 policy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE POLICY plg_automations_provider_templates_select ON public.plg_automations_provider_templates FOR SELECT TO authenticated USING (((tenant_id IS NULL) OR (tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids))));\n\nCREATE POLICY plg_automations_templates_member_all ON public.plg_automations_templates TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids))) WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\n\n-- \u2500\u2500 acl \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nGRANT ALL ON FUNCTION public.automations_set_updated_at() TO authenticated;\nGRANT ALL ON FUNCTION public.automations_set_updated_at() TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_automations_provider_templates TO anon;\nGRANT ALL ON TABLE public.plg_automations_provider_templates TO authenticated;\nGRANT ALL ON TABLE public.plg_automations_provider_templates TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_automations_templates TO anon;\nGRANT ALL ON TABLE public.plg_automations_templates TO authenticated;\nGRANT ALL ON TABLE public.plg_automations_templates TO service_role;\n\n\n-- \u2500\u2500 comment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCOMMENT ON COLUMN public.plg_automations_templates.actions IS 'Channel-neutral message actions: [{id: confirm|cancel|pay, label, kind: reply|payment}]. NULL = the catalog default; [] = deliberately no buttons.';\n\n\n-- \u2500\u2500 default privileges: restore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- Put back, so the NEXT thing installed inherits what the chain had.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEND $dp$;\n";
|
|
2
2
|
export declare const MIGRATION_001_THE_FLOW_HAS_STEPS = "-- 001_the_flow_has_steps.sql\n--\n-- A CADENCE: the same lead touched on day 0, day 2 and day 5, by itself.\n--\n-- This plugin's README has promised \"multi-step sequences, and enrollment\n-- stats\" since M1, and `AutomationsList.tsx` says plainly what shipped instead:\n-- \"um fluxo de um passo \u00E9 o texto com o gatilho escrito na frente\". One row per\n-- (event, channel). Nothing waits, nothing remembers where a person got to, and\n-- nothing stops when they reply.\n--\n-- WHAT THIS DOES NOT BUILD, BECAUSE IT ALREADY EXISTS.\n--\n-- * The clock. `fayz_event_runner` is a pg_cron job that already calls\n-- `event_runner_tick(200)` every minute. This adds a fifth call to it\n-- (packages/db 046), guarded like the other four.\n-- * The delay. `notifications_enqueue(..., p_scheduled_for, p_dedupe_key)`\n-- already means \"send this at time T, and not twice\". A cadence step is a\n-- row in that queue with a computed `scheduled_for`.\n-- * Consent, channel preference and quiet-channel fallback \u2014 all inside\n-- `notifications_resolve_channel`, which plugin-notifications 002 just\n-- taught to read the opt-out correctly. A cadence that dodged the queue\n-- would dodge consent with it, which is the whole reason it does not.\n-- * The claim-and-retry shape. `marketing_run_due` / `marketing_campaign_run`\n-- already solve \"advance a set of enrolled people on a tick\" with\n-- `FOR UPDATE SKIP LOCKED`. `automations_flows_run_due` is that function\n-- with a step pointer.\n--\n-- THE THREE DECISIONS WORTH ARGUING WITH.\n--\n-- 1. THE DELAY IS AN OFFSET FROM THE PREVIOUS STEP, NOT FROM ENROLMENT.\n-- \"Two days after the call\" is how a salesperson describes a cadence, and it\n-- survives inserting a step in the middle. Absolute offsets from day 0 mean\n-- every later step silently shifts when you add one.\n--\n-- 2. THE ENROLMENT CARRIES THE STEP POINTER, AND THE DEDUPE KEY CARRIES THE\n-- POSITION. `flow:<enrolment>:<position>` means replaying a tick, or two\n-- workers taking the same row, cannot send a step twice \u2014\n-- `notifications_enqueue` refuses a repeated `dedupe_key` before it writes\n-- anything. The SKIP LOCKED claim is the first defence; this is the one that\n-- survives a crash between the enqueue and the pointer update.\n--\n-- 3. A FLOW IS BORN INACTIVE. `is_active` defaults false. A cadence that starts\n-- sending the moment somebody saves a draft is a cadence that sends a draft.\n--\n-- WHAT IS STILL MISSING AFTER THIS, AND IS NOT PRETENDED OTHERWISE: e-mail and\n-- SMS still resolve to `notifications_send_to_log`, which records and sends\n-- nothing. A cadence on those channels will enqueue, drain, and deliver to a\n-- log row. WhatsApp reaches a provider; in-app reaches the bell.\n\nSET check_function_bodies = false;\n\n-- \u2500\u2500 tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE TABLE IF NOT EXISTS public.plg_automations_flows (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n unit_id uuid,\n name text NOT NULL,\n description text,\n -- The fact that puts somebody in. Names an event from the aggregated plugin\n -- catalog (`crm.lead_created`, `crm.stage_changed`, \u2026); it is text and not a\n -- foreign key because the catalog is assembled at runtime from whichever\n -- plugins an app mounts.\n entry_event text NOT NULL,\n -- The facts that take somebody out. Empty means the flow only ends by\n -- running out of steps.\n exit_events text[] DEFAULT '{}'::text[] NOT NULL,\n is_active boolean DEFAULT false NOT NULL,\n owner_id uuid,\n created_at timestamptz DEFAULT now() NOT NULL,\n updated_at timestamptz DEFAULT now() NOT NULL,\n CONSTRAINT plg_automations_flows_pkey PRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS public.plg_automations_flow_steps (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n flow_id uuid NOT NULL,\n -- Sparse on purpose (10, 20, 30\u2026): inserting between two steps should not\n -- renumber the ones after it, and the enrolment's pointer names a position.\n position integer NOT NULL,\n -- Minutes to wait AFTER THE PREVIOUS STEP. The first step's delay is measured\n -- from enrolment, so 0 means \"right away\".\n delay_minutes integer DEFAULT 0 NOT NULL,\n kind text DEFAULT 'message' NOT NULL,\n -- kind='message': which template, and optionally force a channel. NULL\n -- channel lets notifications_resolve_channel pick, which is what honours the\n -- recipient's preference.\n template_key text,\n channel text,\n -- kind='task': what the human is asked to do, and by when.\n task_title text,\n task_due_days integer,\n created_at timestamptz DEFAULT now() NOT NULL,\n updated_at timestamptz DEFAULT now() NOT NULL,\n CONSTRAINT plg_automations_flow_steps_pkey PRIMARY KEY (id),\n CONSTRAINT plg_automations_flow_steps_kind_check\n CHECK (kind = ANY (ARRAY['message'::text, 'task'::text])),\n CONSTRAINT plg_automations_flow_steps_delay_check CHECK (delay_minutes >= 0),\n -- A message step with no template has nothing to send; a task step with no\n -- title gives the human nothing to read. Either is a step that would fail at\n -- 3am rather than at save time.\n CONSTRAINT plg_automations_flow_steps_payload_check CHECK (\n (kind = 'message' AND template_key IS NOT NULL)\n OR (kind = 'task' AND task_title IS NOT NULL)\n )\n);\n\nCREATE TABLE IF NOT EXISTS public.plg_automations_enrollments (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n flow_id uuid NOT NULL,\n person_id uuid NOT NULL,\n -- What put them here, for the timeline: ('lead', <id>) or ('deal', <id>).\n subject_type text,\n subject_id uuid,\n status text DEFAULT 'active' NOT NULL,\n -- The position of the step that runs NEXT. NULL once nothing is left.\n current_position integer,\n next_run_at timestamptz DEFAULT now() NOT NULL,\n entered_at timestamptz DEFAULT now() NOT NULL,\n exited_at timestamptz,\n exit_reason text,\n created_at timestamptz DEFAULT now() NOT NULL,\n updated_at timestamptz DEFAULT now() NOT NULL,\n CONSTRAINT plg_automations_enrollments_pkey PRIMARY KEY (id),\n CONSTRAINT plg_automations_enrollments_status_check\n CHECK (status = ANY (ARRAY['active'::text, 'completed'::text, 'exited'::text]))\n);\n\n-- \u2500\u2500 constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE public.plg_automations_flows\n DROP CONSTRAINT IF EXISTS plg_automations_flows_tenant_fkey;\nALTER TABLE public.plg_automations_flows\n ADD CONSTRAINT plg_automations_flows_tenant_fkey\n FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE public.plg_automations_flow_steps\n DROP CONSTRAINT IF EXISTS plg_automations_flow_steps_flow_fkey;\nALTER TABLE public.plg_automations_flow_steps\n ADD CONSTRAINT plg_automations_flow_steps_flow_fkey\n FOREIGN KEY (flow_id) REFERENCES public.plg_automations_flows(id) ON DELETE CASCADE;\n\nALTER TABLE public.plg_automations_enrollments\n DROP CONSTRAINT IF EXISTS plg_automations_enrollments_flow_fkey;\nALTER TABLE public.plg_automations_enrollments\n ADD CONSTRAINT plg_automations_enrollments_flow_fkey\n FOREIGN KEY (flow_id) REFERENCES public.plg_automations_flows(id) ON DELETE CASCADE;\n\n-- \u2500\u2500 index \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE UNIQUE INDEX IF NOT EXISTS plg_automations_flow_steps_position_uq\n ON public.plg_automations_flow_steps (flow_id, position);\nCREATE INDEX IF NOT EXISTS plg_automations_flows_tenant_idx\n ON public.plg_automations_flows (tenant_id);\nCREATE INDEX IF NOT EXISTS plg_automations_flows_entry_idx\n ON public.plg_automations_flows (tenant_id, entry_event) WHERE is_active;\n\n-- One live enrolment per person per flow. Re-entering a cadence somebody is\n-- already in would double every remaining message.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_automations_enrollments_live_uq\n ON public.plg_automations_enrollments (flow_id, person_id) WHERE status = 'active';\n-- The claim index: the runner only ever asks for due, active rows.\nCREATE INDEX IF NOT EXISTS plg_automations_enrollments_due_idx\n ON public.plg_automations_enrollments (next_run_at) WHERE status = 'active';\nCREATE INDEX IF NOT EXISTS plg_automations_enrollments_person_idx\n ON public.plg_automations_enrollments (tenant_id, person_id);\n\n-- \u2500\u2500 row security \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n-- ENABLE **and** FORCE. Without FORCE the table owner \u2014 which is the role that\n-- runs migrations and every SECURITY DEFINER function here \u2014 bypasses these\n-- policies entirely, and `packages/db/test/pgtap/900_verification/001_posture.sql`\n-- (N14b) fails the whole pool for it.\nALTER TABLE public.plg_automations_flows ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_automations_flows FORCE ROW LEVEL SECURITY;\nALTER TABLE public.plg_automations_flow_steps ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_automations_flow_steps FORCE ROW LEVEL SECURITY;\nALTER TABLE public.plg_automations_enrollments ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_automations_enrollments FORCE ROW LEVEL SECURITY;\n\nDROP POLICY IF EXISTS plg_automations_flows_authz_select ON public.plg_automations_flows;\nCREATE POLICY plg_automations_flows_authz_select ON public.plg_automations_flows\n FOR SELECT TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.read', NULL::uuid)));\n\nDROP POLICY IF EXISTS plg_automations_flows_authz_insert ON public.plg_automations_flows;\nCREATE POLICY plg_automations_flows_authz_insert ON public.plg_automations_flows\n FOR INSERT TO authenticated WITH CHECK (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)));\n\nDROP POLICY IF EXISTS plg_automations_flows_authz_update ON public.plg_automations_flows;\nCREATE POLICY plg_automations_flows_authz_update ON public.plg_automations_flows\n FOR UPDATE TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)))\n WITH CHECK (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)));\n\nDROP POLICY IF EXISTS plg_automations_flows_authz_delete ON public.plg_automations_flows;\nCREATE POLICY plg_automations_flows_authz_delete ON public.plg_automations_flows\n FOR DELETE TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)));\n\nDROP POLICY IF EXISTS plg_automations_flow_steps_authz_select ON public.plg_automations_flow_steps;\nCREATE POLICY plg_automations_flow_steps_authz_select ON public.plg_automations_flow_steps\n FOR SELECT TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.read', NULL::uuid)));\n\nDROP POLICY IF EXISTS plg_automations_flow_steps_authz_insert ON public.plg_automations_flow_steps;\n-- The parent flow is checked, not just the stamped tenant. A tenant-only\n-- WITH CHECK lets a caller stamp their OWN tenant_id on a step and hang it off\n-- somebody else's flow \u2014 and the engine looks steps up by flow_id alone, so the\n-- injected step runs in that tenant's cadence.\nCREATE POLICY plg_automations_flow_steps_authz_insert ON public.plg_automations_flow_steps\n FOR INSERT TO authenticated WITH CHECK (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid))\n AND EXISTS (SELECT 1 FROM public.plg_automations_flows f\n WHERE f.id = flow_id AND f.tenant_id = (SELECT app.current_tenant_id())));\n\nDROP POLICY IF EXISTS plg_automations_flow_steps_authz_update ON public.plg_automations_flow_steps;\nCREATE POLICY plg_automations_flow_steps_authz_update ON public.plg_automations_flow_steps\n FOR UPDATE TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)))\n WITH CHECK (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)));\n\nDROP POLICY IF EXISTS plg_automations_flow_steps_authz_delete ON public.plg_automations_flow_steps;\nCREATE POLICY plg_automations_flow_steps_authz_delete ON public.plg_automations_flow_steps\n FOR DELETE TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.edit', NULL::uuid)));\n\n-- Enrolments are READ-ONLY to the operator. They are written by the engine, and\n-- a hand-edited step pointer is a cadence nobody can explain afterwards.\n-- Leaving a flow is `automations_flow_exit`, not an UPDATE.\nDROP POLICY IF EXISTS plg_automations_enrollments_authz_select ON public.plg_automations_enrollments;\nCREATE POLICY plg_automations_enrollments_authz_select ON public.plg_automations_enrollments\n FOR SELECT TO authenticated USING (\n tenant_id = (SELECT app.current_tenant_id())\n AND (SELECT app.has_permission('automations.read', NULL::uuid)));\n\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_automations_flows TO authenticated;\nGRANT ALL ON public.plg_automations_flows TO service_role;\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_automations_flow_steps TO authenticated;\nGRANT ALL ON public.plg_automations_flow_steps TO service_role;\nGRANT SELECT ON public.plg_automations_enrollments TO authenticated;\nGRANT ALL ON public.plg_automations_enrollments TO service_role;\n\nINSERT INTO app.scaffold_registry\n (table_schema, table_name, resource_type, plugin, owner_scoping, mode, previous_policies, audit_hook, scope, distributable)\nVALUES\n ('public', 'plg_automations_flows', 'automations.flow', 'automations', false, 'enforce', ARRAY[]::text[], false, 'unit_optional', false),\n ('public', 'plg_automations_flow_steps', 'automations.flow_step', 'automations', false, 'enforce', ARRAY[]::text[], false, 'tenant', false),\n ('public', 'plg_automations_enrollments', 'automations.enrollment', 'automations', false, 'enforce', ARRAY[]::text[], false, 'tenant', false)\nON CONFLICT DO NOTHING;\n\n-- \u2500\u2500 function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n-- Put somebody into a flow. Returns the enrolment, or NULL when the flow is\n-- inactive, has no steps, or the person is already in it.\nCREATE OR REPLACE FUNCTION public.automations_flow_enroll(\n p_tenant_id uuid, p_flow_id uuid, p_person_id uuid,\n p_subject_type text DEFAULT NULL, p_subject_id uuid DEFAULT NULL\n) RETURNS uuid\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE v_first record; v_id uuid;\nBEGIN\n SELECT s.position, s.delay_minutes INTO v_first\n FROM public.plg_automations_flow_steps s\n JOIN public.plg_automations_flows f ON f.id = s.flow_id\n WHERE s.flow_id = p_flow_id AND f.tenant_id = p_tenant_id AND f.is_active\n ORDER BY s.position\n LIMIT 1;\n\n -- An inactive flow, or one with no steps, enrols nobody. Silently: this is\n -- called from an event handler, and raising would fail the whole delivery.\n IF v_first.position IS NULL THEN RETURN NULL; END IF;\n\n INSERT INTO public.plg_automations_enrollments\n (tenant_id, flow_id, person_id, subject_type, subject_id, current_position, next_run_at)\n VALUES\n (p_tenant_id, p_flow_id, p_person_id, p_subject_type, p_subject_id,\n v_first.position, now() + make_interval(mins => v_first.delay_minutes))\n ON CONFLICT DO NOTHING\n RETURNING id INTO v_id;\n\n RETURN v_id;\nEND $$;\n\n-- Take somebody out of every flow they are in. The reason is kept, because\n-- \"left because they replied\" and \"left because the deal was lost\" are the two\n-- halves of whether a cadence works.\nCREATE OR REPLACE FUNCTION public.automations_flow_exit(\n p_tenant_id uuid, p_person_id uuid, p_reason text, p_flow_ids uuid[] DEFAULT NULL\n) RETURNS integer\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE v_n integer; v_gone uuid[];\nBEGIN\n -- SCOPED TO THE FLOWS THAT ASKED. `p_flow_ids` NULL still means \"every flow\",\n -- which is what a manual \"take this person out\" wants; the event handler\n -- passes the flows whose own `exit_events` name the fact.\n --\n -- Without the scope, one flow declaring `crm.deal_won` pulled the person out\n -- of EVERY flow they were in and cancelled every queued step \u2014 including a\n -- flow that declared no exits at all, whose comment says it \"only ends by\n -- running out of steps\". Flow-level exit_events were meaningless the moment a\n -- second flow existed.\n WITH gone AS (\n UPDATE public.plg_automations_enrollments\n SET status = 'exited', exited_at = now(), exit_reason = p_reason,\n current_position = NULL, updated_at = now()\n WHERE tenant_id = p_tenant_id AND person_id = p_person_id AND status = 'active'\n AND (p_flow_ids IS NULL OR flow_id = ANY (p_flow_ids))\n RETURNING id\n )\n SELECT count(*)::integer, array_agg(id) INTO v_n, v_gone FROM gone;\n\n -- A step already in the queue but not yet sent must not go out after the\n -- person left. `cancelled` was in the status CHECK from the beginning and\n -- nothing ever wrote it; this is what it was for.\n --\n -- Keyed by the ENROLMENTS that just left, not by `flow:%` \u2014 the wide pattern\n -- cancelled the pending steps of flows the person is still in.\n IF v_n > 0 AND to_regclass('public.plg_notifications_queue') IS NOT NULL THEN\n UPDATE public.plg_notifications_queue q\n SET status = 'cancelled', last_error = 'left the flow: ' || p_reason\n WHERE q.tenant_id = p_tenant_id\n AND q.person_id = p_person_id\n AND q.status = 'pending'\n AND split_part(q.dedupe_key, ':', 1) = 'flow'\n AND split_part(q.dedupe_key, ':', 2) = ANY (SELECT unnest(v_gone)::text);\n END IF;\n\n RETURN v_n;\nEND $$;\n\n-- The tick. Claims due enrolments, runs ONE step each, moves the pointer.\nCREATE OR REPLACE FUNCTION public.automations_flows_run_due(p_limit integer DEFAULT 50)\nRETURNS jsonb\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n e record; v_step record; v_next record; v_error text;\n v_ran integer := 0; v_done integer := 0; v_failed integer := 0;\nBEGIN\n -- JOINED to the flow, and `f.is_active` checked here rather than only in\n -- `automations_flow_enroll`. Switching a flow off used to stop new entrants\n -- and nothing else: everybody already mid-cadence kept receiving messages\n -- from a flow the operator had just turned off, which is the opposite of what\n -- the switch promises.\n FOR e IN\n SELECT en.* FROM public.plg_automations_enrollments en\n JOIN public.plg_automations_flows f ON f.id = en.flow_id AND f.is_active\n WHERE en.status = 'active' AND en.next_run_at <= now()\n ORDER BY en.next_run_at\n LIMIT greatest(p_limit, 0)\n FOR UPDATE OF en SKIP LOCKED\n LOOP\n SELECT * INTO v_step FROM public.plg_automations_flow_steps s\n WHERE s.flow_id = e.flow_id AND s.position = e.current_position;\n\n -- The step was deleted while somebody stood on it. Move on rather than\n -- spinning on a pointer that will never resolve.\n IF v_step.id IS NULL THEN\n SELECT * INTO v_next FROM public.plg_automations_flow_steps s\n WHERE s.flow_id = e.flow_id AND s.position > coalesce(e.current_position, -1)\n ORDER BY s.position LIMIT 1;\n ELSE\n BEGIN\n IF v_step.kind = 'message' THEN\n PERFORM public.notifications_enqueue(\n e.tenant_id, v_step.template_key, e.person_id,\n jsonb_build_object('flow_id', e.flow_id, 'enrollment_id', e.id, 'step', v_step.position),\n 'flow:' || e.id::text || ':' || v_step.position::text,\n now(),\n v_step.channel\n );\n ELSIF v_step.kind = 'task' AND to_regclass('public.tasks') IS NOT NULL THEN\n -- `tasks_source_uniq (tenant_id, source, source_key)` makes this\n -- idempotent for free \u2014 the same step cannot raise two tasks.\n INSERT INTO public.tasks\n (tenant_id, title, description, status, due_date, source, source_key)\n VALUES\n (e.tenant_id, v_step.task_title,\n 'Automation flow step ' || v_step.position::text,\n 'todo',\n CASE WHEN v_step.task_due_days IS NOT NULL\n THEN (now() + make_interval(days => v_step.task_due_days))::date END,\n 'automation:flow', e.id::text || ':' || v_step.position::text)\n ON CONFLICT DO NOTHING;\n END IF;\n v_ran := v_ran + 1;\n EXCEPTION WHEN OTHERS THEN\n -- One bad step must not stop the tick for everybody else, so the\n -- enrolment still leaves this step: a template that no longer exists\n -- would otherwise wedge the person forever.\n --\n -- But it does NOT read as a finished cadence. The first cut let the\n -- loop fall through to `completed`, and because a failed step never\n -- produces a queue row, `v_automations_flow_steps` \u2014 which counts queue\n -- statuses \u2014 could not show it either. The screen built to answer\n -- \"where do people stop\" reported the person as having finished.\n v_failed := v_failed + 1;\n v_error := SQLERRM;\n RAISE NOTICE 'automations_flows_run_due: step % of enrolment % failed (%)',\n v_step.position, e.id, SQLERRM;\n END;\n\n SELECT * INTO v_next FROM public.plg_automations_flow_steps s\n WHERE s.flow_id = e.flow_id AND s.position > v_step.position\n ORDER BY s.position LIMIT 1;\n END IF;\n\n IF v_next.id IS NULL THEN\n UPDATE public.plg_automations_enrollments\n SET status = 'completed', current_position = NULL,\n exited_at = now(), updated_at = now(),\n -- A cadence that ended on a step that could not run did not\n -- finish; saying so is the difference between \"nobody answers the\n -- third message\" and \"the third message never left\".\n exit_reason = CASE WHEN v_error IS NOT NULL\n THEN 'last step failed: ' || v_error END\n WHERE id = e.id;\n v_done := v_done + 1;\n ELSE\n UPDATE public.plg_automations_enrollments\n SET current_position = v_next.position,\n next_run_at = now() + make_interval(mins => v_next.delay_minutes),\n updated_at = now()\n WHERE id = e.id;\n END IF;\n\n v_next := NULL; v_step := NULL; v_error := NULL;\n END LOOP;\n\n RETURN jsonb_build_object('steps_ran', v_ran, 'completed', v_done, 'failed', v_failed);\nEND $$;\n\nREVOKE ALL ON FUNCTION public.automations_flow_enroll(uuid, uuid, uuid, text, uuid) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.automations_flow_enroll(uuid, uuid, uuid, text, uuid) TO service_role;\nREVOKE ALL ON FUNCTION public.automations_flow_exit(uuid, uuid, text, uuid[]) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.automations_flow_exit(uuid, uuid, text, uuid[]) TO authenticated;\nGRANT EXECUTE ON FUNCTION public.automations_flow_exit(uuid, uuid, text, uuid[]) TO service_role;\nREVOKE ALL ON FUNCTION public.automations_flows_run_due(integer) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.automations_flows_run_due(integer) TO service_role;\n\n-- \u2500\u2500 the two handlers the event log calls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n-- Who the event is about.\n--\n-- `consume_event_log` calls a handler with `to_jsonb(v_event)` \u2014 the whole\n-- `event_log` ROW. So `tenant_id`, `event_key`, `subject_type` and `subject_id`\n-- are at the top level and everything the emitter chose to send is nested under\n-- `payload`. The first cut read `person_id` at the top level, where it never is:\n-- every delivery answered \"payload carries no tenant, person or event\", was\n-- marked DELIVERED, and the cadence enrolled nobody, ever, with nothing\n-- reporting it.\n--\n-- `subject_id` first, because that is the convention the shipped handlers use\n-- (`plg_notifications_on_fulfillment_completed` reads exactly that), and it is\n-- `text` on the row. The payload fallbacks are for emitters that name the person\n-- there instead.\nCREATE OR REPLACE FUNCTION public.automations_event_person(p_event jsonb)\nRETURNS uuid\nLANGUAGE sql IMMUTABLE\nSET search_path TO ''\nAS $$\n SELECT nullif(coalesce(\n CASE WHEN p_event ->> 'subject_type' IN ('lead', 'person', 'contact')\n THEN p_event ->> 'subject_id' END,\n p_event -> 'payload' ->> 'person_id',\n p_event -> 'payload' ->> 'lead_id',\n p_event -> 'payload' ->> 'contact_id',\n p_event ->> 'person_id',\n p_event ->> 'lead_id'\n ), '')::uuid\n$$;\n\nREVOKE ALL ON FUNCTION public.automations_event_person(jsonb) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.automations_event_person(jsonb) TO service_role;\n\n-- Enrol on the flow's entry event. The payload names the person; a fact that\n-- does not carry one cannot start a cadence.\nCREATE OR REPLACE FUNCTION public.plg_automations_on_entry_event(p_event jsonb)\nRETURNS jsonb\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n v_tenant uuid := nullif(p_event ->> 'tenant_id', '')::uuid;\n v_event text := p_event ->> 'event_key';\n v_person uuid := public.automations_event_person(p_event);\n f record; v_n integer := 0;\nBEGIN\n IF v_tenant IS NULL OR v_person IS NULL OR v_event IS NULL THEN\n RETURN jsonb_build_object('enrolled', 0, 'reason', 'event carries no tenant, person or key');\n END IF;\n\n FOR f IN SELECT id FROM public.plg_automations_flows\n WHERE tenant_id = v_tenant AND entry_event = v_event AND is_active\n LOOP\n IF public.automations_flow_enroll(\n v_tenant, f.id, v_person,\n p_event ->> 'subject_type', nullif(p_event ->> 'subject_id', '')::uuid) IS NOT NULL THEN\n v_n := v_n + 1;\n END IF;\n END LOOP;\n\n RETURN jsonb_build_object('enrolled', v_n);\nEND $$;\n\n-- Leave the flows whose own `exit_events` name this fact.\nCREATE OR REPLACE FUNCTION public.plg_automations_on_exit_event(p_event jsonb)\nRETURNS jsonb\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n v_tenant uuid := nullif(p_event ->> 'tenant_id', '')::uuid;\n v_event text := p_event ->> 'event_key';\n v_person uuid := public.automations_event_person(p_event);\n v_flows uuid[]; v_n integer := 0;\nBEGIN\n IF v_tenant IS NULL OR v_person IS NULL OR v_event IS NULL THEN\n RETURN jsonb_build_object('exited', 0, 'reason', 'event carries no tenant, person or key');\n END IF;\n\n SELECT array_agg(id) INTO v_flows\n FROM public.plg_automations_flows\n WHERE tenant_id = v_tenant AND is_active AND v_event = ANY (exit_events);\n\n IF v_flows IS NOT NULL THEN\n v_n := public.automations_flow_exit(v_tenant, v_person, v_event, v_flows);\n END IF;\n\n RETURN jsonb_build_object('exited', v_n);\nEND $$;\n\nREVOKE ALL ON FUNCTION public.plg_automations_on_entry_event(jsonb) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.plg_automations_on_entry_event(jsonb) TO service_role;\nREVOKE ALL ON FUNCTION public.plg_automations_on_exit_event(jsonb) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.plg_automations_on_exit_event(jsonb) TO service_role;\n\n-- \u2500\u2500 subscription \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n-- The manifest DECLARES the reaction; this is what makes the event log call it.\n-- Both handlers on every CRM fact, because which fact opens a cadence and which\n-- closes it is the operator's choice: the handler looks the flow up and returns\n-- immediately when no flow in that tenant names the event.\n-- Written out one call per line rather than looped over an array. The loop was\n-- shorter and `check-event-contract` could not read it: its registration parser\n-- matches literal arguments, so a FOREACH registering fourteen subscriptions\n-- looked like zero. Same lesson as the manifest side \u2014 the gate reads source,\n-- so the source has to say it.\nSELECT public.register_event_subscription('automations', 'crm.lead_created', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.lead_created', 5);\nSELECT public.register_event_subscription('automations', 'crm.lead_created', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.lead_created', 5);\nSELECT public.register_event_subscription('automations', 'crm.stage_changed', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.stage_changed', 5);\nSELECT public.register_event_subscription('automations', 'crm.stage_changed', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.stage_changed', 5);\nSELECT public.register_event_subscription('automations', 'crm.quote_sent', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.quote_sent', 5);\nSELECT public.register_event_subscription('automations', 'crm.quote_sent', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.quote_sent', 5);\nSELECT public.register_event_subscription('automations', 'crm.quote_approved', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.quote_approved', 5);\nSELECT public.register_event_subscription('automations', 'crm.quote_approved', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.quote_approved', 5);\nSELECT public.register_event_subscription('automations', 'crm.deal_won', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.deal_won', 5);\nSELECT public.register_event_subscription('automations', 'crm.deal_won', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.deal_won', 5);\nSELECT public.register_event_subscription('automations', 'crm.deal_lost', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.deal_lost', 5);\nSELECT public.register_event_subscription('automations', 'crm.deal_lost', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.deal_lost', 5);\nSELECT public.register_event_subscription('automations', 'crm.deal_idle', 'plg_automations_on_entry_event',\n 'Enrol into flows whose entry event is crm.deal_idle', 5);\nSELECT public.register_event_subscription('automations', 'crm.deal_idle', 'plg_automations_on_exit_event',\n 'Leave flows that exit on crm.deal_idle', 5);\n\n-- \u2500\u2500 view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n-- Per-step numbers, which is the question a cadence is judged by: where do\n-- people stop. Aggregating per FLOW would hide that the third message is the\n-- one nobody answers.\nCREATE OR REPLACE VIEW public.v_automations_flow_steps WITH (security_invoker='true') AS\n SELECT\n s.tenant_id, s.flow_id, s.id AS step_id, s.position, s.kind,\n s.template_key, s.channel, s.delay_minutes, s.task_title,\n count(q.id) FILTER (WHERE q.id IS NOT NULL) AS queued,\n count(q.id) FILTER (WHERE q.status = 'sent') AS sent,\n count(q.id) FILTER (WHERE q.status = 'blocked') AS blocked,\n count(q.id) FILTER (WHERE q.status = 'failed') AS failed,\n count(q.id) FILTER (WHERE q.status = 'cancelled') AS cancelled\n FROM public.plg_automations_flow_steps s\n LEFT JOIN public.plg_automations_enrollments e ON e.flow_id = s.flow_id\n LEFT JOIN public.plg_notifications_queue q\n ON q.dedupe_key = 'flow:' || e.id::text || ':' || s.position::text\n GROUP BY s.tenant_id, s.flow_id, s.id, s.position, s.kind,\n s.template_key, s.channel, s.delay_minutes, s.task_title;\n\nGRANT SELECT ON public.v_automations_flow_steps TO authenticated;\nGRANT SELECT ON public.v_automations_flow_steps TO service_role;\n";
|
|
3
|
+
export declare const MIGRATION_002_THE_ENGINE_IS_SERVER_ONLY = "-- 002_the_engine_is_server_only.sql\n--\n-- 001 meant the cadence engine to be service_role only, and wrote\n-- `REVOKE ALL ... FROM PUBLIC`. That does not undo the pool's default\n-- privileges, which grant EXECUTE to `authenticated` on every new function in\n-- public \u2014 so on the live pool any logged-in user of any tenant could enrol a\n-- person into another tenant's flow, exit them, or advance every tenant's\n-- cadences at once, all SECURITY DEFINER with no caller check.\n--\n-- Callers are all server-side: `event_runner_tick` (pg_cron, as postgres)\n-- calls `automations_flows_run_due`, the event log calls the two handlers, and\n-- the handlers call enrol/exit. No screen, edge function or agent tool does.\n-- `automations_flow_exit` was granted to `authenticated` for a manual\n-- \"take this person out\" that was never built; it goes too, and comes back\n-- behind a tenant + permission check when a screen needs it.\n\nREVOKE ALL ON FUNCTION public.automations_flow_enroll(uuid, uuid, uuid, text, uuid) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.automations_flow_enroll(uuid, uuid, uuid, text, uuid) TO service_role;\n\nREVOKE ALL ON FUNCTION public.automations_flow_exit(uuid, uuid, text, uuid[]) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.automations_flow_exit(uuid, uuid, text, uuid[]) TO service_role;\n\nREVOKE ALL ON FUNCTION public.automations_flows_run_due(integer) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.automations_flows_run_due(integer) TO service_role;\n\nREVOKE ALL ON FUNCTION public.plg_automations_on_entry_event(jsonb) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.plg_automations_on_entry_event(jsonb) TO service_role;\n\nREVOKE ALL ON FUNCTION public.plg_automations_on_exit_event(jsonb) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.plg_automations_on_exit_event(jsonb) TO service_role;\n";
|
|
3
4
|
export declare const MIGRATIONS: Array<{
|
|
4
5
|
id: string;
|
|
5
6
|
sql: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,ipsBA8UlC,CAAA;AAED,eAAO,MAAM,gCAAgC,6njCA6mB5C,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,ipsBA8UlC,CAAA;AAED,eAAO,MAAM,gCAAgC,6njCA6mB5C,CAAA;AAED,eAAO,MAAM,uCAAuC,y6DA8BnD,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAIzD,CAAA"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"status": "preview",
|
|
5
5
|
"dependencies": []
|
|
6
6
|
},
|
|
7
|
-
"version": "0.15.
|
|
7
|
+
"version": "0.15.4",
|
|
8
8
|
"description": "[experimental] Fayz SDK — workflows & automations plugin",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"sideEffects": false,
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@tanstack/react-table": "^8.20.0",
|
|
30
|
-
"@fayz-ai/admin": "^0.
|
|
31
|
-
"@fayz-ai/core": "^0.36.
|
|
30
|
+
"@fayz-ai/admin": "^0.37.0",
|
|
31
|
+
"@fayz-ai/core": "^0.36.1",
|
|
32
32
|
"@fayz-ai/sdk": "^0.36.0",
|
|
33
|
-
"@fayz-ai/ui": "^0.
|
|
33
|
+
"@fayz-ai/ui": "^0.37.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/react": "^18.3.0",
|