[{"data":1,"prerenderedAt":6158},["ShallowReactive",2],{"post-creating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite":3},{"id":4,"title":5,"body":6,"canonicalUrl":6141,"cover":6142,"date":6143,"description":6144,"draft":6145,"extension":6146,"hashnodeId":6147,"meta":6148,"navigation":657,"path":6149,"seo":6150,"slug":6151,"stem":6151,"tags":6152,"__hash__":6157},"posts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite.md","Creating 1Password plugin and use it to build an app with Nuxt3, Passage & Appwrite",{"type":7,"value":8,"toc":6122},"minimark",[9,27,33,38,47,56,60,74,81,87,92,116,120,127,130,260,271,276,281,288,363,370,375,388,395,398,401,452,469,472,476,487,492,616,623,669,674,677,784,789,792,925,931,1026,1033,1220,1231,1236,1239,1244,1248,1251,1280,1291,1302,1313,1322,1331,1335,1345,1407,1414,1430,1433,1461,1474,1509,1532,1537,1540,1543,1565,1572,1673,1676,1698,1702,1705,1724,1727,2059,2066,2349,2355,2783,2788,2791,2794,3008,3015,4190,4193,4406,4409,4458,4462,4465,4478,4482,4485,4488,4660,4665,4670,4675,4680,4685,4690,4695,4700,4705,4710,4715,4720,4725,4732,4737,4740,4745,4750,4755,4758,4763,4768,4773,4778,4783,4788,4792,4795,5890,5893,5920,6005,6008,6032,6036,6039,6056,6060,6071,6075,6078,6082,6090,6097,6101,6110,6113,6118],[10,11,12,13,20,21,26],"p",{},"Last year I built an app for my son (and myself). The app idea was very simple, a digital piggy bank tracker integrated with optional auto credit of pocket money. We're actively using ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fmypiggyjar.com",[18],"nofollow","this app"," even now (if interested, you can read the associated article ",[14,22,25],{"href":23,"rel":24},"https:\u002F\u002Frajeev.dev\u002Fnew-years-promise-to-my-son",[18],"here","). But this app had some shortcomings, the biggest one being not able to invite your family members to the app. This app rewrite is intended to fix that shortcoming.",[10,28,29],{},[30,31,32],"em",{},"Tl;dr this article covers a complete rewrite of an existing app using a different technology stack. The article also covers the creation of a 1Password CLI Shell Plugin.",[34,35,37],"h2",{"id":36},"introduction","Introduction",[10,39,40,41,46],{},"To do the rewrite I wanted to use Nuxt3. The reasoning was simple, I've used Nuxt2 in the past, but haven't had the chance to look at Nuxt3 (and Vue3 for that matter). For the database and to process the important database events I decided to use Appwrite. Appwrite has inbuilt authentication but I wanted to try out ",[14,42,45],{"href":43,"rel":44},"https:\u002F\u002Fpassage.1password.com\u002F",[18],"Passage by 1Password",", so this app uses that.",[10,48,49,50,55],{},"Appwrite provides a CLI, and ",[14,51,54],{"href":52,"rel":53},"https:\u002F\u002F1password.com\u002Fdevelopers?utm_source=hashnode&utm_medium=landing-page&utm_campaign=hashnode-hackathon",[18],"1Password"," also has a CLI but both are not integrated. So it seemed logical to first integrate the two and create the Appwrite shell plugin for the 1Password CLI.",[34,57,59],{"id":58},"part-1-creating-a-1password-shell-plugin","Part 1: Creating a 1Password shell plugin",[10,61,62,63,68,69,73],{},"If you go over to the ",[14,64,67],{"href":65,"rel":66},"https:\u002F\u002Fdeveloper.1password.com\u002Fdocs\u002Fcli\u002Fshell-plugins\u002Fcontribute",[18],"1Password docs"," page which mentions how you can contribute and create your own shell plugin, you'll learn that the whole thing is implemented using ",[70,71,72],"code",{},"Go",".",[10,75,76],{},[77,78],"img",{"alt":79,"src":80},"","https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002F3oEjHWzZQaCrZW2aWs\u002Fgiphy.gif",[10,82,83,84,86],{},"I haven't tried going anywhere with ",[70,85,72],{},"😉. But the documentation looked straightforward forward so what is the harm in trying?",[88,89,91],"h3",{"id":90},"setup-the-environment","Setup the environment",[10,93,94,95,100,101,104,105,107,108,111,112,115],{},"I'm not going to rewrite what is already covered in detail in the docs link shared above. Just follow the link and install the needed dependencies. I had installed GNU Make using ",[14,96,99],{"href":97,"rel":98},"https:\u002F\u002Fformulae.brew.sh\u002Fformula\u002Fmake",[18],"homebrew"," which installs it as ",[70,102,103],{},"gmake",". So we'll need to replace make commands with ",[70,106,103],{}," (unless you add a ",[70,109,110],{},"gnubin"," directory to the ",[70,113,114],{},"PATH"," as mentioned in the link).",[88,117,119],{"id":118},"choosing-a-provisioner","Choosing a Provisioner",[10,121,122,123,126],{},"The first decision point comes when you need to choose a ",[70,124,125],{},"provisioner",". As the docs page says, \"Provisioners are in essence hooks that get executed before the executable is run by 1Password CLI, and after the executable exits in case any cleanup is needed.\". So how a third-party CLI authenticates you needs to be configured here. This is not a one size fits all scenario. You need to go and read the said third-party CLI documentation to figure it out. But I had used the appwrite CLI, it needs Email & Password so this reading documentation advice is not for me.",[10,128,129],{},"1Password CLI supports the following provisioners",[131,132,136],"pre",{"className":133,"code":134,"language":135,"meta":79,"style":79},"language-go shiki shiki-themes github-light github-dark","const (\n    APIClientCredentials = sdk.CredentialName(\"API Client Credentials\")\n    APIKey               = sdk.CredentialName(\"API Key\")\n    APIToken             = sdk.CredentialName(\"API Token\")\n    AccessKey            = sdk.CredentialName(\"Access Key\")\n    AccessToken          = sdk.CredentialName(\"Access Token\")\n    AppPassword          = sdk.CredentialName(\"App Password\")\n    AppToken             = sdk.CredentialName(\"App Token\")\n    AuthToken            = sdk.CredentialName(\"Auth Token\")\n    CLIToken             = sdk.CredentialName(\"CLI Token\")\n    Credential           = sdk.CredentialName(\"Credential\")\n    Credentials          = sdk.CredentialName(\"Credentials\")\n    DatabaseCredentials  = sdk.CredentialName(\"Database Credentials\")\n    LoginDetails         = sdk.CredentialName(\"Login Details\")\n    PersonalAPIToken     = sdk.CredentialName(\"Personal API Token\")\n    PersonalAccessToken  = sdk.CredentialName(\"Personal Access Token\")\n    RegistryCredentials  = sdk.CredentialName(\"Registry Credentials\")\n    SecretKey            = sdk.CredentialName(\"Secret Key\")\n    UserLogin            = sdk.CredentialName(\"User Login\")\n)\n","go",[70,137,138,146,152,158,164,170,176,182,188,194,200,206,212,218,224,230,236,242,248,254],{"__ignoreMap":79},[139,140,143],"span",{"class":141,"line":142},"line",1,[139,144,145],{},"const (\n",[139,147,149],{"class":141,"line":148},2,[139,150,151],{},"    APIClientCredentials = sdk.CredentialName(\"API Client Credentials\")\n",[139,153,155],{"class":141,"line":154},3,[139,156,157],{},"    APIKey               = sdk.CredentialName(\"API Key\")\n",[139,159,161],{"class":141,"line":160},4,[139,162,163],{},"    APIToken             = sdk.CredentialName(\"API Token\")\n",[139,165,167],{"class":141,"line":166},5,[139,168,169],{},"    AccessKey            = sdk.CredentialName(\"Access Key\")\n",[139,171,173],{"class":141,"line":172},6,[139,174,175],{},"    AccessToken          = sdk.CredentialName(\"Access Token\")\n",[139,177,179],{"class":141,"line":178},7,[139,180,181],{},"    AppPassword          = sdk.CredentialName(\"App Password\")\n",[139,183,185],{"class":141,"line":184},8,[139,186,187],{},"    AppToken             = sdk.CredentialName(\"App Token\")\n",[139,189,191],{"class":141,"line":190},9,[139,192,193],{},"    AuthToken            = sdk.CredentialName(\"Auth Token\")\n",[139,195,197],{"class":141,"line":196},10,[139,198,199],{},"    CLIToken             = sdk.CredentialName(\"CLI Token\")\n",[139,201,203],{"class":141,"line":202},11,[139,204,205],{},"    Credential           = sdk.CredentialName(\"Credential\")\n",[139,207,209],{"class":141,"line":208},12,[139,210,211],{},"    Credentials          = sdk.CredentialName(\"Credentials\")\n",[139,213,215],{"class":141,"line":214},13,[139,216,217],{},"    DatabaseCredentials  = sdk.CredentialName(\"Database Credentials\")\n",[139,219,221],{"class":141,"line":220},14,[139,222,223],{},"    LoginDetails         = sdk.CredentialName(\"Login Details\")\n",[139,225,227],{"class":141,"line":226},15,[139,228,229],{},"    PersonalAPIToken     = sdk.CredentialName(\"Personal API Token\")\n",[139,231,233],{"class":141,"line":232},16,[139,234,235],{},"    PersonalAccessToken  = sdk.CredentialName(\"Personal Access Token\")\n",[139,237,239],{"class":141,"line":238},17,[139,240,241],{},"    RegistryCredentials  = sdk.CredentialName(\"Registry Credentials\")\n",[139,243,245],{"class":141,"line":244},18,[139,246,247],{},"    SecretKey            = sdk.CredentialName(\"Secret Key\")\n",[139,249,251],{"class":141,"line":250},19,[139,252,253],{},"    UserLogin            = sdk.CredentialName(\"User Login\")\n",[139,255,257],{"class":141,"line":256},20,[139,258,259],{},")\n",[10,261,262,263,266,267,270],{},"Appwrite CLI uses email\u002Fpassword for authentication so, ",[70,264,265],{},"LoginDetails"," and ",[70,268,269],{},"UserLogin"," look like the two promising choices. So I picked one of them. The next step asks for an example credential which was confusing as it didn't say whether this example is for the login or the password. Anyway, after putting some random string there we get the template code generated. And we've got a new problem",[10,272,273],{},[77,274],{"alt":79,"src":275},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F455ad862-10ef-45b9-959b-e7530f934982-4e7e4b8c6d.png",[10,277,278],{},[77,279],{"alt":79,"src":280},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F525ce48a-86fc-4c15-a10b-45cff91c0a41-7117285f7c.png",[10,282,283,284,287],{},"Both of the above choices generate code with errors. These field names are not defined. And also only one ",[70,285,286],{},"fieldname"," is created.",[131,289,291],{"className":133,"code":290,"language":135,"meta":79,"style":79},"Fields: []schema.CredentialField{\n  {\n    Name:                fieldname.Login,\n    MarkdownDescription: \"Login used to authenticate to Appwrite.\",\n    Secret:              true,\n    Composition: &schema.ValueComposition{\n      Length: 21,\n      Charset: schema.Charset{\n        Lowercase: true,\n        Digits:    true,\n      },\n    },\n  },\n},\n",[70,292,293,298,303,308,313,318,323,328,333,338,343,348,353,358],{"__ignoreMap":79},[139,294,295],{"class":141,"line":142},[139,296,297],{},"Fields: []schema.CredentialField{\n",[139,299,300],{"class":141,"line":148},[139,301,302],{},"  {\n",[139,304,305],{"class":141,"line":154},[139,306,307],{},"    Name:                fieldname.Login,\n",[139,309,310],{"class":141,"line":160},[139,311,312],{},"    MarkdownDescription: \"Login used to authenticate to Appwrite.\",\n",[139,314,315],{"class":141,"line":166},[139,316,317],{},"    Secret:              true,\n",[139,319,320],{"class":141,"line":172},[139,321,322],{},"    Composition: &schema.ValueComposition{\n",[139,324,325],{"class":141,"line":178},[139,326,327],{},"      Length: 21,\n",[139,329,330],{"class":141,"line":184},[139,331,332],{},"      Charset: schema.Charset{\n",[139,334,335],{"class":141,"line":190},[139,336,337],{},"        Lowercase: true,\n",[139,339,340],{"class":141,"line":196},[139,341,342],{},"        Digits:    true,\n",[139,344,345],{"class":141,"line":202},[139,346,347],{},"      },\n",[139,349,350],{"class":141,"line":208},[139,351,352],{},"    },\n",[139,354,355],{"class":141,"line":214},[139,356,357],{},"  },\n",[139,359,360],{"class":141,"line":220},[139,361,362],{},"},\n",[10,364,365,366,369],{},"After defining the missing fieldsname, and adding a new field (password) in the array, tried validating, building and initing the plugin (using ",[70,367,368],{},"op plugin init appwrite","). It asked for the login and password and created an entry in the 1Password app. The moment of truth is here",[10,371,372],{},[77,373],{"alt":79,"src":374},"https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002FKJBgowpC3j0U8MNxkQ\u002Fgiphy.gif",[10,376,377,378,381,382,73],{},"Run ",[70,379,380],{},"appwrite login",". Wait for the email and password to be supplied by the 1Password App, ",[30,383,384],{},[385,386,387],"strong",{},"and it never happens",[10,389,390,391,394],{},"This is the moment I realized something is not right. Tried looking for a config file in the system and found one at ",[70,392,393],{},"~\u002F.appwrite\u002Fprefs.json",". It doesn't store a password just a cookie. Essentially what the CLI does is, take the login credentials from you interactively, and then make an auth call and store the auth token in that file for future use. We could store the cookie in 1Password but then who takes care of refreshing the token?",[10,396,397],{},"Before giving up, this is where I got in touch with the 1Password team. Learnt that interactive login is not supported, and the following verbatim response \"If your file supports provisioning the credentials in a file, you can use the SDK’s file provisioner when building the plugin.\".",[10,399,400],{},"Interesting! File provisioner is mentioned in the docs but mostly you're going to miss reading it. Well, time to eat my words and go to appwrite CLI docs, and read it, of course. Found that there is a CI mode that works using the API Key. The only limitation is, it works with a single project at a time. And also the supported CLI commands depends on the permissions granted to the API Key.",[131,402,406],{"className":403,"code":404,"language":405,"meta":79,"style":79},"language-bash shiki shiki-themes github-light github-dark","appwrite client \\\n    --endpoint https:\u002F\u002Fcloud.appwrite.io\u002Fv1 \\\n    --projectId [YOUR_PROJECT_ID] \\\n    --key YOUR_API_KEY\n","bash",[70,407,408,422,432,444],{"__ignoreMap":79},[139,409,410,414,418],{"class":141,"line":142},[139,411,413],{"class":412},"sScJk","appwrite",[139,415,417],{"class":416},"sZZnC"," client",[139,419,421],{"class":420},"sj4cs"," \\\n",[139,423,424,427,430],{"class":141,"line":148},[139,425,426],{"class":420},"    --endpoint",[139,428,429],{"class":416}," https:\u002F\u002Fcloud.appwrite.io\u002Fv1",[139,431,421],{"class":420},[139,433,434,437,441],{"class":141,"line":154},[139,435,436],{"class":420},"    --projectId",[139,438,440],{"class":439},"sVt8B"," [YOUR_PROJECT_ID] ",[139,442,443],{"class":420},"\\\n",[139,445,446,449],{"class":141,"line":160},[139,447,448],{"class":412},"    --key",[139,450,451],{"class":416}," YOUR_API_KEY\n",[10,453,454,455,458,459,462,463,465,466,468],{},"On running this command from the home dir of my system, it added the endpoint and the API key to the ",[70,456,457],{},"prefs.json"," mentioned earlier, but it also created a new ",[70,460,461],{},"appwrite.json"," file mentioning the project ID in the home dir (from where the command was executed). So the ",[70,464,457],{}," doesn't contain the project id. If you've used appwrite then you'll know that the project folder also contains an ",[70,467,461],{}," file mentioning the project id. This hints that we only need to store the endpoint and the API key in 1Password, and then run the appwrite commands from the project directory itself (1Password supports tying up credentials to a particular dir).",[10,470,471],{},"After all this detective work, we are finally ready to implement the plugin.",[88,473,475],{"id":474},"implementing-the-plugin","Implementing the plugin",[10,477,478,479,482,483,486],{},"After deleting and running the ",[70,480,481],{},"gmake new-plugin"," command again, this time I selected API Key as the credential type. Modified the generate ",[70,484,485],{},"api_key.go"," file as shown below",[10,488,489],{},[385,490,491],{},"Required Fields, Default Provisioner & Importer",[131,493,495],{"className":133,"code":494,"language":135,"meta":79,"style":79},"Fields: []schema.CredentialField{\n    {\n        Name:                fieldname.APIKey,\n        MarkdownDescription: \"API Key used to authenticate to Appwrite.\",\n        Secret:              true,\n        Composition: &schema.ValueComposition{\n            Length: 256,\n            Charset: schema.Charset{\n                Lowercase: true,\n                Digits:    true,\n            },\n        },\n    },\n    {\n        Name:                fieldname.Endpoint,\n        MarkdownDescription: \"Appwrite server endpoint.\",\n        Secret:              false,\n        Optional:            false,\n    },\n},\nDefaultProvisioner: provision.TempFile(appwriteConfig, provision.AtFixedPath(ConfigPath())),\nImporter: importer.TryAll(\n    TryAppwriteConfigFile(),\n)}\n",[70,496,497,501,506,511,516,521,526,531,536,541,546,551,556,560,564,569,574,579,584,588,592,598,604,610],{"__ignoreMap":79},[139,498,499],{"class":141,"line":142},[139,500,297],{},[139,502,503],{"class":141,"line":148},[139,504,505],{},"    {\n",[139,507,508],{"class":141,"line":154},[139,509,510],{},"        Name:                fieldname.APIKey,\n",[139,512,513],{"class":141,"line":160},[139,514,515],{},"        MarkdownDescription: \"API Key used to authenticate to Appwrite.\",\n",[139,517,518],{"class":141,"line":166},[139,519,520],{},"        Secret:              true,\n",[139,522,523],{"class":141,"line":172},[139,524,525],{},"        Composition: &schema.ValueComposition{\n",[139,527,528],{"class":141,"line":178},[139,529,530],{},"            Length: 256,\n",[139,532,533],{"class":141,"line":184},[139,534,535],{},"            Charset: schema.Charset{\n",[139,537,538],{"class":141,"line":190},[139,539,540],{},"                Lowercase: true,\n",[139,542,543],{"class":141,"line":196},[139,544,545],{},"                Digits:    true,\n",[139,547,548],{"class":141,"line":202},[139,549,550],{},"            },\n",[139,552,553],{"class":141,"line":208},[139,554,555],{},"        },\n",[139,557,558],{"class":141,"line":214},[139,559,352],{},[139,561,562],{"class":141,"line":220},[139,563,505],{},[139,565,566],{"class":141,"line":226},[139,567,568],{},"        Name:                fieldname.Endpoint,\n",[139,570,571],{"class":141,"line":232},[139,572,573],{},"        MarkdownDescription: \"Appwrite server endpoint.\",\n",[139,575,576],{"class":141,"line":238},[139,577,578],{},"        Secret:              false,\n",[139,580,581],{"class":141,"line":244},[139,582,583],{},"        Optional:            false,\n",[139,585,586],{"class":141,"line":250},[139,587,352],{},[139,589,590],{"class":141,"line":256},[139,591,362],{},[139,593,595],{"class":141,"line":594},21,[139,596,597],{},"DefaultProvisioner: provision.TempFile(appwriteConfig, provision.AtFixedPath(ConfigPath())),\n",[139,599,601],{"class":141,"line":600},22,[139,602,603],{},"Importer: importer.TryAll(\n",[139,605,607],{"class":141,"line":606},23,[139,608,609],{},"    TryAppwriteConfigFile(),\n",[139,611,613],{"class":141,"line":612},24,[139,614,615],{},")}\n",[10,617,618,619,622],{},"Note that we're creating the temp file at a fixed path ",[70,620,621],{},"provision.AtFixedPath(ConfigPath()))",". Where ConfigPath is as shown below",[131,624,626],{"className":133,"code":625,"language":135,"meta":79,"style":79},"func ConfigPath() string {\n    configDir, err := os.UserHomeDir()\n    if err != nil {\n        return \"~\u002F.appwrite\u002Fprefs.json\"\n    }\n\n    return configDir + \"\u002F.appwrite\u002Fprefs.json\"\n}\n",[70,627,628,633,638,643,648,653,659,664],{"__ignoreMap":79},[139,629,630],{"class":141,"line":142},[139,631,632],{},"func ConfigPath() string {\n",[139,634,635],{"class":141,"line":148},[139,636,637],{},"    configDir, err := os.UserHomeDir()\n",[139,639,640],{"class":141,"line":154},[139,641,642],{},"    if err != nil {\n",[139,644,645],{"class":141,"line":160},[139,646,647],{},"        return \"~\u002F.appwrite\u002Fprefs.json\"\n",[139,649,650],{"class":141,"line":166},[139,651,652],{},"    }\n",[139,654,655],{"class":141,"line":172},[139,656,658],{"emptyLinePlaceholder":657},true,"\n",[139,660,661],{"class":141,"line":178},[139,662,663],{},"    return configDir + \"\u002F.appwrite\u002Fprefs.json\"\n",[139,665,666],{"class":141,"line":184},[139,667,668],{},"}\n",[10,670,671],{},[385,672,673],{},"appwriteConfig function",[10,675,676],{},"This function takes the credentials from 1Password and converts that into a JSON object so that a temp config file can be created for the appwrite CLI",[131,678,680],{"className":133,"code":679,"language":135,"meta":79,"style":79},"func appwriteConfig(in sdk.ProvisionInput) ([]byte, error) {\n    \u002F\u002F Create config object from the incoming fields\n    config := Config{\n        APIKey:   in.ItemFields[fieldname.APIKey],\n        Endpoint: in.ItemFields[fieldname.Endpoint],\n    }\n\n    \u002F\u002F Covert the content to a JSON string\n    contents, err := json.MarshalIndent(&config, \"\", \"  \")\n    if err != nil {\n        return nil, err\n    }\n\n    \u002F\u002F Convert the string to bytes, to be written to the temp file\n    return []byte(contents), nil\n}\n\n\u002F\u002F The config struct (notice the differnt json key names)\ntype Config struct {\n    APIKey   string `json:\"key\"`\n    Endpoint string `json:\"endpoint\"`\n}\n",[70,681,682,687,692,697,702,707,711,715,720,725,729,734,738,742,747,752,756,760,765,770,775,780],{"__ignoreMap":79},[139,683,684],{"class":141,"line":142},[139,685,686],{},"func appwriteConfig(in sdk.ProvisionInput) ([]byte, error) {\n",[139,688,689],{"class":141,"line":148},[139,690,691],{},"    \u002F\u002F Create config object from the incoming fields\n",[139,693,694],{"class":141,"line":154},[139,695,696],{},"    config := Config{\n",[139,698,699],{"class":141,"line":160},[139,700,701],{},"        APIKey:   in.ItemFields[fieldname.APIKey],\n",[139,703,704],{"class":141,"line":166},[139,705,706],{},"        Endpoint: in.ItemFields[fieldname.Endpoint],\n",[139,708,709],{"class":141,"line":172},[139,710,652],{},[139,712,713],{"class":141,"line":178},[139,714,658],{"emptyLinePlaceholder":657},[139,716,717],{"class":141,"line":184},[139,718,719],{},"    \u002F\u002F Covert the content to a JSON string\n",[139,721,722],{"class":141,"line":190},[139,723,724],{},"    contents, err := json.MarshalIndent(&config, \"\", \"  \")\n",[139,726,727],{"class":141,"line":196},[139,728,642],{},[139,730,731],{"class":141,"line":202},[139,732,733],{},"        return nil, err\n",[139,735,736],{"class":141,"line":208},[139,737,652],{},[139,739,740],{"class":141,"line":214},[139,741,658],{"emptyLinePlaceholder":657},[139,743,744],{"class":141,"line":220},[139,745,746],{},"    \u002F\u002F Convert the string to bytes, to be written to the temp file\n",[139,748,749],{"class":141,"line":226},[139,750,751],{},"    return []byte(contents), nil\n",[139,753,754],{"class":141,"line":232},[139,755,668],{},[139,757,758],{"class":141,"line":238},[139,759,658],{"emptyLinePlaceholder":657},[139,761,762],{"class":141,"line":244},[139,763,764],{},"\u002F\u002F The config struct (notice the differnt json key names)\n",[139,766,767],{"class":141,"line":250},[139,768,769],{},"type Config struct {\n",[139,771,772],{"class":141,"line":256},[139,773,774],{},"    APIKey   string `json:\"key\"`\n",[139,776,777],{"class":141,"line":594},[139,778,779],{},"    Endpoint string `json:\"endpoint\"`\n",[139,781,782],{"class":141,"line":600},[139,783,668],{},[10,785,786],{},[385,787,788],{},"TryAppwriteConfigFile function",[10,790,791],{},"This function reads an existing prefs.json file at the config path and shows a prompt to the user that these credentials can be imported into 1Password when they run any of the appwrite commands requiring authentication.",[131,793,795],{"className":133,"code":794,"language":135,"meta":79,"style":79},"func TryAppwriteConfigFile() sdk.Importer {\n    return importer.TryFile(ConfigPath(), func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {\n        var config Config\n        if err := contents.ToJSON(&config); err != nil {\n            out.AddError(err)\n            return\n        }\n\n        fmt.Println(\"Printing the imported file\")\n        fmt.Println(config)\n\n        if config.APIKey == \"\" {\n            return\n        }\n\n        if config.Endpoint == \"\" {\n            return\n        }\n\n        out.AddCandidate(sdk.ImportCandidate{\n            Fields: map[sdk.FieldName]string{\n                fieldname.APIKey:   config.APIKey,\n                fieldname.Endpoint: config.Endpoint,\n            },\n        })\n    })\n}\n",[70,796,797,802,807,812,817,822,827,832,836,841,846,850,855,859,863,867,872,876,880,884,889,894,899,904,908,914,920],{"__ignoreMap":79},[139,798,799],{"class":141,"line":142},[139,800,801],{},"func TryAppwriteConfigFile() sdk.Importer {\n",[139,803,804],{"class":141,"line":148},[139,805,806],{},"    return importer.TryFile(ConfigPath(), func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {\n",[139,808,809],{"class":141,"line":154},[139,810,811],{},"        var config Config\n",[139,813,814],{"class":141,"line":160},[139,815,816],{},"        if err := contents.ToJSON(&config); err != nil {\n",[139,818,819],{"class":141,"line":166},[139,820,821],{},"            out.AddError(err)\n",[139,823,824],{"class":141,"line":172},[139,825,826],{},"            return\n",[139,828,829],{"class":141,"line":178},[139,830,831],{},"        }\n",[139,833,834],{"class":141,"line":184},[139,835,658],{"emptyLinePlaceholder":657},[139,837,838],{"class":141,"line":190},[139,839,840],{},"        fmt.Println(\"Printing the imported file\")\n",[139,842,843],{"class":141,"line":196},[139,844,845],{},"        fmt.Println(config)\n",[139,847,848],{"class":141,"line":202},[139,849,658],{"emptyLinePlaceholder":657},[139,851,852],{"class":141,"line":208},[139,853,854],{},"        if config.APIKey == \"\" {\n",[139,856,857],{"class":141,"line":214},[139,858,826],{},[139,860,861],{"class":141,"line":220},[139,862,831],{},[139,864,865],{"class":141,"line":226},[139,866,658],{"emptyLinePlaceholder":657},[139,868,869],{"class":141,"line":232},[139,870,871],{},"        if config.Endpoint == \"\" {\n",[139,873,874],{"class":141,"line":238},[139,875,826],{},[139,877,878],{"class":141,"line":244},[139,879,831],{},[139,881,882],{"class":141,"line":250},[139,883,658],{"emptyLinePlaceholder":657},[139,885,886],{"class":141,"line":256},[139,887,888],{},"        out.AddCandidate(sdk.ImportCandidate{\n",[139,890,891],{"class":141,"line":594},[139,892,893],{},"            Fields: map[sdk.FieldName]string{\n",[139,895,896],{"class":141,"line":600},[139,897,898],{},"                fieldname.APIKey:   config.APIKey,\n",[139,900,901],{"class":141,"line":606},[139,902,903],{},"                fieldname.Endpoint: config.Endpoint,\n",[139,905,906],{"class":141,"line":612},[139,907,550],{},[139,909,911],{"class":141,"line":910},25,[139,912,913],{},"        })\n",[139,915,917],{"class":141,"line":916},26,[139,918,919],{},"    })\n",[139,921,923],{"class":141,"line":922},27,[139,924,668],{},[10,926,927,928,486],{},"That's it. We're done. The only thing remaining is writing some tests, and configuring which appwrite commands don't require auth. The latter can be done in ",[70,929,930],{},"appwrite.go",[131,932,934],{"className":133,"code":933,"language":135,"meta":79,"style":79},"NeedsAuth: needsauth.IfAll(\n    needsauth.NotForHelpOrVersion(),\n    needsauth.NotWithoutArgs(),\n    needsauth.NotWhenContainsArgs(\"client\"),\n    needsauth.NotWhenContainsArgs(\"login\"),\n    needsauth.NotWhenContainsArgs(\"logout\"),\n    needsauth.NotForExactArgs(\"deploy\"),\n    needsauth.NotForExactArgs(\"projects\"),\n    needsauth.NotForExactArgs(\"storage\"),\n    needsauth.NotForExactArgs(\"teams\"),\n    needsauth.NotForExactArgs(\"users\"),\n    needsauth.NotForExactArgs(\"account\"),\n    needsauth.NotForExactArgs(\"avatars\"),\n    needsauth.NotForExactArgs(\"functions\"),\n    needsauth.NotForExactArgs(\"databases\"),\n    needsauth.NotForExactArgs(\"health\"),\n    needsauth.NotForExactArgs(\"locale\"),\n),\n",[70,935,936,941,946,951,956,961,966,971,976,981,986,991,996,1001,1006,1011,1016,1021],{"__ignoreMap":79},[139,937,938],{"class":141,"line":142},[139,939,940],{},"NeedsAuth: needsauth.IfAll(\n",[139,942,943],{"class":141,"line":148},[139,944,945],{},"    needsauth.NotForHelpOrVersion(),\n",[139,947,948],{"class":141,"line":154},[139,949,950],{},"    needsauth.NotWithoutArgs(),\n",[139,952,953],{"class":141,"line":160},[139,954,955],{},"    needsauth.NotWhenContainsArgs(\"client\"),\n",[139,957,958],{"class":141,"line":166},[139,959,960],{},"    needsauth.NotWhenContainsArgs(\"login\"),\n",[139,962,963],{"class":141,"line":172},[139,964,965],{},"    needsauth.NotWhenContainsArgs(\"logout\"),\n",[139,967,968],{"class":141,"line":178},[139,969,970],{},"    needsauth.NotForExactArgs(\"deploy\"),\n",[139,972,973],{"class":141,"line":184},[139,974,975],{},"    needsauth.NotForExactArgs(\"projects\"),\n",[139,977,978],{"class":141,"line":190},[139,979,980],{},"    needsauth.NotForExactArgs(\"storage\"),\n",[139,982,983],{"class":141,"line":196},[139,984,985],{},"    needsauth.NotForExactArgs(\"teams\"),\n",[139,987,988],{"class":141,"line":202},[139,989,990],{},"    needsauth.NotForExactArgs(\"users\"),\n",[139,992,993],{"class":141,"line":208},[139,994,995],{},"    needsauth.NotForExactArgs(\"account\"),\n",[139,997,998],{"class":141,"line":214},[139,999,1000],{},"    needsauth.NotForExactArgs(\"avatars\"),\n",[139,1002,1003],{"class":141,"line":220},[139,1004,1005],{},"    needsauth.NotForExactArgs(\"functions\"),\n",[139,1007,1008],{"class":141,"line":226},[139,1009,1010],{},"    needsauth.NotForExactArgs(\"databases\"),\n",[139,1012,1013],{"class":141,"line":232},[139,1014,1015],{},"    needsauth.NotForExactArgs(\"health\"),\n",[139,1017,1018],{"class":141,"line":238},[139,1019,1020],{},"    needsauth.NotForExactArgs(\"locale\"),\n",[139,1022,1023],{"class":141,"line":244},[139,1024,1025],{},"),\n",[10,1027,1028,1029,1032],{},"The plugin tests can be modified in ",[70,1030,1031],{},"api_key_test.go"," file.",[131,1034,1036],{"className":133,"code":1035,"language":135,"meta":79,"style":79},"\u002F\u002F Test whether our file provision is working\nfunc TestAPIKeyProvisioner(t *testing.T) {\n    plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{\n        \"temp file\": {\n            ItemFields: map[sdk.FieldName]string{\n                fieldname.APIKey:   \"zsaugacpwq6k54nnbdbmh1cys98u2a32qqkacma2ioxn1e2j6eyrk9urom0vzcvm6qbbm8s6l4xbm86n37foauiqba9tlcvohuoz87j7nwvpob5wr71k58i105fn39a10vj7ob84opwf1vrfat3m8konch7xxy2z2dh1ykohdbef7xgmvtn82lebe4mzmfzoylqy4jslrok11zbjtmd6xs84ukd7b1k9ofyuanvinmlhkgua32p5x0gqbexample\",\n                fieldname.Endpoint: \"http:\u002F\u002Flocalhost\u002Fv1\",\n            },\n            ExpectedOutput: sdk.ProvisionOutput{\n                Files: map[string]sdk.OutputFile{\n                    ConfigPath(): {\n                        Contents: []byte(plugintest.LoadFixture(t, \"import_prefs.json\")),\n                    },\n                },\n            },\n        },\n    })\n}\n\n\u002F\u002F Test the importer\nfunc TestAPIKeyImporter(t *testing.T) {\n    plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{\n        \"Appwrite prefs file\": {\n            Files: map[string]string{\n                ConfigPath(): plugintest.LoadFixture(t, \"import_prefs.json\"),\n            },\n            ExpectedCandidates: []sdk.ImportCandidate{\n                {\n                    Fields: map[sdk.FieldName]string{\n                        fieldname.APIKey:   \"zsaugacpwq6k54nnbdbmh1cys98u2a32qqkacma2ioxn1e2j6eyrk9urom0vzcvm6qbbm8s6l4xbm86n37foauiqba9tlcvohuoz87j7nwvpob5wr71k58i105fn39a10vj7ob84opwf1vrfat3m8konch7xxy2z2dh1ykohdbef7xgmvtn82lebe4mzmfzoylqy4jslrok11zbjtmd6xs84ukd7b1k9ofyuanvinmlhkgua32p5x0gqbexample\",\n                        fieldname.Endpoint: \"http:\u002F\u002Flocalhost\u002Fv1\",\n                    },\n                },\n            },\n        },\n    })\n}\n",[70,1037,1038,1043,1048,1053,1058,1063,1068,1073,1077,1082,1087,1092,1097,1102,1107,1111,1115,1119,1123,1127,1132,1137,1142,1147,1152,1157,1161,1166,1172,1178,1184,1190,1195,1200,1205,1210,1215],{"__ignoreMap":79},[139,1039,1040],{"class":141,"line":142},[139,1041,1042],{},"\u002F\u002F Test whether our file provision is working\n",[139,1044,1045],{"class":141,"line":148},[139,1046,1047],{},"func TestAPIKeyProvisioner(t *testing.T) {\n",[139,1049,1050],{"class":141,"line":154},[139,1051,1052],{},"    plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{\n",[139,1054,1055],{"class":141,"line":160},[139,1056,1057],{},"        \"temp file\": {\n",[139,1059,1060],{"class":141,"line":166},[139,1061,1062],{},"            ItemFields: map[sdk.FieldName]string{\n",[139,1064,1065],{"class":141,"line":172},[139,1066,1067],{},"                fieldname.APIKey:   \"zsaugacpwq6k54nnbdbmh1cys98u2a32qqkacma2ioxn1e2j6eyrk9urom0vzcvm6qbbm8s6l4xbm86n37foauiqba9tlcvohuoz87j7nwvpob5wr71k58i105fn39a10vj7ob84opwf1vrfat3m8konch7xxy2z2dh1ykohdbef7xgmvtn82lebe4mzmfzoylqy4jslrok11zbjtmd6xs84ukd7b1k9ofyuanvinmlhkgua32p5x0gqbexample\",\n",[139,1069,1070],{"class":141,"line":178},[139,1071,1072],{},"                fieldname.Endpoint: \"http:\u002F\u002Flocalhost\u002Fv1\",\n",[139,1074,1075],{"class":141,"line":184},[139,1076,550],{},[139,1078,1079],{"class":141,"line":190},[139,1080,1081],{},"            ExpectedOutput: sdk.ProvisionOutput{\n",[139,1083,1084],{"class":141,"line":196},[139,1085,1086],{},"                Files: map[string]sdk.OutputFile{\n",[139,1088,1089],{"class":141,"line":202},[139,1090,1091],{},"                    ConfigPath(): {\n",[139,1093,1094],{"class":141,"line":208},[139,1095,1096],{},"                        Contents: []byte(plugintest.LoadFixture(t, \"import_prefs.json\")),\n",[139,1098,1099],{"class":141,"line":214},[139,1100,1101],{},"                    },\n",[139,1103,1104],{"class":141,"line":220},[139,1105,1106],{},"                },\n",[139,1108,1109],{"class":141,"line":226},[139,1110,550],{},[139,1112,1113],{"class":141,"line":232},[139,1114,555],{},[139,1116,1117],{"class":141,"line":238},[139,1118,919],{},[139,1120,1121],{"class":141,"line":244},[139,1122,668],{},[139,1124,1125],{"class":141,"line":250},[139,1126,658],{"emptyLinePlaceholder":657},[139,1128,1129],{"class":141,"line":256},[139,1130,1131],{},"\u002F\u002F Test the importer\n",[139,1133,1134],{"class":141,"line":594},[139,1135,1136],{},"func TestAPIKeyImporter(t *testing.T) {\n",[139,1138,1139],{"class":141,"line":600},[139,1140,1141],{},"    plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{\n",[139,1143,1144],{"class":141,"line":606},[139,1145,1146],{},"        \"Appwrite prefs file\": {\n",[139,1148,1149],{"class":141,"line":612},[139,1150,1151],{},"            Files: map[string]string{\n",[139,1153,1154],{"class":141,"line":910},[139,1155,1156],{},"                ConfigPath(): plugintest.LoadFixture(t, \"import_prefs.json\"),\n",[139,1158,1159],{"class":141,"line":916},[139,1160,550],{},[139,1162,1163],{"class":141,"line":922},[139,1164,1165],{},"            ExpectedCandidates: []sdk.ImportCandidate{\n",[139,1167,1169],{"class":141,"line":1168},28,[139,1170,1171],{},"                {\n",[139,1173,1175],{"class":141,"line":1174},29,[139,1176,1177],{},"                    Fields: map[sdk.FieldName]string{\n",[139,1179,1181],{"class":141,"line":1180},30,[139,1182,1183],{},"                        fieldname.APIKey:   \"zsaugacpwq6k54nnbdbmh1cys98u2a32qqkacma2ioxn1e2j6eyrk9urom0vzcvm6qbbm8s6l4xbm86n37foauiqba9tlcvohuoz87j7nwvpob5wr71k58i105fn39a10vj7ob84opwf1vrfat3m8konch7xxy2z2dh1ykohdbef7xgmvtn82lebe4mzmfzoylqy4jslrok11zbjtmd6xs84ukd7b1k9ofyuanvinmlhkgua32p5x0gqbexample\",\n",[139,1185,1187],{"class":141,"line":1186},31,[139,1188,1189],{},"                        fieldname.Endpoint: \"http:\u002F\u002Flocalhost\u002Fv1\",\n",[139,1191,1193],{"class":141,"line":1192},32,[139,1194,1101],{},[139,1196,1198],{"class":141,"line":1197},33,[139,1199,1106],{},[139,1201,1203],{"class":141,"line":1202},34,[139,1204,550],{},[139,1206,1208],{"class":141,"line":1207},35,[139,1209,555],{},[139,1211,1213],{"class":141,"line":1212},36,[139,1214,919],{},[139,1216,1218],{"class":141,"line":1217},37,[139,1219,668],{},[10,1221,1222,1223,1226,1227,1230],{},"To run the tests we can run ",[70,1224,1225],{},"gmake test"," or ",[70,1228,1229],{},"make test"," command.",[10,1232,1233],{},[77,1234],{"alt":79,"src":1235},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F263bfdc5-70cd-4580-8d52-5c2045295ddb-762413a54b.png",[10,1237,1238],{},"And this gives us the green light we needed.",[10,1240,1241],{},[77,1242],{"alt":79,"src":1243},"https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002FeIG0HfouRQJQr1wBzz\u002Fgiphy.gif",[34,1245,1247],{"id":1246},"part-2-creating-the-app","Part 2: Creating the app",[10,1249,1250],{},"We start with the basics here. Scaffold the app using the below command",[131,1252,1254],{"className":403,"code":1253,"language":405,"meta":79,"style":79},"npx nuxi@latest init \u003Cproject-name>\n",[70,1255,1256],{"__ignoreMap":79},[139,1257,1258,1261,1264,1267,1271,1274,1277],{"class":141,"line":142},[139,1259,1260],{"class":412},"npx",[139,1262,1263],{"class":416}," nuxi@latest",[139,1265,1266],{"class":416}," init",[139,1268,1270],{"class":1269},"szBVR"," \u003C",[139,1272,1273],{"class":416},"project-nam",[139,1275,1276],{"class":439},"e",[139,1278,1279],{"class":1269},">\n",[10,1281,1282,1283,1286,1287,1290],{},"Since we're using ",[70,1284,1285],{},"Passage"," for auth, install ",[70,1288,1289],{},"@passageidentity\u002Fpassage-elements",". Now here is a catch, to make use of the appwrite client SDK we need to create a session using email\u002Fpassword or through supported OAuth providers. Both of these are ruled out as passage auth doesn't need a password, and is also not a supported OAuth provider. I still installed the appwrite SDK as I used its locale services in my app flow.",[131,1292,1296],{"className":1293,"code":1294,"language":1295,"meta":79,"style":79},"language-json shiki shiki-themes github-light github-dark","yarn add @passageidentity\u002Fpassage-elements appwrite\n","json",[70,1297,1298],{"__ignoreMap":79},[139,1299,1300],{"class":141,"line":142},[139,1301,1294],{"class":439},[10,1303,1304,1305,1308,1309,1312],{},"We definitely need the server SDKs of both of the above. ",[70,1306,1307],{},"@passageidentity\u002Fpassage-node"," for verifying the authenticity of incoming client requests, and ",[70,1310,1311],{},"node-appwrite"," for interacting with the appwrite database.",[131,1314,1316],{"className":1293,"code":1315,"language":1295,"meta":79,"style":79},"yarn add node-appwrite @passageidentity\u002Fpassage-node\n",[70,1317,1318],{"__ignoreMap":79},[139,1319,1320],{"class":141,"line":142},[139,1321,1315],{"class":439},[10,1323,1324,1325,1330],{},"For styling and readymade components, I decided to use ",[14,1326,1329],{"href":1327,"rel":1328},"https:\u002F\u002Fui.nuxtlabs.com\u002F",[18],"NuxtLabs UI",". Now we're all set to start writing the app.",[88,1332,1334],{"id":1333},"passage-auth","Passage Auth",[10,1336,1337,1338,1341,1342,1032],{},"Passage provides readymade custom web components which handle the end-to-end auth for you. To use it in a Nuxt (or Vue) app, we need to register these components as custom elements in the Nuxt config file. If we do not do that then we'll get warnings in our browser console. Add the following inside the ",[70,1339,1340],{},"defineNuxtConfig"," input object in your ",[70,1343,1344],{},"nuxt.config.ts",[131,1346,1350],{"className":1347,"code":1348,"language":1349,"meta":79,"style":79},"language-typescript shiki shiki-themes github-light github-dark","vue: {\n  compilerOptions: {\n    isCustomElement: (tag) => tag.startsWith('passage-'),\n  },\n},\n","typescript",[70,1351,1352,1360,1367,1399,1403],{"__ignoreMap":79},[139,1353,1354,1357],{"class":141,"line":142},[139,1355,1356],{"class":412},"vue",[139,1358,1359],{"class":439},": {\n",[139,1361,1362,1365],{"class":141,"line":148},[139,1363,1364],{"class":412},"  compilerOptions",[139,1366,1359],{"class":439},[139,1368,1369,1372,1375,1379,1382,1385,1388,1391,1394,1397],{"class":141,"line":154},[139,1370,1371],{"class":412},"    isCustomElement",[139,1373,1374],{"class":439},": (",[139,1376,1378],{"class":1377},"s4XuR","tag",[139,1380,1381],{"class":439},") ",[139,1383,1384],{"class":1269},"=>",[139,1386,1387],{"class":439}," tag.",[139,1389,1390],{"class":412},"startsWith",[139,1392,1393],{"class":439},"(",[139,1395,1396],{"class":416},"'passage-'",[139,1398,1025],{"class":439},[139,1400,1401],{"class":141,"line":160},[139,1402,357],{"class":439},[139,1404,1405],{"class":141,"line":166},[139,1406,362],{"class":439},[10,1408,1409,1410,1413],{},"Now I tried to use the ",[70,1411,1412],{},"\u003Cpassage-auth>"," component directly in a page template after adding the necessary import",[131,1415,1417],{"className":1347,"code":1416,"language":1349,"meta":79,"style":79},"import '@passageidentity\u002Fpassage-elements\u002Fpassage-auth';\n",[70,1418,1419],{"__ignoreMap":79},[139,1420,1421,1424,1427],{"class":141,"line":142},[139,1422,1423],{"class":1269},"import",[139,1425,1426],{"class":416}," '@passageidentity\u002Fpassage-elements\u002Fpassage-auth'",[139,1428,1429],{"class":439},";\n",[10,1431,1432],{},"But it doesn't work, you'll get the below error",[131,1434,1436],{"className":403,"code":1435,"language":405,"meta":79,"style":79},"Cannot use import statement outside a module\n",[70,1437,1438],{"__ignoreMap":79},[139,1439,1440,1443,1446,1449,1452,1455,1458],{"class":141,"line":142},[139,1441,1442],{"class":412},"Cannot",[139,1444,1445],{"class":416}," use",[139,1447,1448],{"class":416}," import",[139,1450,1451],{"class":416}," statement",[139,1453,1454],{"class":416}," outside",[139,1456,1457],{"class":416}," a",[139,1459,1460],{"class":416}," module\n",[10,1462,1463,1464,1469,1470,1473],{},"This happens because Nuxt is running in SSR mode and web components are not available server side. You can get more information on this passage documentation for ",[14,1465,1468],{"href":1466,"rel":1467},"https:\u002F\u002Fdocs.passage.id\u002Ffrontend\u002Fexamples-by-framework\u002Fnext.js#:~:text=Calling%20the%20import,error%20being%20thrown.",[18],"NextJs here",". I tried following the same approach outlined in the link, load the component client side in the ",[70,1471,1472],{},"onMounted"," hook",[131,1475,1477],{"className":1347,"code":1476,"language":1349,"meta":79,"style":79},"onMounted(()=>{\n  require('@passageidentity\u002Fpassage-elements\u002Fpassage-auth');\n});\n",[70,1478,1479,1491,1504],{"__ignoreMap":79},[139,1480,1481,1483,1486,1488],{"class":141,"line":142},[139,1482,1472],{"class":412},[139,1484,1485],{"class":439},"(()",[139,1487,1384],{"class":1269},[139,1489,1490],{"class":439},"{\n",[139,1492,1493,1496,1498,1501],{"class":141,"line":148},[139,1494,1495],{"class":412},"  require",[139,1497,1393],{"class":439},[139,1499,1500],{"class":416},"'@passageidentity\u002Fpassage-elements\u002Fpassage-auth'",[139,1502,1503],{"class":439},");\n",[139,1505,1506],{"class":141,"line":154},[139,1507,1508],{"class":439},"});\n",[10,1510,1511,1512,1515,1516,1519,1520,1522,1523,1525,1526,1528,1529,73],{},"But sadly this also is a no-go. Nuxt3 ships with ",[70,1513,1514],{},"Vite"," bundler by default, and using ",[70,1517,1518],{},"require"," is not supported in ",[70,1521,1514],{},". No issues, simply replace ",[70,1524,1518],{}," with ",[70,1527,1423],{}," there you might say, but we get typescript error ",[70,1530,1531],{},"\"An import declaration can only be used at the top level of a namespace or module\"",[10,1533,1534],{},[77,1535],{"alt":79,"src":1536},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F4529834c-8f69-4acc-b186-06affe2b9394-3ce3d48348.png",[10,1538,1539],{},"So what is the solution? To resolve this I created separate components which only contain passage elements, and load this new component only on the client side using the \u003CClientOnly> option provided by Nuxt.",[10,1541,1542],{},"So a SignUp component might look like this",[131,1544,1548],{"className":1545,"code":1546,"language":1547,"meta":79,"style":79},"language-xml shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cpassage-register :app-id=\"passageAppId\" \u002F>\n\u003C\u002Ftemplate>\n","xml",[70,1549,1550,1555,1560],{"__ignoreMap":79},[139,1551,1552],{"class":141,"line":142},[139,1553,1554],{},"\u003Ctemplate>\n",[139,1556,1557],{"class":141,"line":148},[139,1558,1559],{},"  \u003Cpassage-register :app-id=\"passageAppId\" \u002F>\n",[139,1561,1562],{"class":141,"line":154},[139,1563,1564],{},"\u003C\u002Ftemplate>\n",[10,1566,1567,1568,1571],{},"And then you use this component on a ",[70,1569,1570],{},"sign-up"," page as shown below",[131,1573,1575],{"className":1545,"code":1574,"language":1547,"meta":79,"style":79},"\u003Ctemplate>\n  \u003CClientOnly>\n    \u003CUContainer>\n      \u003CUCard class=\"max-w-md mx-auto mt-8 text-center\">\n        \u003Ch1 class=\"text-3xl font-medium\">Sign up\u003C\u002Fh1>\n\n        \u003Csign-up \u002F>\n\n        \u003Cdiv\n          class=\"text-sm font-medium text-gray-500 dark:text-gray-300 text-center\"\n        >\n          Already have an account?\n          \u003CUButton variant=\"link\" :padded=\"false\" to=\"\u002Fsign-in\">\n            Sign in\n          \u003C\u002FUButton>\n        \u003C\u002Fdiv>\n      \u003C\u002FUCard>\n    \u003C\u002FUContainer>\n  \u003C\u002FClientOnly>\n\u003C\u002Ftemplate>\n",[70,1576,1577,1581,1586,1591,1596,1601,1605,1610,1614,1619,1624,1629,1634,1639,1644,1649,1654,1659,1664,1669],{"__ignoreMap":79},[139,1578,1579],{"class":141,"line":142},[139,1580,1554],{},[139,1582,1583],{"class":141,"line":148},[139,1584,1585],{},"  \u003CClientOnly>\n",[139,1587,1588],{"class":141,"line":154},[139,1589,1590],{},"    \u003CUContainer>\n",[139,1592,1593],{"class":141,"line":160},[139,1594,1595],{},"      \u003CUCard class=\"max-w-md mx-auto mt-8 text-center\">\n",[139,1597,1598],{"class":141,"line":166},[139,1599,1600],{},"        \u003Ch1 class=\"text-3xl font-medium\">Sign up\u003C\u002Fh1>\n",[139,1602,1603],{"class":141,"line":172},[139,1604,658],{"emptyLinePlaceholder":657},[139,1606,1607],{"class":141,"line":178},[139,1608,1609],{},"        \u003Csign-up \u002F>\n",[139,1611,1612],{"class":141,"line":184},[139,1613,658],{"emptyLinePlaceholder":657},[139,1615,1616],{"class":141,"line":190},[139,1617,1618],{},"        \u003Cdiv\n",[139,1620,1621],{"class":141,"line":196},[139,1622,1623],{},"          class=\"text-sm font-medium text-gray-500 dark:text-gray-300 text-center\"\n",[139,1625,1626],{"class":141,"line":202},[139,1627,1628],{},"        >\n",[139,1630,1631],{"class":141,"line":208},[139,1632,1633],{},"          Already have an account?\n",[139,1635,1636],{"class":141,"line":214},[139,1637,1638],{},"          \u003CUButton variant=\"link\" :padded=\"false\" to=\"\u002Fsign-in\">\n",[139,1640,1641],{"class":141,"line":220},[139,1642,1643],{},"            Sign in\n",[139,1645,1646],{"class":141,"line":226},[139,1647,1648],{},"          \u003C\u002FUButton>\n",[139,1650,1651],{"class":141,"line":232},[139,1652,1653],{},"        \u003C\u002Fdiv>\n",[139,1655,1656],{"class":141,"line":238},[139,1657,1658],{},"      \u003C\u002FUCard>\n",[139,1660,1661],{"class":141,"line":244},[139,1662,1663],{},"    \u003C\u002FUContainer>\n",[139,1665,1666],{"class":141,"line":250},[139,1667,1668],{},"  \u003C\u002FClientOnly>\n",[139,1670,1671],{"class":141,"line":256},[139,1672,1564],{},[10,1674,1675],{},"After making the above adjustments it works all right.",[1677,1678,1680,1684],"div",{"dataNodeType":1679},"callout",[1677,1681,1683],{"dataNodeType":1682},"callout-emoji","💡",[1677,1685,1687,1688,266,1691,1694,1695,1697],{"dataNodeType":1686},"callout-text","Please note that I'm using ",[70,1689,1690],{},"\u003Cpassage-register>",[70,1692,1693],{},"\u003Cpassage-login>"," components instead of the ",[70,1696,1412],{}," because I've some custom registration fields.",[88,1699,1701],{"id":1700},"creating-user-family-accounts","Creating user & family accounts",[10,1703,1704],{},"Since we want to be able to invite family members (or create accounts for them ourselves) to the app, we need to roll out our own database schema to connect their accounts. But appwrite supports the creation of Teams and linking user accounts to them. To use this feature we will need to create appwrite users and then link those users to a team. How do we correlate the Passage users to the appwrite users?",[10,1706,1707,1708,1711,1712,1715,1716,1719,1720,73],{},"Passage allows us to listen to new account creations or a fresh login, by attaching a callback (",[70,1709,1710],{},"onSuccess",") to passage elements. But there is a catch, we can't simply bind the callback to the pasasge element using the ",[70,1713,1714],{},"\"@\""," syntax of vue. I needed to use the ",[70,1717,1718],{},"\".\""," syntax to make it work. You can read more about this ",[14,1721,25],{"href":1722,"rel":1723},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Fweb-components.html#passing-dom-properties",[18],[10,1725,1726],{},"This is my final SignUp component which uses the passage callback to make an API call to a Nuxt serverless API",[131,1728,1730],{"className":1347,"code":1729,"language":1349,"meta":79,"style":79},"\u003Cscript setup lang=\"ts\">\nimport '@passageidentity\u002Fpassage-elements\u002Fpassage-register';\nimport { authResult } from '@passageidentity\u002Fpassage-elements';\nconst { getUser } = usePassageUser();\n\nconst {\n  public: { passageAppId },\n} = useRuntimeConfig();\n\nconst onRegistrationDone = async (authResult: authResult) => {\n  try {\n    const res = await $fetch('\u002Fapi\u002Fusers', {\n      method: 'post',\n    });\n\n    console.log('got response from user create', res);\n    await getUser(authResult.auth_token);\n    navigateTo('\u002Fonboarding');\n  } catch (error) {\n    console.log('failed to create user in appwrite');\n  }\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cpassage-register :app-id=\"passageAppId\" .onSuccess=\"onRegistrationDone\" \u002F>\n\u003C\u002Ftemplate>\n",[70,1731,1732,1748,1757,1772,1794,1798,1805,1819,1831,1835,1866,1873,1897,1908,1913,1917,1933,1944,1956,1967,1980,1985,1990,2000,2004,2013,2051],{"__ignoreMap":79},[139,1733,1734,1737,1740,1743,1746],{"class":141,"line":142},[139,1735,1736],{"class":1269},"\u003C",[139,1738,1739],{"class":439},"script setup lang",[139,1741,1742],{"class":1269},"=",[139,1744,1745],{"class":416},"\"ts\"",[139,1747,1279],{"class":1269},[139,1749,1750,1752,1755],{"class":141,"line":148},[139,1751,1423],{"class":1269},[139,1753,1754],{"class":416}," '@passageidentity\u002Fpassage-elements\u002Fpassage-register'",[139,1756,1429],{"class":439},[139,1758,1759,1761,1764,1767,1770],{"class":141,"line":154},[139,1760,1423],{"class":1269},[139,1762,1763],{"class":439}," { authResult } ",[139,1765,1766],{"class":1269},"from",[139,1768,1769],{"class":416}," '@passageidentity\u002Fpassage-elements'",[139,1771,1429],{"class":439},[139,1773,1774,1777,1780,1783,1786,1788,1791],{"class":141,"line":160},[139,1775,1776],{"class":1269},"const",[139,1778,1779],{"class":439}," { ",[139,1781,1782],{"class":420},"getUser",[139,1784,1785],{"class":439}," } ",[139,1787,1742],{"class":1269},[139,1789,1790],{"class":412}," usePassageUser",[139,1792,1793],{"class":439},"();\n",[139,1795,1796],{"class":141,"line":166},[139,1797,658],{"emptyLinePlaceholder":657},[139,1799,1800,1802],{"class":141,"line":172},[139,1801,1776],{"class":1269},[139,1803,1804],{"class":439}," {\n",[139,1806,1807,1810,1813,1816],{"class":141,"line":178},[139,1808,1809],{"class":1377},"  public",[139,1811,1812],{"class":439},": { ",[139,1814,1815],{"class":420},"passageAppId",[139,1817,1818],{"class":439}," },\n",[139,1820,1821,1824,1826,1829],{"class":141,"line":184},[139,1822,1823],{"class":439},"} ",[139,1825,1742],{"class":1269},[139,1827,1828],{"class":412}," useRuntimeConfig",[139,1830,1793],{"class":439},[139,1832,1833],{"class":141,"line":190},[139,1834,658],{"emptyLinePlaceholder":657},[139,1836,1837,1839,1842,1845,1848,1851,1854,1857,1860,1862,1864],{"class":141,"line":196},[139,1838,1776],{"class":1269},[139,1840,1841],{"class":412}," onRegistrationDone",[139,1843,1844],{"class":1269}," =",[139,1846,1847],{"class":1269}," async",[139,1849,1850],{"class":439}," (",[139,1852,1853],{"class":1377},"authResult",[139,1855,1856],{"class":1269},":",[139,1858,1859],{"class":412}," authResult",[139,1861,1381],{"class":439},[139,1863,1384],{"class":1269},[139,1865,1804],{"class":439},[139,1867,1868,1871],{"class":141,"line":202},[139,1869,1870],{"class":1269},"  try",[139,1872,1804],{"class":439},[139,1874,1875,1878,1881,1883,1886,1889,1891,1894],{"class":141,"line":208},[139,1876,1877],{"class":1269},"    const",[139,1879,1880],{"class":420}," res",[139,1882,1844],{"class":1269},[139,1884,1885],{"class":1269}," await",[139,1887,1888],{"class":412}," $fetch",[139,1890,1393],{"class":439},[139,1892,1893],{"class":416},"'\u002Fapi\u002Fusers'",[139,1895,1896],{"class":439},", {\n",[139,1898,1899,1902,1905],{"class":141,"line":214},[139,1900,1901],{"class":439},"      method: ",[139,1903,1904],{"class":416},"'post'",[139,1906,1907],{"class":439},",\n",[139,1909,1910],{"class":141,"line":220},[139,1911,1912],{"class":439},"    });\n",[139,1914,1915],{"class":141,"line":226},[139,1916,658],{"emptyLinePlaceholder":657},[139,1918,1919,1922,1925,1927,1930],{"class":141,"line":232},[139,1920,1921],{"class":439},"    console.",[139,1923,1924],{"class":412},"log",[139,1926,1393],{"class":439},[139,1928,1929],{"class":416},"'got response from user create'",[139,1931,1932],{"class":439},", res);\n",[139,1934,1935,1938,1941],{"class":141,"line":238},[139,1936,1937],{"class":1269},"    await",[139,1939,1940],{"class":412}," getUser",[139,1942,1943],{"class":439},"(authResult.auth_token);\n",[139,1945,1946,1949,1951,1954],{"class":141,"line":244},[139,1947,1948],{"class":412},"    navigateTo",[139,1950,1393],{"class":439},[139,1952,1953],{"class":416},"'\u002Fonboarding'",[139,1955,1503],{"class":439},[139,1957,1958,1961,1964],{"class":141,"line":250},[139,1959,1960],{"class":439},"  } ",[139,1962,1963],{"class":1269},"catch",[139,1965,1966],{"class":439}," (error) {\n",[139,1968,1969,1971,1973,1975,1978],{"class":141,"line":256},[139,1970,1921],{"class":439},[139,1972,1924],{"class":412},[139,1974,1393],{"class":439},[139,1976,1977],{"class":416},"'failed to create user in appwrite'",[139,1979,1503],{"class":439},[139,1981,1982],{"class":141,"line":594},[139,1983,1984],{"class":439},"  }\n",[139,1986,1987],{"class":141,"line":600},[139,1988,1989],{"class":439},"};\n",[139,1991,1992,1995,1998],{"class":141,"line":606},[139,1993,1994],{"class":1269},"\u003C\u002F",[139,1996,1997],{"class":439},"script",[139,1999,1279],{"class":1269},[139,2001,2002],{"class":141,"line":612},[139,2003,658],{"emptyLinePlaceholder":657},[139,2005,2006,2008,2011],{"class":141,"line":910},[139,2007,1736],{"class":439},[139,2009,2010],{"class":412},"template",[139,2012,1279],{"class":439},[139,2014,2015,2018,2021,2024,2027,2030,2032,2035,2037,2040,2043,2045,2048],{"class":141,"line":916},[139,2016,2017],{"class":1269},"  \u003C",[139,2019,2020],{"class":439},"passage",[139,2022,2023],{"class":1269},"-",[139,2025,2026],{"class":412},"register",[139,2028,2029],{"class":439}," :app",[139,2031,2023],{"class":1269},[139,2033,2034],{"class":439},"id",[139,2036,1742],{"class":1269},[139,2038,2039],{"class":416},"\"passageAppId\"",[139,2041,2042],{"class":439}," .onSuccess",[139,2044,1742],{"class":1269},[139,2046,2047],{"class":416},"\"onRegistrationDone\"",[139,2049,2050],{"class":1269}," \u002F>\n",[139,2052,2053,2055,2057],{"class":141,"line":922},[139,2054,1994],{"class":1269},[139,2056,2010],{"class":439},[139,2058,1279],{"class":1269},[10,2060,2061,2062,2065],{},"And this is the ",[70,2063,2064],{},"\u002Fapi\u002Fusers"," code. We receive the request from the client, verify its authenticity using the passage's node SDK, and then create a new user in appwrite using their node SDK.",[131,2067,2069],{"className":1347,"code":2068,"language":1349,"meta":79,"style":79},"import { UserObject } from '@passageidentity\u002Fpassage-node';\nimport { protectRoute } from '..\u002FusePassage';\nimport { useAppwrite } from '..\u002FuseAppwrite';\n\nexport default defineEventHandler(async (event) => {\n  console.log('incoming post event for api\u002Fusers\u002F', event);\n\n  await protectRoute(event);\n\n  const user = event.context.auth.user as UserObject;\n  console.log(`got some auth user:`, user);\n\n  const { $users } = useAppwrite();\n\n  \u002F\u002F create an appwrite user with the same ID returned by Passage\n  const res = await $users.create(\n    user.id,\n    user.email,\n    undefined,\n    undefined,\n    user.user_metadata?.name as string\n  );\n\n  console.log('res of user create', res);\n\n  return {\n    status: 'ok',\n  };\n});\n",[70,2070,2071,2085,2099,2113,2117,2144,2159,2163,2174,2178,2199,2213,2217,2235,2239,2245,2264,2269,2274,2281,2287,2297,2302,2306,2319,2323,2330,2340,2345],{"__ignoreMap":79},[139,2072,2073,2075,2078,2080,2083],{"class":141,"line":142},[139,2074,1423],{"class":1269},[139,2076,2077],{"class":439}," { UserObject } ",[139,2079,1766],{"class":1269},[139,2081,2082],{"class":416}," '@passageidentity\u002Fpassage-node'",[139,2084,1429],{"class":439},[139,2086,2087,2089,2092,2094,2097],{"class":141,"line":148},[139,2088,1423],{"class":1269},[139,2090,2091],{"class":439}," { protectRoute } ",[139,2093,1766],{"class":1269},[139,2095,2096],{"class":416}," '..\u002FusePassage'",[139,2098,1429],{"class":439},[139,2100,2101,2103,2106,2108,2111],{"class":141,"line":154},[139,2102,1423],{"class":1269},[139,2104,2105],{"class":439}," { useAppwrite } ",[139,2107,1766],{"class":1269},[139,2109,2110],{"class":416}," '..\u002FuseAppwrite'",[139,2112,1429],{"class":439},[139,2114,2115],{"class":141,"line":160},[139,2116,658],{"emptyLinePlaceholder":657},[139,2118,2119,2122,2125,2128,2130,2133,2135,2138,2140,2142],{"class":141,"line":166},[139,2120,2121],{"class":1269},"export",[139,2123,2124],{"class":1269}," default",[139,2126,2127],{"class":412}," defineEventHandler",[139,2129,1393],{"class":439},[139,2131,2132],{"class":1269},"async",[139,2134,1850],{"class":439},[139,2136,2137],{"class":1377},"event",[139,2139,1381],{"class":439},[139,2141,1384],{"class":1269},[139,2143,1804],{"class":439},[139,2145,2146,2149,2151,2153,2156],{"class":141,"line":172},[139,2147,2148],{"class":439},"  console.",[139,2150,1924],{"class":412},[139,2152,1393],{"class":439},[139,2154,2155],{"class":416},"'incoming post event for api\u002Fusers\u002F'",[139,2157,2158],{"class":439},", event);\n",[139,2160,2161],{"class":141,"line":178},[139,2162,658],{"emptyLinePlaceholder":657},[139,2164,2165,2168,2171],{"class":141,"line":184},[139,2166,2167],{"class":1269},"  await",[139,2169,2170],{"class":412}," protectRoute",[139,2172,2173],{"class":439},"(event);\n",[139,2175,2176],{"class":141,"line":190},[139,2177,658],{"emptyLinePlaceholder":657},[139,2179,2180,2183,2186,2188,2191,2194,2197],{"class":141,"line":196},[139,2181,2182],{"class":1269},"  const",[139,2184,2185],{"class":420}," user",[139,2187,1844],{"class":1269},[139,2189,2190],{"class":439}," event.context.auth.user ",[139,2192,2193],{"class":1269},"as",[139,2195,2196],{"class":412}," UserObject",[139,2198,1429],{"class":439},[139,2200,2201,2203,2205,2207,2210],{"class":141,"line":202},[139,2202,2148],{"class":439},[139,2204,1924],{"class":412},[139,2206,1393],{"class":439},[139,2208,2209],{"class":416},"`got some auth user:`",[139,2211,2212],{"class":439},", user);\n",[139,2214,2215],{"class":141,"line":208},[139,2216,658],{"emptyLinePlaceholder":657},[139,2218,2219,2221,2223,2226,2228,2230,2233],{"class":141,"line":214},[139,2220,2182],{"class":1269},[139,2222,1779],{"class":439},[139,2224,2225],{"class":420},"$users",[139,2227,1785],{"class":439},[139,2229,1742],{"class":1269},[139,2231,2232],{"class":412}," useAppwrite",[139,2234,1793],{"class":439},[139,2236,2237],{"class":141,"line":220},[139,2238,658],{"emptyLinePlaceholder":657},[139,2240,2241],{"class":141,"line":226},[139,2242,2244],{"class":2243},"sJ8bj","  \u002F\u002F create an appwrite user with the same ID returned by Passage\n",[139,2246,2247,2249,2251,2253,2255,2258,2261],{"class":141,"line":232},[139,2248,2182],{"class":1269},[139,2250,1880],{"class":420},[139,2252,1844],{"class":1269},[139,2254,1885],{"class":1269},[139,2256,2257],{"class":439}," $users.",[139,2259,2260],{"class":412},"create",[139,2262,2263],{"class":439},"(\n",[139,2265,2266],{"class":141,"line":238},[139,2267,2268],{"class":439},"    user.id,\n",[139,2270,2271],{"class":141,"line":244},[139,2272,2273],{"class":439},"    user.email,\n",[139,2275,2276,2279],{"class":141,"line":250},[139,2277,2278],{"class":420},"    undefined",[139,2280,1907],{"class":439},[139,2282,2283,2285],{"class":141,"line":256},[139,2284,2278],{"class":420},[139,2286,1907],{"class":439},[139,2288,2289,2292,2294],{"class":141,"line":594},[139,2290,2291],{"class":439},"    user.user_metadata?.name ",[139,2293,2193],{"class":1269},[139,2295,2296],{"class":420}," string\n",[139,2298,2299],{"class":141,"line":600},[139,2300,2301],{"class":439},"  );\n",[139,2303,2304],{"class":141,"line":606},[139,2305,658],{"emptyLinePlaceholder":657},[139,2307,2308,2310,2312,2314,2317],{"class":141,"line":612},[139,2309,2148],{"class":439},[139,2311,1924],{"class":412},[139,2313,1393],{"class":439},[139,2315,2316],{"class":416},"'res of user create'",[139,2318,1932],{"class":439},[139,2320,2321],{"class":141,"line":910},[139,2322,658],{"emptyLinePlaceholder":657},[139,2324,2325,2328],{"class":141,"line":916},[139,2326,2327],{"class":1269},"  return",[139,2329,1804],{"class":439},[139,2331,2332,2335,2338],{"class":141,"line":922},[139,2333,2334],{"class":439},"    status: ",[139,2336,2337],{"class":416},"'ok'",[139,2339,1907],{"class":439},[139,2341,2342],{"class":141,"line":1168},[139,2343,2344],{"class":439},"  };\n",[139,2346,2347],{"class":141,"line":1174},[139,2348,1508],{"class":439},[10,2350,2061,2351,2354],{},[70,2352,2353],{},"protectRoute"," function to verify the authenticity of the request",[131,2356,2358],{"className":1347,"code":2357,"language":1349,"meta":79,"style":79},"import { H3Event } from 'h3';\nimport Passage, { Metadata } from '@passageidentity\u002Fpassage-node';\n\nlet _passage: Passage | null = null;\n\nconst getPassage = () => {\n  if (!_passage) {\n    const {\n      passageApiKey,\n      public: { passageAppId },\n    } = useRuntimeConfig();\n\n    const passageConfig = {\n      appID: passageAppId,\n      apiKey: passageApiKey,\n    };\n\n    _passage = new Passage(passageConfig);\n  }\n\n  return _passage;\n};\n\nexport const protectRoute = async (event: H3Event) => {\n  const passage = getPassage();\n\n  try {\n    const userId = await passage.authenticateRequest(event.node.req);\n\n    if (userId) {\n      console.log('request authenticated', userId);\n\n      const user = await passage.user.get(userId);\n      event.context.auth = { user };\n \n      return;\n    }\n  } catch (error) {\n    console.log('failed to authenticate request', error);\n  }\n\n  throw createError({\n    statusCode: 401,\n    message: 'Unauthorized',\n  });\n};\n",[70,2359,2360,2374,2387,2391,2416,2420,2436,2449,2455,2462,2473,2484,2488,2499,2504,2509,2514,2518,2533,2537,2541,2548,2552,2556,2584,2597,2601,2607,2627,2631,2639,2654,2658,2678,2688,2693,2700,2704,2713,2728,2733,2738,2750,2761,2772,2778],{"__ignoreMap":79},[139,2361,2362,2364,2367,2369,2372],{"class":141,"line":142},[139,2363,1423],{"class":1269},[139,2365,2366],{"class":439}," { H3Event } ",[139,2368,1766],{"class":1269},[139,2370,2371],{"class":416}," 'h3'",[139,2373,1429],{"class":439},[139,2375,2376,2378,2381,2383,2385],{"class":141,"line":148},[139,2377,1423],{"class":1269},[139,2379,2380],{"class":439}," Passage, { Metadata } ",[139,2382,1766],{"class":1269},[139,2384,2082],{"class":416},[139,2386,1429],{"class":439},[139,2388,2389],{"class":141,"line":154},[139,2390,658],{"emptyLinePlaceholder":657},[139,2392,2393,2396,2399,2401,2404,2407,2410,2412,2414],{"class":141,"line":160},[139,2394,2395],{"class":1269},"let",[139,2397,2398],{"class":439}," _passage",[139,2400,1856],{"class":1269},[139,2402,2403],{"class":412}," Passage",[139,2405,2406],{"class":1269}," |",[139,2408,2409],{"class":420}," null",[139,2411,1844],{"class":1269},[139,2413,2409],{"class":420},[139,2415,1429],{"class":439},[139,2417,2418],{"class":141,"line":166},[139,2419,658],{"emptyLinePlaceholder":657},[139,2421,2422,2424,2427,2429,2432,2434],{"class":141,"line":172},[139,2423,1776],{"class":1269},[139,2425,2426],{"class":412}," getPassage",[139,2428,1844],{"class":1269},[139,2430,2431],{"class":439}," () ",[139,2433,1384],{"class":1269},[139,2435,1804],{"class":439},[139,2437,2438,2441,2443,2446],{"class":141,"line":178},[139,2439,2440],{"class":1269},"  if",[139,2442,1850],{"class":439},[139,2444,2445],{"class":1269},"!",[139,2447,2448],{"class":439},"_passage) {\n",[139,2450,2451,2453],{"class":141,"line":184},[139,2452,1877],{"class":1269},[139,2454,1804],{"class":439},[139,2456,2457,2460],{"class":141,"line":190},[139,2458,2459],{"class":420},"      passageApiKey",[139,2461,1907],{"class":439},[139,2463,2464,2467,2469,2471],{"class":141,"line":196},[139,2465,2466],{"class":1377},"      public",[139,2468,1812],{"class":439},[139,2470,1815],{"class":420},[139,2472,1818],{"class":439},[139,2474,2475,2478,2480,2482],{"class":141,"line":202},[139,2476,2477],{"class":439},"    } ",[139,2479,1742],{"class":1269},[139,2481,1828],{"class":412},[139,2483,1793],{"class":439},[139,2485,2486],{"class":141,"line":208},[139,2487,658],{"emptyLinePlaceholder":657},[139,2489,2490,2492,2495,2497],{"class":141,"line":214},[139,2491,1877],{"class":1269},[139,2493,2494],{"class":420}," passageConfig",[139,2496,1844],{"class":1269},[139,2498,1804],{"class":439},[139,2500,2501],{"class":141,"line":220},[139,2502,2503],{"class":439},"      appID: passageAppId,\n",[139,2505,2506],{"class":141,"line":226},[139,2507,2508],{"class":439},"      apiKey: passageApiKey,\n",[139,2510,2511],{"class":141,"line":232},[139,2512,2513],{"class":439},"    };\n",[139,2515,2516],{"class":141,"line":238},[139,2517,658],{"emptyLinePlaceholder":657},[139,2519,2520,2523,2525,2528,2530],{"class":141,"line":244},[139,2521,2522],{"class":439},"    _passage ",[139,2524,1742],{"class":1269},[139,2526,2527],{"class":1269}," new",[139,2529,2403],{"class":412},[139,2531,2532],{"class":439},"(passageConfig);\n",[139,2534,2535],{"class":141,"line":250},[139,2536,1984],{"class":439},[139,2538,2539],{"class":141,"line":256},[139,2540,658],{"emptyLinePlaceholder":657},[139,2542,2543,2545],{"class":141,"line":594},[139,2544,2327],{"class":1269},[139,2546,2547],{"class":439}," _passage;\n",[139,2549,2550],{"class":141,"line":600},[139,2551,1989],{"class":439},[139,2553,2554],{"class":141,"line":606},[139,2555,658],{"emptyLinePlaceholder":657},[139,2557,2558,2560,2563,2565,2567,2569,2571,2573,2575,2578,2580,2582],{"class":141,"line":612},[139,2559,2121],{"class":1269},[139,2561,2562],{"class":1269}," const",[139,2564,2170],{"class":412},[139,2566,1844],{"class":1269},[139,2568,1847],{"class":1269},[139,2570,1850],{"class":439},[139,2572,2137],{"class":1377},[139,2574,1856],{"class":1269},[139,2576,2577],{"class":412}," H3Event",[139,2579,1381],{"class":439},[139,2581,1384],{"class":1269},[139,2583,1804],{"class":439},[139,2585,2586,2588,2591,2593,2595],{"class":141,"line":910},[139,2587,2182],{"class":1269},[139,2589,2590],{"class":420}," passage",[139,2592,1844],{"class":1269},[139,2594,2426],{"class":412},[139,2596,1793],{"class":439},[139,2598,2599],{"class":141,"line":916},[139,2600,658],{"emptyLinePlaceholder":657},[139,2602,2603,2605],{"class":141,"line":922},[139,2604,1870],{"class":1269},[139,2606,1804],{"class":439},[139,2608,2609,2611,2614,2616,2618,2621,2624],{"class":141,"line":1168},[139,2610,1877],{"class":1269},[139,2612,2613],{"class":420}," userId",[139,2615,1844],{"class":1269},[139,2617,1885],{"class":1269},[139,2619,2620],{"class":439}," passage.",[139,2622,2623],{"class":412},"authenticateRequest",[139,2625,2626],{"class":439},"(event.node.req);\n",[139,2628,2629],{"class":141,"line":1174},[139,2630,658],{"emptyLinePlaceholder":657},[139,2632,2633,2636],{"class":141,"line":1180},[139,2634,2635],{"class":1269},"    if",[139,2637,2638],{"class":439}," (userId) {\n",[139,2640,2641,2644,2646,2648,2651],{"class":141,"line":1186},[139,2642,2643],{"class":439},"      console.",[139,2645,1924],{"class":412},[139,2647,1393],{"class":439},[139,2649,2650],{"class":416},"'request authenticated'",[139,2652,2653],{"class":439},", userId);\n",[139,2655,2656],{"class":141,"line":1192},[139,2657,658],{"emptyLinePlaceholder":657},[139,2659,2660,2663,2665,2667,2669,2672,2675],{"class":141,"line":1197},[139,2661,2662],{"class":1269},"      const",[139,2664,2185],{"class":420},[139,2666,1844],{"class":1269},[139,2668,1885],{"class":1269},[139,2670,2671],{"class":439}," passage.user.",[139,2673,2674],{"class":412},"get",[139,2676,2677],{"class":439},"(userId);\n",[139,2679,2680,2683,2685],{"class":141,"line":1202},[139,2681,2682],{"class":439},"      event.context.auth ",[139,2684,1742],{"class":1269},[139,2686,2687],{"class":439}," { user };\n",[139,2689,2690],{"class":141,"line":1207},[139,2691,2692],{"class":439}," \n",[139,2694,2695,2698],{"class":141,"line":1212},[139,2696,2697],{"class":1269},"      return",[139,2699,1429],{"class":439},[139,2701,2702],{"class":141,"line":1217},[139,2703,652],{"class":439},[139,2705,2707,2709,2711],{"class":141,"line":2706},38,[139,2708,1960],{"class":439},[139,2710,1963],{"class":1269},[139,2712,1966],{"class":439},[139,2714,2716,2718,2720,2722,2725],{"class":141,"line":2715},39,[139,2717,1921],{"class":439},[139,2719,1924],{"class":412},[139,2721,1393],{"class":439},[139,2723,2724],{"class":416},"'failed to authenticate request'",[139,2726,2727],{"class":439},", error);\n",[139,2729,2731],{"class":141,"line":2730},40,[139,2732,1984],{"class":439},[139,2734,2736],{"class":141,"line":2735},41,[139,2737,658],{"emptyLinePlaceholder":657},[139,2739,2741,2744,2747],{"class":141,"line":2740},42,[139,2742,2743],{"class":1269},"  throw",[139,2745,2746],{"class":412}," createError",[139,2748,2749],{"class":439},"({\n",[139,2751,2753,2756,2759],{"class":141,"line":2752},43,[139,2754,2755],{"class":439},"    statusCode: ",[139,2757,2758],{"class":420},"401",[139,2760,1907],{"class":439},[139,2762,2764,2767,2770],{"class":141,"line":2763},44,[139,2765,2766],{"class":439},"    message: ",[139,2768,2769],{"class":416},"'Unauthorized'",[139,2771,1907],{"class":439},[139,2773,2775],{"class":141,"line":2774},45,[139,2776,2777],{"class":439},"  });\n",[139,2779,2781],{"class":141,"line":2780},46,[139,2782,1989],{"class":439},[10,2784,2785],{},[385,2786,2787],{},"Handle adding family members",[10,2789,2790],{},"During the onboarding of a new user, the app asks them to create a family account and add family members to it. But to invite the family members they should have a passage account, right? That is the approach we've followed above, the appwrite user id is provided by Passage. To handle this, we first create Passage users using the passage-node SDK, get the user ids, create corresponding appwrite users, and then finally link these users to the created team\u002Ffamily.",[10,2792,2793],{},"The frontend function which adds the family members",[131,2795,2797],{"className":1347,"code":2796,"language":1349,"meta":79,"style":79},"const addMembers = async () => {\n  loading.value = true;\n  try {\n    const res: any = await $fetch('\u002Fapi\u002Ffamilies', {\n      method: 'post',\n      body: {\n        type: 'ADD_MEMBERS',\n        familyId: metadata?.family_id,\n        members: members.value,\n        onboardStep: metadata?.onboard_step,\n      },\n    });\n\n    console.log('response of add members', res);\n    if (res.user && user.value) {\n      user.value.user_metadata = res.user.user_metadata;\n    }\n\n    emit('membersAdded');\n  } catch (error) {\n    console.log('error is adding members', error);\n  }\n\n  loading.value = false;\n};\n",[70,2798,2799,2816,2828,2834,2858,2866,2871,2881,2886,2891,2896,2900,2904,2908,2921,2934,2944,2948,2952,2964,2972,2985,2989,2993,3004],{"__ignoreMap":79},[139,2800,2801,2803,2806,2808,2810,2812,2814],{"class":141,"line":142},[139,2802,1776],{"class":1269},[139,2804,2805],{"class":412}," addMembers",[139,2807,1844],{"class":1269},[139,2809,1847],{"class":1269},[139,2811,2431],{"class":439},[139,2813,1384],{"class":1269},[139,2815,1804],{"class":439},[139,2817,2818,2821,2823,2826],{"class":141,"line":148},[139,2819,2820],{"class":439},"  loading.value ",[139,2822,1742],{"class":1269},[139,2824,2825],{"class":420}," true",[139,2827,1429],{"class":439},[139,2829,2830,2832],{"class":141,"line":154},[139,2831,1870],{"class":1269},[139,2833,1804],{"class":439},[139,2835,2836,2838,2840,2842,2845,2847,2849,2851,2853,2856],{"class":141,"line":160},[139,2837,1877],{"class":1269},[139,2839,1880],{"class":420},[139,2841,1856],{"class":1269},[139,2843,2844],{"class":420}," any",[139,2846,1844],{"class":1269},[139,2848,1885],{"class":1269},[139,2850,1888],{"class":412},[139,2852,1393],{"class":439},[139,2854,2855],{"class":416},"'\u002Fapi\u002Ffamilies'",[139,2857,1896],{"class":439},[139,2859,2860,2862,2864],{"class":141,"line":166},[139,2861,1901],{"class":439},[139,2863,1904],{"class":416},[139,2865,1907],{"class":439},[139,2867,2868],{"class":141,"line":172},[139,2869,2870],{"class":439},"      body: {\n",[139,2872,2873,2876,2879],{"class":141,"line":178},[139,2874,2875],{"class":439},"        type: ",[139,2877,2878],{"class":416},"'ADD_MEMBERS'",[139,2880,1907],{"class":439},[139,2882,2883],{"class":141,"line":184},[139,2884,2885],{"class":439},"        familyId: metadata?.family_id,\n",[139,2887,2888],{"class":141,"line":190},[139,2889,2890],{"class":439},"        members: members.value,\n",[139,2892,2893],{"class":141,"line":196},[139,2894,2895],{"class":439},"        onboardStep: metadata?.onboard_step,\n",[139,2897,2898],{"class":141,"line":202},[139,2899,347],{"class":439},[139,2901,2902],{"class":141,"line":208},[139,2903,1912],{"class":439},[139,2905,2906],{"class":141,"line":214},[139,2907,658],{"emptyLinePlaceholder":657},[139,2909,2910,2912,2914,2916,2919],{"class":141,"line":220},[139,2911,1921],{"class":439},[139,2913,1924],{"class":412},[139,2915,1393],{"class":439},[139,2917,2918],{"class":416},"'response of add members'",[139,2920,1932],{"class":439},[139,2922,2923,2925,2928,2931],{"class":141,"line":226},[139,2924,2635],{"class":1269},[139,2926,2927],{"class":439}," (res.user ",[139,2929,2930],{"class":1269},"&&",[139,2932,2933],{"class":439}," user.value) {\n",[139,2935,2936,2939,2941],{"class":141,"line":232},[139,2937,2938],{"class":439},"      user.value.user_metadata ",[139,2940,1742],{"class":1269},[139,2942,2943],{"class":439}," res.user.user_metadata;\n",[139,2945,2946],{"class":141,"line":238},[139,2947,652],{"class":439},[139,2949,2950],{"class":141,"line":244},[139,2951,658],{"emptyLinePlaceholder":657},[139,2953,2954,2957,2959,2962],{"class":141,"line":250},[139,2955,2956],{"class":412},"    emit",[139,2958,1393],{"class":439},[139,2960,2961],{"class":416},"'membersAdded'",[139,2963,1503],{"class":439},[139,2965,2966,2968,2970],{"class":141,"line":256},[139,2967,1960],{"class":439},[139,2969,1963],{"class":1269},[139,2971,1966],{"class":439},[139,2973,2974,2976,2978,2980,2983],{"class":141,"line":594},[139,2975,1921],{"class":439},[139,2977,1924],{"class":412},[139,2979,1393],{"class":439},[139,2981,2982],{"class":416},"'error is adding members'",[139,2984,2727],{"class":439},[139,2986,2987],{"class":141,"line":600},[139,2988,1984],{"class":439},[139,2990,2991],{"class":141,"line":606},[139,2992,658],{"emptyLinePlaceholder":657},[139,2994,2995,2997,2999,3002],{"class":141,"line":612},[139,2996,2820],{"class":439},[139,2998,1742],{"class":1269},[139,3000,3001],{"class":420}," false",[139,3003,1429],{"class":439},[139,3005,3006],{"class":141,"line":910},[139,3007,1989],{"class":439},[10,3009,3010,3011,3014],{},"The ",[70,3012,3013],{},"\u002Fapi\u002Ffamilies"," code",[131,3016,3018],{"className":1347,"code":3017,"language":1349,"meta":79,"style":79},"import { UserObject } from '@passageidentity\u002Fpassage-node';\nimport { protectRoute, createUser, updateUser } from '..\u002FusePassage';\nimport { useAppwrite } from '..\u002FuseAppwrite';\n\nconst addFamilyMembers = async (\n  userId: string,\n  urlOrigin: string,\n  data: any\n) => {\n  if (!data.familyId || !data.members || !data.members.length) {\n    throw createError({\n      statusCode: 400,\n      message: 'Missing familyId or members to add',\n    });\n  }\n\n  const { $users, $teams } = useAppwrite();\n  try {\n    const userPromises = [];\n    for (const member of data.members) {\n      \u002F\u002F Create Passage Users\n      userPromises.push(\n        createUser(member.email, {\n          name: member.name,\n          family_id: data.familyId,\n          roles: member.role,\n          onboard_step: 'done',\n        })\n      );\n    }\n\n    const users = await Promise.all(userPromises);\n    console.log('created new users in passage: ', users);\n\n    const appwriteUserPromises = [];\n    for (const user of users) {\n      appwriteUserPromises.push(\n        $users.create(\n          user.id,\n          user.email,\n          undefined,\n          undefined,\n          user.user_metadata?.name as string\n        )\n      );\n    }\n\n    const appwriteUsers = await Promise.all(appwriteUserPromises);\n\n    console.log('created new users in appwrite: ', appwriteUsers);\n\n    const memberPromises = [];\n    for (const user of users) {\n      memberPromises.push(\n        $teams.createMembership(\n          data.familyId,\n          [user.user_metadata?.roles as string],\n          `${urlOrigin}\u002Fjoin-team`,\n          user.email,\n          user.id,\n          user.phone,\n          user.user_metadata?.name as string\n        )\n      );\n    }\n\n    const memberships = await Promise.all(memberPromises);\n\n    console.log('created memberships in appwrite', memberships);\n\n    let updatedUser;\n    if (data.onboardStep === 'family') {\n      \u002F\u002F updat passage user metadata\n      updatedUser = await updateUser(userId, {\n        onboard_step: 'jar',\n      });\n    }\n\n    return {\n      user: updatedUser,\n    };\n  } catch (error) {\n    console.log('failed to add members', error);\n    throw createError({\n      statusCode: 500,\n      message: 'Failed to add family members',\n    });\n  }\n};\n\nexport default defineEventHandler(async (event) => {\n  console.log('incoming post event for api\u002Ffamilies\u002F');\n\n  await protectRoute(event);\n\n  const body = await readBody(event);\n  console.log('body', body);\n\n  if (!body.type || !['CREATE', 'ADD_MEMBERS'].includes(body.type)) {\n    throw createError({\n      statusCode: 400,\n      message: 'Missing or unsupported event type',\n    });\n  }\n\n  const user = event.context.auth.user as UserObject;\n  console.log(`got some auth user: ${user}`);\n\n  const origin = getHeader(event, 'origin');\n\n  let data;\n  if (body.type === 'CREATE') {\n    data = await createFamily(user.id, origin || '', body);\n  } else {\n    data = await addFamilyMembers(user.id, origin || '', body);\n  }\n\n  return {\n    status: 'ok',\n    ...data,\n  };\n});\n",[70,3019,3020,3032,3045,3057,3061,3075,3087,3098,3108,3116,3149,3158,3168,3178,3182,3186,3190,3212,3218,3230,3248,3253,3263,3271,3276,3281,3286,3296,3300,3305,3309,3313,3335,3349,3353,3364,3379,3388,3397,3402,3407,3414,3420,3429,3434,3438,3442,3447,3468,3473,3488,3493,3505,3520,3530,3541,3547,3560,3574,3579,3584,3590,3599,3604,3609,3614,3619,3640,3645,3660,3665,3674,3690,3696,3712,3723,3729,3734,3739,3747,3753,3758,3767,3781,3790,3800,3810,3815,3820,3825,3830,3853,3867,3872,3881,3886,3903,3918,3923,3958,3967,3976,3986,3991,3996,4001,4018,4038,4043,4064,4069,4078,4093,4116,4126,4145,4150,4155,4162,4171,4180,4185],{"__ignoreMap":79},[139,3021,3022,3024,3026,3028,3030],{"class":141,"line":142},[139,3023,1423],{"class":1269},[139,3025,2077],{"class":439},[139,3027,1766],{"class":1269},[139,3029,2082],{"class":416},[139,3031,1429],{"class":439},[139,3033,3034,3036,3039,3041,3043],{"class":141,"line":148},[139,3035,1423],{"class":1269},[139,3037,3038],{"class":439}," { protectRoute, createUser, updateUser } ",[139,3040,1766],{"class":1269},[139,3042,2096],{"class":416},[139,3044,1429],{"class":439},[139,3046,3047,3049,3051,3053,3055],{"class":141,"line":154},[139,3048,1423],{"class":1269},[139,3050,2105],{"class":439},[139,3052,1766],{"class":1269},[139,3054,2110],{"class":416},[139,3056,1429],{"class":439},[139,3058,3059],{"class":141,"line":160},[139,3060,658],{"emptyLinePlaceholder":657},[139,3062,3063,3065,3068,3070,3072],{"class":141,"line":166},[139,3064,1776],{"class":1269},[139,3066,3067],{"class":412}," addFamilyMembers",[139,3069,1844],{"class":1269},[139,3071,1847],{"class":1269},[139,3073,3074],{"class":439}," (\n",[139,3076,3077,3080,3082,3085],{"class":141,"line":172},[139,3078,3079],{"class":1377},"  userId",[139,3081,1856],{"class":1269},[139,3083,3084],{"class":420}," string",[139,3086,1907],{"class":439},[139,3088,3089,3092,3094,3096],{"class":141,"line":178},[139,3090,3091],{"class":1377},"  urlOrigin",[139,3093,1856],{"class":1269},[139,3095,3084],{"class":420},[139,3097,1907],{"class":439},[139,3099,3100,3103,3105],{"class":141,"line":184},[139,3101,3102],{"class":1377},"  data",[139,3104,1856],{"class":1269},[139,3106,3107],{"class":420}," any\n",[139,3109,3110,3112,3114],{"class":141,"line":190},[139,3111,1381],{"class":439},[139,3113,1384],{"class":1269},[139,3115,1804],{"class":439},[139,3117,3118,3120,3122,3124,3127,3130,3133,3136,3138,3140,3143,3146],{"class":141,"line":196},[139,3119,2440],{"class":1269},[139,3121,1850],{"class":439},[139,3123,2445],{"class":1269},[139,3125,3126],{"class":439},"data.familyId ",[139,3128,3129],{"class":1269},"||",[139,3131,3132],{"class":1269}," !",[139,3134,3135],{"class":439},"data.members ",[139,3137,3129],{"class":1269},[139,3139,3132],{"class":1269},[139,3141,3142],{"class":439},"data.members.",[139,3144,3145],{"class":420},"length",[139,3147,3148],{"class":439},") {\n",[139,3150,3151,3154,3156],{"class":141,"line":202},[139,3152,3153],{"class":1269},"    throw",[139,3155,2746],{"class":412},[139,3157,2749],{"class":439},[139,3159,3160,3163,3166],{"class":141,"line":208},[139,3161,3162],{"class":439},"      statusCode: ",[139,3164,3165],{"class":420},"400",[139,3167,1907],{"class":439},[139,3169,3170,3173,3176],{"class":141,"line":214},[139,3171,3172],{"class":439},"      message: ",[139,3174,3175],{"class":416},"'Missing familyId or members to add'",[139,3177,1907],{"class":439},[139,3179,3180],{"class":141,"line":220},[139,3181,1912],{"class":439},[139,3183,3184],{"class":141,"line":226},[139,3185,1984],{"class":439},[139,3187,3188],{"class":141,"line":232},[139,3189,658],{"emptyLinePlaceholder":657},[139,3191,3192,3194,3196,3198,3201,3204,3206,3208,3210],{"class":141,"line":238},[139,3193,2182],{"class":1269},[139,3195,1779],{"class":439},[139,3197,2225],{"class":420},[139,3199,3200],{"class":439},", ",[139,3202,3203],{"class":420},"$teams",[139,3205,1785],{"class":439},[139,3207,1742],{"class":1269},[139,3209,2232],{"class":412},[139,3211,1793],{"class":439},[139,3213,3214,3216],{"class":141,"line":244},[139,3215,1870],{"class":1269},[139,3217,1804],{"class":439},[139,3219,3220,3222,3225,3227],{"class":141,"line":250},[139,3221,1877],{"class":1269},[139,3223,3224],{"class":420}," userPromises",[139,3226,1844],{"class":1269},[139,3228,3229],{"class":439}," [];\n",[139,3231,3232,3235,3237,3239,3242,3245],{"class":141,"line":256},[139,3233,3234],{"class":1269},"    for",[139,3236,1850],{"class":439},[139,3238,1776],{"class":1269},[139,3240,3241],{"class":420}," member",[139,3243,3244],{"class":1269}," of",[139,3246,3247],{"class":439}," data.members) {\n",[139,3249,3250],{"class":141,"line":594},[139,3251,3252],{"class":2243},"      \u002F\u002F Create Passage Users\n",[139,3254,3255,3258,3261],{"class":141,"line":600},[139,3256,3257],{"class":439},"      userPromises.",[139,3259,3260],{"class":412},"push",[139,3262,2263],{"class":439},[139,3264,3265,3268],{"class":141,"line":606},[139,3266,3267],{"class":412},"        createUser",[139,3269,3270],{"class":439},"(member.email, {\n",[139,3272,3273],{"class":141,"line":612},[139,3274,3275],{"class":439},"          name: member.name,\n",[139,3277,3278],{"class":141,"line":910},[139,3279,3280],{"class":439},"          family_id: data.familyId,\n",[139,3282,3283],{"class":141,"line":916},[139,3284,3285],{"class":439},"          roles: member.role,\n",[139,3287,3288,3291,3294],{"class":141,"line":922},[139,3289,3290],{"class":439},"          onboard_step: ",[139,3292,3293],{"class":416},"'done'",[139,3295,1907],{"class":439},[139,3297,3298],{"class":141,"line":1168},[139,3299,913],{"class":439},[139,3301,3302],{"class":141,"line":1174},[139,3303,3304],{"class":439},"      );\n",[139,3306,3307],{"class":141,"line":1180},[139,3308,652],{"class":439},[139,3310,3311],{"class":141,"line":1186},[139,3312,658],{"emptyLinePlaceholder":657},[139,3314,3315,3317,3320,3322,3324,3327,3329,3332],{"class":141,"line":1192},[139,3316,1877],{"class":1269},[139,3318,3319],{"class":420}," users",[139,3321,1844],{"class":1269},[139,3323,1885],{"class":1269},[139,3325,3326],{"class":420}," Promise",[139,3328,73],{"class":439},[139,3330,3331],{"class":412},"all",[139,3333,3334],{"class":439},"(userPromises);\n",[139,3336,3337,3339,3341,3343,3346],{"class":141,"line":1197},[139,3338,1921],{"class":439},[139,3340,1924],{"class":412},[139,3342,1393],{"class":439},[139,3344,3345],{"class":416},"'created new users in passage: '",[139,3347,3348],{"class":439},", users);\n",[139,3350,3351],{"class":141,"line":1202},[139,3352,658],{"emptyLinePlaceholder":657},[139,3354,3355,3357,3360,3362],{"class":141,"line":1207},[139,3356,1877],{"class":1269},[139,3358,3359],{"class":420}," appwriteUserPromises",[139,3361,1844],{"class":1269},[139,3363,3229],{"class":439},[139,3365,3366,3368,3370,3372,3374,3376],{"class":141,"line":1212},[139,3367,3234],{"class":1269},[139,3369,1850],{"class":439},[139,3371,1776],{"class":1269},[139,3373,2185],{"class":420},[139,3375,3244],{"class":1269},[139,3377,3378],{"class":439}," users) {\n",[139,3380,3381,3384,3386],{"class":141,"line":1217},[139,3382,3383],{"class":439},"      appwriteUserPromises.",[139,3385,3260],{"class":412},[139,3387,2263],{"class":439},[139,3389,3390,3393,3395],{"class":141,"line":2706},[139,3391,3392],{"class":439},"        $users.",[139,3394,2260],{"class":412},[139,3396,2263],{"class":439},[139,3398,3399],{"class":141,"line":2715},[139,3400,3401],{"class":439},"          user.id,\n",[139,3403,3404],{"class":141,"line":2730},[139,3405,3406],{"class":439},"          user.email,\n",[139,3408,3409,3412],{"class":141,"line":2735},[139,3410,3411],{"class":420},"          undefined",[139,3413,1907],{"class":439},[139,3415,3416,3418],{"class":141,"line":2740},[139,3417,3411],{"class":420},[139,3419,1907],{"class":439},[139,3421,3422,3425,3427],{"class":141,"line":2752},[139,3423,3424],{"class":439},"          user.user_metadata?.name ",[139,3426,2193],{"class":1269},[139,3428,2296],{"class":420},[139,3430,3431],{"class":141,"line":2763},[139,3432,3433],{"class":439},"        )\n",[139,3435,3436],{"class":141,"line":2774},[139,3437,3304],{"class":439},[139,3439,3440],{"class":141,"line":2780},[139,3441,652],{"class":439},[139,3443,3445],{"class":141,"line":3444},47,[139,3446,658],{"emptyLinePlaceholder":657},[139,3448,3450,3452,3455,3457,3459,3461,3463,3465],{"class":141,"line":3449},48,[139,3451,1877],{"class":1269},[139,3453,3454],{"class":420}," appwriteUsers",[139,3456,1844],{"class":1269},[139,3458,1885],{"class":1269},[139,3460,3326],{"class":420},[139,3462,73],{"class":439},[139,3464,3331],{"class":412},[139,3466,3467],{"class":439},"(appwriteUserPromises);\n",[139,3469,3471],{"class":141,"line":3470},49,[139,3472,658],{"emptyLinePlaceholder":657},[139,3474,3476,3478,3480,3482,3485],{"class":141,"line":3475},50,[139,3477,1921],{"class":439},[139,3479,1924],{"class":412},[139,3481,1393],{"class":439},[139,3483,3484],{"class":416},"'created new users in appwrite: '",[139,3486,3487],{"class":439},", appwriteUsers);\n",[139,3489,3491],{"class":141,"line":3490},51,[139,3492,658],{"emptyLinePlaceholder":657},[139,3494,3496,3498,3501,3503],{"class":141,"line":3495},52,[139,3497,1877],{"class":1269},[139,3499,3500],{"class":420}," memberPromises",[139,3502,1844],{"class":1269},[139,3504,3229],{"class":439},[139,3506,3508,3510,3512,3514,3516,3518],{"class":141,"line":3507},53,[139,3509,3234],{"class":1269},[139,3511,1850],{"class":439},[139,3513,1776],{"class":1269},[139,3515,2185],{"class":420},[139,3517,3244],{"class":1269},[139,3519,3378],{"class":439},[139,3521,3523,3526,3528],{"class":141,"line":3522},54,[139,3524,3525],{"class":439},"      memberPromises.",[139,3527,3260],{"class":412},[139,3529,2263],{"class":439},[139,3531,3533,3536,3539],{"class":141,"line":3532},55,[139,3534,3535],{"class":439},"        $teams.",[139,3537,3538],{"class":412},"createMembership",[139,3540,2263],{"class":439},[139,3542,3544],{"class":141,"line":3543},56,[139,3545,3546],{"class":439},"          data.familyId,\n",[139,3548,3550,3553,3555,3557],{"class":141,"line":3549},57,[139,3551,3552],{"class":439},"          [user.user_metadata?.roles ",[139,3554,2193],{"class":1269},[139,3556,3084],{"class":420},[139,3558,3559],{"class":439},"],\n",[139,3561,3563,3566,3569,3572],{"class":141,"line":3562},58,[139,3564,3565],{"class":416},"          `${",[139,3567,3568],{"class":439},"urlOrigin",[139,3570,3571],{"class":416},"}\u002Fjoin-team`",[139,3573,1907],{"class":439},[139,3575,3577],{"class":141,"line":3576},59,[139,3578,3406],{"class":439},[139,3580,3582],{"class":141,"line":3581},60,[139,3583,3401],{"class":439},[139,3585,3587],{"class":141,"line":3586},61,[139,3588,3589],{"class":439},"          user.phone,\n",[139,3591,3593,3595,3597],{"class":141,"line":3592},62,[139,3594,3424],{"class":439},[139,3596,2193],{"class":1269},[139,3598,2296],{"class":420},[139,3600,3602],{"class":141,"line":3601},63,[139,3603,3433],{"class":439},[139,3605,3607],{"class":141,"line":3606},64,[139,3608,3304],{"class":439},[139,3610,3612],{"class":141,"line":3611},65,[139,3613,652],{"class":439},[139,3615,3617],{"class":141,"line":3616},66,[139,3618,658],{"emptyLinePlaceholder":657},[139,3620,3622,3624,3627,3629,3631,3633,3635,3637],{"class":141,"line":3621},67,[139,3623,1877],{"class":1269},[139,3625,3626],{"class":420}," memberships",[139,3628,1844],{"class":1269},[139,3630,1885],{"class":1269},[139,3632,3326],{"class":420},[139,3634,73],{"class":439},[139,3636,3331],{"class":412},[139,3638,3639],{"class":439},"(memberPromises);\n",[139,3641,3643],{"class":141,"line":3642},68,[139,3644,658],{"emptyLinePlaceholder":657},[139,3646,3648,3650,3652,3654,3657],{"class":141,"line":3647},69,[139,3649,1921],{"class":439},[139,3651,1924],{"class":412},[139,3653,1393],{"class":439},[139,3655,3656],{"class":416},"'created memberships in appwrite'",[139,3658,3659],{"class":439},", memberships);\n",[139,3661,3663],{"class":141,"line":3662},70,[139,3664,658],{"emptyLinePlaceholder":657},[139,3666,3668,3671],{"class":141,"line":3667},71,[139,3669,3670],{"class":1269},"    let",[139,3672,3673],{"class":439}," updatedUser;\n",[139,3675,3677,3679,3682,3685,3688],{"class":141,"line":3676},72,[139,3678,2635],{"class":1269},[139,3680,3681],{"class":439}," (data.onboardStep ",[139,3683,3684],{"class":1269},"===",[139,3686,3687],{"class":416}," 'family'",[139,3689,3148],{"class":439},[139,3691,3693],{"class":141,"line":3692},73,[139,3694,3695],{"class":2243},"      \u002F\u002F updat passage user metadata\n",[139,3697,3699,3702,3704,3706,3709],{"class":141,"line":3698},74,[139,3700,3701],{"class":439},"      updatedUser ",[139,3703,1742],{"class":1269},[139,3705,1885],{"class":1269},[139,3707,3708],{"class":412}," updateUser",[139,3710,3711],{"class":439},"(userId, {\n",[139,3713,3715,3718,3721],{"class":141,"line":3714},75,[139,3716,3717],{"class":439},"        onboard_step: ",[139,3719,3720],{"class":416},"'jar'",[139,3722,1907],{"class":439},[139,3724,3726],{"class":141,"line":3725},76,[139,3727,3728],{"class":439},"      });\n",[139,3730,3732],{"class":141,"line":3731},77,[139,3733,652],{"class":439},[139,3735,3737],{"class":141,"line":3736},78,[139,3738,658],{"emptyLinePlaceholder":657},[139,3740,3742,3745],{"class":141,"line":3741},79,[139,3743,3744],{"class":1269},"    return",[139,3746,1804],{"class":439},[139,3748,3750],{"class":141,"line":3749},80,[139,3751,3752],{"class":439},"      user: updatedUser,\n",[139,3754,3756],{"class":141,"line":3755},81,[139,3757,2513],{"class":439},[139,3759,3761,3763,3765],{"class":141,"line":3760},82,[139,3762,1960],{"class":439},[139,3764,1963],{"class":1269},[139,3766,1966],{"class":439},[139,3768,3770,3772,3774,3776,3779],{"class":141,"line":3769},83,[139,3771,1921],{"class":439},[139,3773,1924],{"class":412},[139,3775,1393],{"class":439},[139,3777,3778],{"class":416},"'failed to add members'",[139,3780,2727],{"class":439},[139,3782,3784,3786,3788],{"class":141,"line":3783},84,[139,3785,3153],{"class":1269},[139,3787,2746],{"class":412},[139,3789,2749],{"class":439},[139,3791,3793,3795,3798],{"class":141,"line":3792},85,[139,3794,3162],{"class":439},[139,3796,3797],{"class":420},"500",[139,3799,1907],{"class":439},[139,3801,3803,3805,3808],{"class":141,"line":3802},86,[139,3804,3172],{"class":439},[139,3806,3807],{"class":416},"'Failed to add family members'",[139,3809,1907],{"class":439},[139,3811,3813],{"class":141,"line":3812},87,[139,3814,1912],{"class":439},[139,3816,3818],{"class":141,"line":3817},88,[139,3819,1984],{"class":439},[139,3821,3823],{"class":141,"line":3822},89,[139,3824,1989],{"class":439},[139,3826,3828],{"class":141,"line":3827},90,[139,3829,658],{"emptyLinePlaceholder":657},[139,3831,3833,3835,3837,3839,3841,3843,3845,3847,3849,3851],{"class":141,"line":3832},91,[139,3834,2121],{"class":1269},[139,3836,2124],{"class":1269},[139,3838,2127],{"class":412},[139,3840,1393],{"class":439},[139,3842,2132],{"class":1269},[139,3844,1850],{"class":439},[139,3846,2137],{"class":1377},[139,3848,1381],{"class":439},[139,3850,1384],{"class":1269},[139,3852,1804],{"class":439},[139,3854,3856,3858,3860,3862,3865],{"class":141,"line":3855},92,[139,3857,2148],{"class":439},[139,3859,1924],{"class":412},[139,3861,1393],{"class":439},[139,3863,3864],{"class":416},"'incoming post event for api\u002Ffamilies\u002F'",[139,3866,1503],{"class":439},[139,3868,3870],{"class":141,"line":3869},93,[139,3871,658],{"emptyLinePlaceholder":657},[139,3873,3875,3877,3879],{"class":141,"line":3874},94,[139,3876,2167],{"class":1269},[139,3878,2170],{"class":412},[139,3880,2173],{"class":439},[139,3882,3884],{"class":141,"line":3883},95,[139,3885,658],{"emptyLinePlaceholder":657},[139,3887,3889,3891,3894,3896,3898,3901],{"class":141,"line":3888},96,[139,3890,2182],{"class":1269},[139,3892,3893],{"class":420}," body",[139,3895,1844],{"class":1269},[139,3897,1885],{"class":1269},[139,3899,3900],{"class":412}," readBody",[139,3902,2173],{"class":439},[139,3904,3906,3908,3910,3912,3915],{"class":141,"line":3905},97,[139,3907,2148],{"class":439},[139,3909,1924],{"class":412},[139,3911,1393],{"class":439},[139,3913,3914],{"class":416},"'body'",[139,3916,3917],{"class":439},", body);\n",[139,3919,3921],{"class":141,"line":3920},98,[139,3922,658],{"emptyLinePlaceholder":657},[139,3924,3926,3928,3930,3932,3935,3937,3939,3942,3945,3947,3949,3952,3955],{"class":141,"line":3925},99,[139,3927,2440],{"class":1269},[139,3929,1850],{"class":439},[139,3931,2445],{"class":1269},[139,3933,3934],{"class":439},"body.type ",[139,3936,3129],{"class":1269},[139,3938,3132],{"class":1269},[139,3940,3941],{"class":439},"[",[139,3943,3944],{"class":416},"'CREATE'",[139,3946,3200],{"class":439},[139,3948,2878],{"class":416},[139,3950,3951],{"class":439},"].",[139,3953,3954],{"class":412},"includes",[139,3956,3957],{"class":439},"(body.type)) {\n",[139,3959,3961,3963,3965],{"class":141,"line":3960},100,[139,3962,3153],{"class":1269},[139,3964,2746],{"class":412},[139,3966,2749],{"class":439},[139,3968,3970,3972,3974],{"class":141,"line":3969},101,[139,3971,3162],{"class":439},[139,3973,3165],{"class":420},[139,3975,1907],{"class":439},[139,3977,3979,3981,3984],{"class":141,"line":3978},102,[139,3980,3172],{"class":439},[139,3982,3983],{"class":416},"'Missing or unsupported event type'",[139,3985,1907],{"class":439},[139,3987,3989],{"class":141,"line":3988},103,[139,3990,1912],{"class":439},[139,3992,3994],{"class":141,"line":3993},104,[139,3995,1984],{"class":439},[139,3997,3999],{"class":141,"line":3998},105,[139,4000,658],{"emptyLinePlaceholder":657},[139,4002,4004,4006,4008,4010,4012,4014,4016],{"class":141,"line":4003},106,[139,4005,2182],{"class":1269},[139,4007,2185],{"class":420},[139,4009,1844],{"class":1269},[139,4011,2190],{"class":439},[139,4013,2193],{"class":1269},[139,4015,2196],{"class":412},[139,4017,1429],{"class":439},[139,4019,4021,4023,4025,4027,4030,4033,4036],{"class":141,"line":4020},107,[139,4022,2148],{"class":439},[139,4024,1924],{"class":412},[139,4026,1393],{"class":439},[139,4028,4029],{"class":416},"`got some auth user: ${",[139,4031,4032],{"class":439},"user",[139,4034,4035],{"class":416},"}`",[139,4037,1503],{"class":439},[139,4039,4041],{"class":141,"line":4040},108,[139,4042,658],{"emptyLinePlaceholder":657},[139,4044,4046,4048,4051,4053,4056,4059,4062],{"class":141,"line":4045},109,[139,4047,2182],{"class":1269},[139,4049,4050],{"class":420}," origin",[139,4052,1844],{"class":1269},[139,4054,4055],{"class":412}," getHeader",[139,4057,4058],{"class":439},"(event, ",[139,4060,4061],{"class":416},"'origin'",[139,4063,1503],{"class":439},[139,4065,4067],{"class":141,"line":4066},110,[139,4068,658],{"emptyLinePlaceholder":657},[139,4070,4072,4075],{"class":141,"line":4071},111,[139,4073,4074],{"class":1269},"  let",[139,4076,4077],{"class":439}," data;\n",[139,4079,4081,4083,4086,4088,4091],{"class":141,"line":4080},112,[139,4082,2440],{"class":1269},[139,4084,4085],{"class":439}," (body.type ",[139,4087,3684],{"class":1269},[139,4089,4090],{"class":416}," 'CREATE'",[139,4092,3148],{"class":439},[139,4094,4096,4099,4101,4103,4106,4109,4111,4114],{"class":141,"line":4095},113,[139,4097,4098],{"class":439},"    data ",[139,4100,1742],{"class":1269},[139,4102,1885],{"class":1269},[139,4104,4105],{"class":412}," createFamily",[139,4107,4108],{"class":439},"(user.id, origin ",[139,4110,3129],{"class":1269},[139,4112,4113],{"class":416}," ''",[139,4115,3917],{"class":439},[139,4117,4119,4121,4124],{"class":141,"line":4118},114,[139,4120,1960],{"class":439},[139,4122,4123],{"class":1269},"else",[139,4125,1804],{"class":439},[139,4127,4129,4131,4133,4135,4137,4139,4141,4143],{"class":141,"line":4128},115,[139,4130,4098],{"class":439},[139,4132,1742],{"class":1269},[139,4134,1885],{"class":1269},[139,4136,3067],{"class":412},[139,4138,4108],{"class":439},[139,4140,3129],{"class":1269},[139,4142,4113],{"class":416},[139,4144,3917],{"class":439},[139,4146,4148],{"class":141,"line":4147},116,[139,4149,1984],{"class":439},[139,4151,4153],{"class":141,"line":4152},117,[139,4154,658],{"emptyLinePlaceholder":657},[139,4156,4158,4160],{"class":141,"line":4157},118,[139,4159,2327],{"class":1269},[139,4161,1804],{"class":439},[139,4163,4165,4167,4169],{"class":141,"line":4164},119,[139,4166,2334],{"class":439},[139,4168,2337],{"class":416},[139,4170,1907],{"class":439},[139,4172,4174,4177],{"class":141,"line":4173},120,[139,4175,4176],{"class":1269},"    ...",[139,4178,4179],{"class":439},"data,\n",[139,4181,4183],{"class":141,"line":4182},121,[139,4184,2344],{"class":439},[139,4186,4188],{"class":141,"line":4187},122,[139,4189,1508],{"class":439},[10,4191,4192],{},"These are the passage functions to create and update a user.",[131,4194,4196],{"className":1347,"code":4195,"language":1349,"meta":79,"style":79},"import Passage, { Metadata } from '@passageidentity\u002Fpassage-node';\n\nexport const createUser = async (email: string, metadata: Metadata) => {\n  const passage = getPassage();\n\n  let userData = await passage.user.create({ email, user_metadata: metadata });\n  console.log(userData);\n\n  return userData;\n};\n\nexport const updateUser = async (userId: string, data: Metadata) => {\n  const passage = getPassage();\n\n  let userData = await passage.user.update(userId, { user_metadata: data });\n  console.log(userData);\n\n  return userData;\n};\n",[70,4197,4198,4210,4214,4252,4264,4268,4286,4295,4299,4306,4310,4314,4350,4362,4366,4384,4392,4396,4402],{"__ignoreMap":79},[139,4199,4200,4202,4204,4206,4208],{"class":141,"line":142},[139,4201,1423],{"class":1269},[139,4203,2380],{"class":439},[139,4205,1766],{"class":1269},[139,4207,2082],{"class":416},[139,4209,1429],{"class":439},[139,4211,4212],{"class":141,"line":148},[139,4213,658],{"emptyLinePlaceholder":657},[139,4215,4216,4218,4220,4223,4225,4227,4229,4232,4234,4236,4238,4241,4243,4246,4248,4250],{"class":141,"line":154},[139,4217,2121],{"class":1269},[139,4219,2562],{"class":1269},[139,4221,4222],{"class":412}," createUser",[139,4224,1844],{"class":1269},[139,4226,1847],{"class":1269},[139,4228,1850],{"class":439},[139,4230,4231],{"class":1377},"email",[139,4233,1856],{"class":1269},[139,4235,3084],{"class":420},[139,4237,3200],{"class":439},[139,4239,4240],{"class":1377},"metadata",[139,4242,1856],{"class":1269},[139,4244,4245],{"class":412}," Metadata",[139,4247,1381],{"class":439},[139,4249,1384],{"class":1269},[139,4251,1804],{"class":439},[139,4253,4254,4256,4258,4260,4262],{"class":141,"line":160},[139,4255,2182],{"class":1269},[139,4257,2590],{"class":420},[139,4259,1844],{"class":1269},[139,4261,2426],{"class":412},[139,4263,1793],{"class":439},[139,4265,4266],{"class":141,"line":166},[139,4267,658],{"emptyLinePlaceholder":657},[139,4269,4270,4272,4275,4277,4279,4281,4283],{"class":141,"line":172},[139,4271,4074],{"class":1269},[139,4273,4274],{"class":439}," userData ",[139,4276,1742],{"class":1269},[139,4278,1885],{"class":1269},[139,4280,2671],{"class":439},[139,4282,2260],{"class":412},[139,4284,4285],{"class":439},"({ email, user_metadata: metadata });\n",[139,4287,4288,4290,4292],{"class":141,"line":178},[139,4289,2148],{"class":439},[139,4291,1924],{"class":412},[139,4293,4294],{"class":439},"(userData);\n",[139,4296,4297],{"class":141,"line":184},[139,4298,658],{"emptyLinePlaceholder":657},[139,4300,4301,4303],{"class":141,"line":190},[139,4302,2327],{"class":1269},[139,4304,4305],{"class":439}," userData;\n",[139,4307,4308],{"class":141,"line":196},[139,4309,1989],{"class":439},[139,4311,4312],{"class":141,"line":202},[139,4313,658],{"emptyLinePlaceholder":657},[139,4315,4316,4318,4320,4322,4324,4326,4328,4331,4333,4335,4337,4340,4342,4344,4346,4348],{"class":141,"line":208},[139,4317,2121],{"class":1269},[139,4319,2562],{"class":1269},[139,4321,3708],{"class":412},[139,4323,1844],{"class":1269},[139,4325,1847],{"class":1269},[139,4327,1850],{"class":439},[139,4329,4330],{"class":1377},"userId",[139,4332,1856],{"class":1269},[139,4334,3084],{"class":420},[139,4336,3200],{"class":439},[139,4338,4339],{"class":1377},"data",[139,4341,1856],{"class":1269},[139,4343,4245],{"class":412},[139,4345,1381],{"class":439},[139,4347,1384],{"class":1269},[139,4349,1804],{"class":439},[139,4351,4352,4354,4356,4358,4360],{"class":141,"line":214},[139,4353,2182],{"class":1269},[139,4355,2590],{"class":420},[139,4357,1844],{"class":1269},[139,4359,2426],{"class":412},[139,4361,1793],{"class":439},[139,4363,4364],{"class":141,"line":220},[139,4365,658],{"emptyLinePlaceholder":657},[139,4367,4368,4370,4372,4374,4376,4378,4381],{"class":141,"line":226},[139,4369,4074],{"class":1269},[139,4371,4274],{"class":439},[139,4373,1742],{"class":1269},[139,4375,1885],{"class":1269},[139,4377,2671],{"class":439},[139,4379,4380],{"class":412},"update",[139,4382,4383],{"class":439},"(userId, { user_metadata: data });\n",[139,4385,4386,4388,4390],{"class":141,"line":232},[139,4387,2148],{"class":439},[139,4389,1924],{"class":412},[139,4391,4294],{"class":439},[139,4393,4394],{"class":141,"line":238},[139,4395,658],{"emptyLinePlaceholder":657},[139,4397,4398,4400],{"class":141,"line":244},[139,4399,2327],{"class":1269},[139,4401,4305],{"class":439},[139,4403,4404],{"class":141,"line":250},[139,4405,1989],{"class":439},[10,4407,4408],{},"As you can see, we're storing important user metadata inside the passage user object itself. Barring the name, none of the other fields are visible to the user. We use the familyId field from this metadata to add members to the same family as the calling user.",[131,4410,4412],{"className":1347,"code":4411,"language":1349,"meta":79,"style":79},"{\n  name: member.name,\n  family_id: data.familyId,\n  roles: member.role,\n  onboard_step: 'done',\n}\n",[70,4413,4414,4418,4426,4434,4442,4454],{"__ignoreMap":79},[139,4415,4416],{"class":141,"line":142},[139,4417,1490],{"class":439},[139,4419,4420,4423],{"class":141,"line":148},[139,4421,4422],{"class":412},"  name",[139,4424,4425],{"class":439},": member.name,\n",[139,4427,4428,4431],{"class":141,"line":154},[139,4429,4430],{"class":412},"  family_id",[139,4432,4433],{"class":439},": data.familyId,\n",[139,4435,4436,4439],{"class":141,"line":160},[139,4437,4438],{"class":412},"  roles",[139,4440,4441],{"class":439},": member.role,\n",[139,4443,4444,4447,4450,4452],{"class":141,"line":166},[139,4445,4446],{"class":412},"  onboard_step",[139,4448,4449],{"class":439},": ",[139,4451,3293],{"class":416},[139,4453,1907],{"class":439},[139,4455,4456],{"class":141,"line":172},[139,4457,668],{"class":439},[88,4459,4461],{"id":4460},"appwrite-functions","Appwrite functions",[10,4463,4464],{},"The app also uses some appwrite functions to handle important database events. The shell plugin that we created in the first part gets verified for creating\u002Fdeploying these functions. The database events that the app handles currently include",[4466,4467,4468,4472,4475],"ol",{},[4469,4470,4471],"li",{},"Jar created event: Whenever a new jar is created and it has auto credit enabled, then we update the jar object and set its nextMoneyAt field. This is the field that is queried by the cron job for auto-crediting the configured amount to the jar.",[4469,4473,4474],{},"Transaction event: On every transaction (create or update), we update the corresponding jar balance. If the transaction was done by a child, it will be pending and the jar won't be updated. Once the said transaction is approved (transaction update event) by a family member (role: member), then only the jar gets updated.",[4469,4476,4477],{},"Cron job: This job runs every day at midnight and auto credits the configured auto credit amount to the eligible jars.",[88,4479,4481],{"id":4480},"app-screenshots","App Screenshots",[10,4483,4484],{},"The rest of the app follows the same principle. All app data is fetched from Nuxt serverless APIs where every API call is authenticated by the passage-node SDK. Here are some of the screenshots from the app.",[10,4486,4487],{},"For styling the passage elements, I had to set the following CSS variables",[131,4489,4493],{"className":4490,"code":4491,"language":4492,"meta":79,"style":79},"language-css shiki shiki-themes github-light github-dark","passage-register,\npassage-login {\n  --passage-container-background-color: transparent;\n  --passage-body-text-color: #ffffff;\n  --passage-primary-color: #fbbf24;\n  --passage-onprimary-color: #0f172a;\n  --passage-hover-color: #f59e0b;\n  --passage-button-font-weight: 500;\n  --passage-container-max-width: 100%;\n  --passage-container-padding: 30px 0 10px;\n  --passage-button-width: 100%;\n  --passage-control-border-color: #6b6b6b;\n  --passage-otp-input-background-color: #434343;\n}\n","css",[70,4494,4495,4503,4510,4522,4534,4546,4558,4570,4581,4596,4619,4632,4644,4656],{"__ignoreMap":79},[139,4496,4497,4501],{"class":141,"line":142},[139,4498,4500],{"class":4499},"s9eBZ","passage-register",[139,4502,1907],{"class":439},[139,4504,4505,4508],{"class":141,"line":148},[139,4506,4507],{"class":4499},"passage-login",[139,4509,1804],{"class":439},[139,4511,4512,4515,4517,4520],{"class":141,"line":154},[139,4513,4514],{"class":1377},"  --passage-container-background-color",[139,4516,4449],{"class":439},[139,4518,4519],{"class":420},"transparent",[139,4521,1429],{"class":439},[139,4523,4524,4527,4529,4532],{"class":141,"line":160},[139,4525,4526],{"class":1377},"  --passage-body-text-color",[139,4528,4449],{"class":439},[139,4530,4531],{"class":420},"#ffffff",[139,4533,1429],{"class":439},[139,4535,4536,4539,4541,4544],{"class":141,"line":166},[139,4537,4538],{"class":1377},"  --passage-primary-color",[139,4540,4449],{"class":439},[139,4542,4543],{"class":420},"#fbbf24",[139,4545,1429],{"class":439},[139,4547,4548,4551,4553,4556],{"class":141,"line":172},[139,4549,4550],{"class":1377},"  --passage-onprimary-color",[139,4552,4449],{"class":439},[139,4554,4555],{"class":420},"#0f172a",[139,4557,1429],{"class":439},[139,4559,4560,4563,4565,4568],{"class":141,"line":178},[139,4561,4562],{"class":1377},"  --passage-hover-color",[139,4564,4449],{"class":439},[139,4566,4567],{"class":420},"#f59e0b",[139,4569,1429],{"class":439},[139,4571,4572,4575,4577,4579],{"class":141,"line":184},[139,4573,4574],{"class":1377},"  --passage-button-font-weight",[139,4576,4449],{"class":439},[139,4578,3797],{"class":420},[139,4580,1429],{"class":439},[139,4582,4583,4586,4588,4591,4594],{"class":141,"line":190},[139,4584,4585],{"class":1377},"  --passage-container-max-width",[139,4587,4449],{"class":439},[139,4589,4590],{"class":420},"100",[139,4592,4593],{"class":1269},"%",[139,4595,1429],{"class":439},[139,4597,4598,4601,4603,4606,4609,4612,4615,4617],{"class":141,"line":196},[139,4599,4600],{"class":1377},"  --passage-container-padding",[139,4602,4449],{"class":439},[139,4604,4605],{"class":420},"30",[139,4607,4608],{"class":1269},"px",[139,4610,4611],{"class":420}," 0",[139,4613,4614],{"class":420}," 10",[139,4616,4608],{"class":1269},[139,4618,1429],{"class":439},[139,4620,4621,4624,4626,4628,4630],{"class":141,"line":202},[139,4622,4623],{"class":1377},"  --passage-button-width",[139,4625,4449],{"class":439},[139,4627,4590],{"class":420},[139,4629,4593],{"class":1269},[139,4631,1429],{"class":439},[139,4633,4634,4637,4639,4642],{"class":141,"line":208},[139,4635,4636],{"class":1377},"  --passage-control-border-color",[139,4638,4449],{"class":439},[139,4640,4641],{"class":420},"#6b6b6b",[139,4643,1429],{"class":439},[139,4645,4646,4649,4651,4654],{"class":141,"line":214},[139,4647,4648],{"class":1377},"  --passage-otp-input-background-color",[139,4650,4449],{"class":439},[139,4652,4653],{"class":420},"#434343",[139,4655,1429],{"class":439},[139,4657,4658],{"class":141,"line":220},[139,4659,668],{"class":439},[10,4661,4662],{},[385,4663,4664],{},"Sign up screen",[10,4666,4667],{},[77,4668],{"alt":79,"src":4669},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F76c11c70-5027-4877-a728-c513995b70e1-217ff6f9d7.png",[10,4671,4672],{},[385,4673,4674],{},"Sign in screen",[10,4676,4677],{},[77,4678],{"alt":79,"src":4679},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F080ed958-8923-42e3-b1c5-bc03a0b9ac58-e8754762a3.png",[10,4681,4682],{},[385,4683,4684],{},"Sign in code",[10,4686,4687],{},[77,4688],{"alt":79,"src":4689},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F840f6a6e-aadc-4cca-9cc7-ba4ca7230358-aa9c709935.png",[10,4691,4692],{},[385,4693,4694],{},"App dashboard",[10,4696,4697],{},[77,4698],{"alt":79,"src":4699},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002Fe8c785ff-8264-4106-9621-21eb1fab5ebd-c64013df21.png",[10,4701,4702],{},[385,4703,4704],{},"Family screen",[10,4706,4707],{},[77,4708],{"alt":79,"src":4709},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F5f08fe85-092c-4cf7-bcf0-896afcd1d63f-e0b2b2b2dd.png",[10,4711,4712],{},[385,4713,4714],{},"Transactions screen",[10,4716,4717],{},[77,4718],{"alt":79,"src":4719},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F0fbc61de-fa7d-43e3-b18c-174d0655636f-9eb3315a2d.png",[10,4721,4722],{},[385,4723,4724],{},"User profile screen",[10,4726,4727,4728,4731],{},"This screen uses the ",[70,4729,4730],{},"\u003Cpassage-profile>"," component provided by the passage's client SDK. We can change our profile update operation from here.",[10,4733,4734],{},[77,4735],{"alt":79,"src":4736},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F0d6b79ca-bf7e-4b25-aecc-aac2af48d7a8-b6d533fd98.png",[10,4738,4739],{},"But it seems there is a bug in profile updation which the Passage team needs to look at. This app uses 3 other hidden metadata fields, and when you try to update the name it throws an error as shown below",[10,4741,4742],{},[77,4743],{"alt":79,"src":4744},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002Fe9a25f5b-8511-463c-b6cb-8461791e0b32-711d59e2ad.png",[10,4746,4747],{},[77,4748],{"alt":79,"src":4749},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F180dd719-7d5b-4ac4-9f19-403bb2506ba1-81c404632e.png",[10,4751,4752],{},[77,4753],{"alt":79,"src":4754},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002Fed3c8ec4-168d-4c52-b77a-61f9d6dcda88-a716576372.png",[10,4756,4757],{},"The error message is clear enough, but the app doesn't have any control on this component. This can be fixed by the Passage team only.",[10,4759,4760],{},[385,4761,4762],{},"Create jar",[10,4764,4765],{},[77,4766],{"alt":79,"src":4767},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002Fa45d7db4-dc6f-49e8-8015-15d0327f3491-86581615cc.png",[10,4769,4770],{},[385,4771,4772],{},"Add family members",[10,4774,4775],{},[77,4776],{"alt":79,"src":4777},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F13e01a97-8906-4927-a921-6c1187ef0a21-3a772cbc96.png",[10,4779,4780],{},[385,4781,4782],{},"Make transaction",[10,4784,4785],{},[77,4786],{"alt":79,"src":4787},"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002F7d3955f1-8cb8-45e9-b7ee-c63cee1016c2-01372e5400.png",[34,4789,4791],{"id":4790},"part-3-automating-the-env-file","Part 3: Automating the env file",[10,4793,4794],{},"As this app utilizes 1Password in a big way, I decided to make use of the 1Password CLI to handle the app env file as well. Below is a simple script that reads an env file and creates a 1Password item in the specified vault and replaces field values with the respective 1Password secrets.",[131,4796,4798],{"className":403,"code":4797,"language":405,"meta":79,"style":79},"#!\u002Fbin\u002Fsh\n\n# Parse command-line arguments\nwhile [ $# -gt 0 ]; do\n  key=\"$1\"\n\n  case $key in\n    --vault)\n      vault=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    --item-name)\n      item_name=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    --env-file)\n      env_file=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    *)\n      # unknown option\n      shift\n      ;;\n  esac\ndone\n\n# Check if the --vault, --item-name, and --env-file parameters were provided\nif [ -z \"$vault\" ] || [ -z \"$item_name\" ] || [ -z \"$env_file\" ]; then\n  echo \"Please provide --vault, --item-name, and --env-file parameters.\"\n  exit 1\nfi\n\n# Check if the specified .env file exists\nif [ ! -f \"$env_file\" ]; then\n  echo \"The specified .env file does not exist.\"\n  exit 1\nfi\n\n# Check if the 1Password CLI is installed\nif ! command -v op > \u002Fdev\u002Fnull 2>&1; then\n  echo \"1Password CLI is not installed. Please install it before running this script.\"\n  exit 1\nfi\n\n# Check if the item already exists in the vault\nif op item get \"$item_name\" --vault \"$vault\" > \u002Fdev\u002Fnull 2>&1; then\n  echo \"Item '$item_name' already exists in the '$vault' vault. Skipping creation.\"\n  exit 0\nfi\n\n# Variable to store the fields string\nfields=()\nskipped_fields=()\n\n# Read the .env file line by line\nwhile IFS= read -r line || [ -n \"$line\" ]; do\n  # Skip empty lines and comments\n  if [ -z \"$line\" ] || [ \"${line#\"#\"}\" != \"$line\" ]; then\n    continue\n  fi\n\n  # Split the line into key and value\n  key=\"${line%%=*}\"\n  value=\"${line#*=}\"\n\n  # Check if the value is already a reference to a 1Password secret\n  if [ \"${value#op:\u002F\u002F}\" != \"$value\" ]; then\n    echo \"Skipping creation of '$key' field as it is already a reference to a 1Password secret.\"\n    skipped_fields+=($line)\n    continue\n  fi\n\n  # Add the key-value pair to the fields array\n  fields+=(\"$key=$value\")\ndone \u003C \"$env_file\"\n\n# Create the item in 1Password using the op create command\nop item create \\\n  --category Server \\\n  --title \"$item_name\" \\\n  --vault \"$vault\" \\\n  \"${fields[@]}\" \\\n  --tags \"$item_name,env\"\n\n# Update the .env file with the secret references\nfor ((i = 0; i \u003C ${#fields[@]}; i++)); do\n  # Split the field into key and value\n  field=\"${fields[i]}\"\n  IFS=\"=\" read -r key value \u003C\u003C\u003C \"$field\"\n\n  fields[i]=\"$key=op:\u002F\u002F$vault\u002F$item_name\u002F$key\"\ndone\n\nfinal_fields=(\"${fields[@]}\" \"${skipped_fields[@]}\")\n\n# Overwrite the .env file with the updated key-value pairs\nprintf \"%s\\n\" \"${final_fields[@]}\" > \"$env_file\"\n\necho \"Item '$item_name' created successfully in the '$vault' vault.\"\necho \"Updated the '$env_file' file with the secret references.\"\n",[70,4799,4800,4805,4809,4814,4836,4852,4856,4867,4875,4889,4897,4904,4909,4916,4929,4935,4941,4945,4952,4965,4971,4977,4981,4986,4991,4996,5000,5005,5010,5014,5019,5073,5081,5089,5094,5098,5103,5124,5131,5137,5141,5145,5150,5179,5186,5192,5196,5200,5205,5242,5259,5266,5270,5274,5279,5289,5298,5302,5307,5344,5349,5393,5398,5403,5407,5412,5433,5450,5454,5459,5494,5508,5519,5523,5527,5531,5536,5557,5570,5574,5579,5590,5600,5613,5626,5643,5655,5659,5664,5702,5707,5721,5751,5755,5782,5786,5790,5822,5826,5831,5857,5861,5878],{"__ignoreMap":79},[139,4801,4802],{"class":141,"line":142},[139,4803,4804],{"class":2243},"#!\u002Fbin\u002Fsh\n",[139,4806,4807],{"class":141,"line":148},[139,4808,658],{"emptyLinePlaceholder":657},[139,4810,4811],{"class":141,"line":154},[139,4812,4813],{"class":2243},"# Parse command-line arguments\n",[139,4815,4816,4819,4822,4825,4828,4830,4833],{"class":141,"line":160},[139,4817,4818],{"class":1269},"while",[139,4820,4821],{"class":439}," [ ",[139,4823,4824],{"class":420},"$#",[139,4826,4827],{"class":1269}," -gt",[139,4829,4611],{"class":420},[139,4831,4832],{"class":439}," ]; ",[139,4834,4835],{"class":1269},"do\n",[139,4837,4838,4841,4843,4846,4849],{"class":141,"line":166},[139,4839,4840],{"class":439},"  key",[139,4842,1742],{"class":1269},[139,4844,4845],{"class":416},"\"",[139,4847,4848],{"class":420},"$1",[139,4850,4851],{"class":416},"\"\n",[139,4853,4854],{"class":141,"line":172},[139,4855,658],{"emptyLinePlaceholder":657},[139,4857,4858,4861,4864],{"class":141,"line":178},[139,4859,4860],{"class":1269},"  case",[139,4862,4863],{"class":439}," $key ",[139,4865,4866],{"class":1269},"in\n",[139,4868,4869,4873],{"class":141,"line":184},[139,4870,4872],{"class":4871},"sA_wV","    --vault",[139,4874,259],{"class":1269},[139,4876,4877,4880,4882,4884,4887],{"class":141,"line":190},[139,4878,4879],{"class":439},"      vault",[139,4881,1742],{"class":1269},[139,4883,4845],{"class":416},[139,4885,4886],{"class":420},"$2",[139,4888,4851],{"class":416},[139,4890,4891,4894],{"class":141,"line":196},[139,4892,4893],{"class":420},"      shift",[139,4895,4896],{"class":2243}," # past argument\n",[139,4898,4899,4901],{"class":141,"line":202},[139,4900,4893],{"class":420},[139,4902,4903],{"class":2243}," # past value\n",[139,4905,4906],{"class":141,"line":208},[139,4907,4908],{"class":439},"      ;;\n",[139,4910,4911,4914],{"class":141,"line":214},[139,4912,4913],{"class":4871},"    --item-name",[139,4915,259],{"class":1269},[139,4917,4918,4921,4923,4925,4927],{"class":141,"line":220},[139,4919,4920],{"class":439},"      item_name",[139,4922,1742],{"class":1269},[139,4924,4845],{"class":416},[139,4926,4886],{"class":420},[139,4928,4851],{"class":416},[139,4930,4931,4933],{"class":141,"line":226},[139,4932,4893],{"class":420},[139,4934,4896],{"class":2243},[139,4936,4937,4939],{"class":141,"line":232},[139,4938,4893],{"class":420},[139,4940,4903],{"class":2243},[139,4942,4943],{"class":141,"line":238},[139,4944,4908],{"class":439},[139,4946,4947,4950],{"class":141,"line":244},[139,4948,4949],{"class":4871},"    --env-file",[139,4951,259],{"class":1269},[139,4953,4954,4957,4959,4961,4963],{"class":141,"line":250},[139,4955,4956],{"class":439},"      env_file",[139,4958,1742],{"class":1269},[139,4960,4845],{"class":416},[139,4962,4886],{"class":420},[139,4964,4851],{"class":416},[139,4966,4967,4969],{"class":141,"line":256},[139,4968,4893],{"class":420},[139,4970,4896],{"class":2243},[139,4972,4973,4975],{"class":141,"line":594},[139,4974,4893],{"class":420},[139,4976,4903],{"class":2243},[139,4978,4979],{"class":141,"line":600},[139,4980,4908],{"class":439},[139,4982,4983],{"class":141,"line":606},[139,4984,4985],{"class":1269},"    *)\n",[139,4987,4988],{"class":141,"line":612},[139,4989,4990],{"class":2243},"      # unknown option\n",[139,4992,4993],{"class":141,"line":910},[139,4994,4995],{"class":420},"      shift\n",[139,4997,4998],{"class":141,"line":916},[139,4999,4908],{"class":439},[139,5001,5002],{"class":141,"line":922},[139,5003,5004],{"class":1269},"  esac\n",[139,5006,5007],{"class":141,"line":1168},[139,5008,5009],{"class":1269},"done\n",[139,5011,5012],{"class":141,"line":1174},[139,5013,658],{"emptyLinePlaceholder":657},[139,5015,5016],{"class":141,"line":1180},[139,5017,5018],{"class":2243},"# Check if the --vault, --item-name, and --env-file parameters were provided\n",[139,5020,5021,5024,5026,5029,5032,5035,5037,5040,5042,5044,5046,5048,5051,5053,5055,5057,5059,5061,5063,5066,5068,5070],{"class":141,"line":1186},[139,5022,5023],{"class":1269},"if",[139,5025,4821],{"class":439},[139,5027,5028],{"class":1269},"-z",[139,5030,5031],{"class":416}," \"",[139,5033,5034],{"class":439},"$vault",[139,5036,4845],{"class":416},[139,5038,5039],{"class":439}," ] ",[139,5041,3129],{"class":1269},[139,5043,4821],{"class":439},[139,5045,5028],{"class":1269},[139,5047,5031],{"class":416},[139,5049,5050],{"class":439},"$item_name",[139,5052,4845],{"class":416},[139,5054,5039],{"class":439},[139,5056,3129],{"class":1269},[139,5058,4821],{"class":439},[139,5060,5028],{"class":1269},[139,5062,5031],{"class":416},[139,5064,5065],{"class":439},"$env_file",[139,5067,4845],{"class":416},[139,5069,4832],{"class":439},[139,5071,5072],{"class":1269},"then\n",[139,5074,5075,5078],{"class":141,"line":1192},[139,5076,5077],{"class":420},"  echo",[139,5079,5080],{"class":416}," \"Please provide --vault, --item-name, and --env-file parameters.\"\n",[139,5082,5083,5086],{"class":141,"line":1197},[139,5084,5085],{"class":420},"  exit",[139,5087,5088],{"class":420}," 1\n",[139,5090,5091],{"class":141,"line":1202},[139,5092,5093],{"class":1269},"fi\n",[139,5095,5096],{"class":141,"line":1207},[139,5097,658],{"emptyLinePlaceholder":657},[139,5099,5100],{"class":141,"line":1212},[139,5101,5102],{"class":2243},"# Check if the specified .env file exists\n",[139,5104,5105,5107,5109,5111,5114,5116,5118,5120,5122],{"class":141,"line":1217},[139,5106,5023],{"class":1269},[139,5108,4821],{"class":439},[139,5110,2445],{"class":1269},[139,5112,5113],{"class":1269}," -f",[139,5115,5031],{"class":416},[139,5117,5065],{"class":439},[139,5119,4845],{"class":416},[139,5121,4832],{"class":439},[139,5123,5072],{"class":1269},[139,5125,5126,5128],{"class":141,"line":2706},[139,5127,5077],{"class":420},[139,5129,5130],{"class":416}," \"The specified .env file does not exist.\"\n",[139,5132,5133,5135],{"class":141,"line":2715},[139,5134,5085],{"class":420},[139,5136,5088],{"class":420},[139,5138,5139],{"class":141,"line":2730},[139,5140,5093],{"class":1269},[139,5142,5143],{"class":141,"line":2735},[139,5144,658],{"emptyLinePlaceholder":657},[139,5146,5147],{"class":141,"line":2740},[139,5148,5149],{"class":2243},"# Check if the 1Password CLI is installed\n",[139,5151,5152,5154,5156,5159,5162,5165,5168,5171,5174,5177],{"class":141,"line":2752},[139,5153,5023],{"class":1269},[139,5155,3132],{"class":1269},[139,5157,5158],{"class":420}," command",[139,5160,5161],{"class":420}," -v",[139,5163,5164],{"class":416}," op",[139,5166,5167],{"class":1269}," >",[139,5169,5170],{"class":416}," \u002Fdev\u002Fnull",[139,5172,5173],{"class":1269}," 2>&1",[139,5175,5176],{"class":439},"; ",[139,5178,5072],{"class":1269},[139,5180,5181,5183],{"class":141,"line":2763},[139,5182,5077],{"class":420},[139,5184,5185],{"class":416}," \"1Password CLI is not installed. Please install it before running this script.\"\n",[139,5187,5188,5190],{"class":141,"line":2774},[139,5189,5085],{"class":420},[139,5191,5088],{"class":420},[139,5193,5194],{"class":141,"line":2780},[139,5195,5093],{"class":1269},[139,5197,5198],{"class":141,"line":3444},[139,5199,658],{"emptyLinePlaceholder":657},[139,5201,5202],{"class":141,"line":3449},[139,5203,5204],{"class":2243},"# Check if the item already exists in the vault\n",[139,5206,5207,5209,5211,5214,5217,5219,5221,5223,5226,5228,5230,5232,5234,5236,5238,5240],{"class":141,"line":3470},[139,5208,5023],{"class":1269},[139,5210,5164],{"class":412},[139,5212,5213],{"class":416}," item",[139,5215,5216],{"class":416}," get",[139,5218,5031],{"class":416},[139,5220,5050],{"class":439},[139,5222,4845],{"class":416},[139,5224,5225],{"class":420}," --vault",[139,5227,5031],{"class":416},[139,5229,5034],{"class":439},[139,5231,4845],{"class":416},[139,5233,5167],{"class":1269},[139,5235,5170],{"class":416},[139,5237,5173],{"class":1269},[139,5239,5176],{"class":439},[139,5241,5072],{"class":1269},[139,5243,5244,5246,5249,5251,5254,5256],{"class":141,"line":3475},[139,5245,5077],{"class":420},[139,5247,5248],{"class":416}," \"Item '",[139,5250,5050],{"class":439},[139,5252,5253],{"class":416},"' already exists in the '",[139,5255,5034],{"class":439},[139,5257,5258],{"class":416},"' vault. Skipping creation.\"\n",[139,5260,5261,5263],{"class":141,"line":3490},[139,5262,5085],{"class":420},[139,5264,5265],{"class":420}," 0\n",[139,5267,5268],{"class":141,"line":3495},[139,5269,5093],{"class":1269},[139,5271,5272],{"class":141,"line":3507},[139,5273,658],{"emptyLinePlaceholder":657},[139,5275,5276],{"class":141,"line":3522},[139,5277,5278],{"class":2243},"# Variable to store the fields string\n",[139,5280,5281,5284,5286],{"class":141,"line":3532},[139,5282,5283],{"class":439},"fields",[139,5285,1742],{"class":1269},[139,5287,5288],{"class":439},"()\n",[139,5290,5291,5294,5296],{"class":141,"line":3543},[139,5292,5293],{"class":439},"skipped_fields",[139,5295,1742],{"class":1269},[139,5297,5288],{"class":439},[139,5299,5300],{"class":141,"line":3549},[139,5301,658],{"emptyLinePlaceholder":657},[139,5303,5304],{"class":141,"line":3562},[139,5305,5306],{"class":2243},"# Read the .env file line by line\n",[139,5308,5309,5311,5314,5316,5319,5322,5325,5328,5330,5333,5335,5338,5340,5342],{"class":141,"line":3576},[139,5310,4818],{"class":1269},[139,5312,5313],{"class":439}," IFS",[139,5315,1742],{"class":1269},[139,5317,5318],{"class":420}," read",[139,5320,5321],{"class":420}," -r",[139,5323,5324],{"class":416}," line",[139,5326,5327],{"class":1269}," ||",[139,5329,4821],{"class":439},[139,5331,5332],{"class":1269},"-n",[139,5334,5031],{"class":416},[139,5336,5337],{"class":439},"$line",[139,5339,4845],{"class":416},[139,5341,4832],{"class":439},[139,5343,4835],{"class":1269},[139,5345,5346],{"class":141,"line":3581},[139,5347,5348],{"class":2243},"  # Skip empty lines and comments\n",[139,5350,5351,5353,5355,5357,5359,5361,5363,5365,5367,5369,5372,5374,5377,5380,5383,5385,5387,5389,5391],{"class":141,"line":3586},[139,5352,2440],{"class":1269},[139,5354,4821],{"class":439},[139,5356,5028],{"class":1269},[139,5358,5031],{"class":416},[139,5360,5337],{"class":439},[139,5362,4845],{"class":416},[139,5364,5039],{"class":439},[139,5366,3129],{"class":1269},[139,5368,4821],{"class":439},[139,5370,5371],{"class":416},"\"${",[139,5373,141],{"class":439},[139,5375,5376],{"class":1269},"#",[139,5378,5379],{"class":416},"\"#\"}\"",[139,5381,5382],{"class":1269}," !=",[139,5384,5031],{"class":416},[139,5386,5337],{"class":439},[139,5388,4845],{"class":416},[139,5390,4832],{"class":439},[139,5392,5072],{"class":1269},[139,5394,5395],{"class":141,"line":3592},[139,5396,5397],{"class":1269},"    continue\n",[139,5399,5400],{"class":141,"line":3601},[139,5401,5402],{"class":1269},"  fi\n",[139,5404,5405],{"class":141,"line":3606},[139,5406,658],{"emptyLinePlaceholder":657},[139,5408,5409],{"class":141,"line":3611},[139,5410,5411],{"class":2243},"  # Split the line into key and value\n",[139,5413,5414,5416,5418,5420,5422,5425,5427,5430],{"class":141,"line":3616},[139,5415,4840],{"class":439},[139,5417,1742],{"class":1269},[139,5419,5371],{"class":416},[139,5421,141],{"class":439},[139,5423,5424],{"class":1269},"%%",[139,5426,1742],{"class":416},[139,5428,5429],{"class":1269},"*",[139,5431,5432],{"class":416},"}\"\n",[139,5434,5435,5438,5440,5442,5444,5447],{"class":141,"line":3621},[139,5436,5437],{"class":439},"  value",[139,5439,1742],{"class":1269},[139,5441,5371],{"class":416},[139,5443,141],{"class":439},[139,5445,5446],{"class":1269},"#*",[139,5448,5449],{"class":416},"=}\"\n",[139,5451,5452],{"class":141,"line":3642},[139,5453,658],{"emptyLinePlaceholder":657},[139,5455,5456],{"class":141,"line":3647},[139,5457,5458],{"class":2243},"  # Check if the value is already a reference to a 1Password secret\n",[139,5460,5461,5463,5465,5467,5470,5472,5475,5478,5481,5483,5485,5488,5490,5492],{"class":141,"line":3662},[139,5462,2440],{"class":1269},[139,5464,4821],{"class":439},[139,5466,5371],{"class":416},[139,5468,5469],{"class":439},"value",[139,5471,5376],{"class":1269},[139,5473,5474],{"class":439},"op",[139,5476,5477],{"class":1269},":\u002F\u002F",[139,5479,5480],{"class":416},"}\"",[139,5482,5382],{"class":1269},[139,5484,5031],{"class":416},[139,5486,5487],{"class":439},"$value",[139,5489,4845],{"class":416},[139,5491,4832],{"class":439},[139,5493,5072],{"class":1269},[139,5495,5496,5499,5502,5505],{"class":141,"line":3667},[139,5497,5498],{"class":420},"    echo",[139,5500,5501],{"class":416}," \"Skipping creation of '",[139,5503,5504],{"class":439},"$key",[139,5506,5507],{"class":416},"' field as it is already a reference to a 1Password secret.\"\n",[139,5509,5510,5513,5516],{"class":141,"line":3676},[139,5511,5512],{"class":439},"    skipped_fields",[139,5514,5515],{"class":1269},"+=",[139,5517,5518],{"class":439},"($line)\n",[139,5520,5521],{"class":141,"line":3692},[139,5522,5397],{"class":1269},[139,5524,5525],{"class":141,"line":3698},[139,5526,5402],{"class":1269},[139,5528,5529],{"class":141,"line":3714},[139,5530,658],{"emptyLinePlaceholder":657},[139,5532,5533],{"class":141,"line":3725},[139,5534,5535],{"class":2243},"  # Add the key-value pair to the fields array\n",[139,5537,5538,5541,5543,5545,5547,5549,5551,5553,5555],{"class":141,"line":3731},[139,5539,5540],{"class":439},"  fields",[139,5542,5515],{"class":1269},[139,5544,1393],{"class":439},[139,5546,4845],{"class":416},[139,5548,5504],{"class":439},[139,5550,1742],{"class":416},[139,5552,5487],{"class":439},[139,5554,4845],{"class":416},[139,5556,259],{"class":439},[139,5558,5559,5562,5564,5566,5568],{"class":141,"line":3736},[139,5560,5561],{"class":1269},"done",[139,5563,1270],{"class":1269},[139,5565,5031],{"class":416},[139,5567,5065],{"class":439},[139,5569,4851],{"class":416},[139,5571,5572],{"class":141,"line":3741},[139,5573,658],{"emptyLinePlaceholder":657},[139,5575,5576],{"class":141,"line":3749},[139,5577,5578],{"class":2243},"# Create the item in 1Password using the op create command\n",[139,5580,5581,5583,5585,5588],{"class":141,"line":3755},[139,5582,5474],{"class":412},[139,5584,5213],{"class":416},[139,5586,5587],{"class":416}," create",[139,5589,421],{"class":420},[139,5591,5592,5595,5598],{"class":141,"line":3760},[139,5593,5594],{"class":420},"  --category",[139,5596,5597],{"class":416}," Server",[139,5599,421],{"class":420},[139,5601,5602,5605,5607,5609,5611],{"class":141,"line":3769},[139,5603,5604],{"class":420},"  --title",[139,5606,5031],{"class":416},[139,5608,5050],{"class":439},[139,5610,4845],{"class":416},[139,5612,421],{"class":420},[139,5614,5615,5618,5620,5622,5624],{"class":141,"line":3783},[139,5616,5617],{"class":420},"  --vault",[139,5619,5031],{"class":416},[139,5621,5034],{"class":439},[139,5623,4845],{"class":416},[139,5625,421],{"class":420},[139,5627,5628,5631,5633,5635,5638,5641],{"class":141,"line":3792},[139,5629,5630],{"class":416},"  \"${",[139,5632,5283],{"class":439},[139,5634,3941],{"class":416},[139,5636,5637],{"class":1269},"@",[139,5639,5640],{"class":416},"]}\"",[139,5642,421],{"class":420},[139,5644,5645,5648,5650,5652],{"class":141,"line":3802},[139,5646,5647],{"class":420},"  --tags",[139,5649,5031],{"class":416},[139,5651,5050],{"class":439},[139,5653,5654],{"class":416},",env\"\n",[139,5656,5657],{"class":141,"line":3812},[139,5658,658],{"emptyLinePlaceholder":657},[139,5660,5661],{"class":141,"line":3817},[139,5662,5663],{"class":2243},"# Update the .env file with the secret references\n",[139,5665,5666,5669,5672,5674,5676,5679,5681,5684,5686,5689,5691,5694,5697,5700],{"class":141,"line":3822},[139,5667,5668],{"class":1269},"for",[139,5670,5671],{"class":439}," ((i ",[139,5673,1742],{"class":1269},[139,5675,4611],{"class":420},[139,5677,5678],{"class":439},"; i ",[139,5680,1736],{"class":1269},[139,5682,5683],{"class":439}," ${",[139,5685,5376],{"class":1269},[139,5687,5688],{"class":439},"fields[",[139,5690,5637],{"class":1269},[139,5692,5693],{"class":439},"]}; i",[139,5695,5696],{"class":1269},"++",[139,5698,5699],{"class":439},")); ",[139,5701,4835],{"class":1269},[139,5703,5704],{"class":141,"line":3827},[139,5705,5706],{"class":2243},"  # Split the field into key and value\n",[139,5708,5709,5712,5714,5716,5718],{"class":141,"line":3832},[139,5710,5711],{"class":439},"  field",[139,5713,1742],{"class":1269},[139,5715,5371],{"class":416},[139,5717,5283],{"class":439},[139,5719,5720],{"class":416},"[i]}\"\n",[139,5722,5723,5726,5728,5731,5733,5735,5738,5741,5744,5746,5749],{"class":141,"line":3855},[139,5724,5725],{"class":439},"  IFS",[139,5727,1742],{"class":1269},[139,5729,5730],{"class":416},"\"=\"",[139,5732,5318],{"class":420},[139,5734,5321],{"class":420},[139,5736,5737],{"class":416}," key",[139,5739,5740],{"class":416}," value",[139,5742,5743],{"class":1269}," \u003C\u003C\u003C",[139,5745,5031],{"class":416},[139,5747,5748],{"class":439},"$field",[139,5750,4851],{"class":416},[139,5752,5753],{"class":141,"line":3869},[139,5754,658],{"emptyLinePlaceholder":657},[139,5756,5757,5760,5762,5764,5766,5769,5771,5774,5776,5778,5780],{"class":141,"line":3874},[139,5758,5759],{"class":439},"  fields[i]",[139,5761,1742],{"class":1269},[139,5763,4845],{"class":416},[139,5765,5504],{"class":439},[139,5767,5768],{"class":416},"=op:\u002F\u002F",[139,5770,5034],{"class":439},[139,5772,5773],{"class":416},"\u002F",[139,5775,5050],{"class":439},[139,5777,5773],{"class":416},[139,5779,5504],{"class":439},[139,5781,4851],{"class":416},[139,5783,5784],{"class":141,"line":3883},[139,5785,5009],{"class":1269},[139,5787,5788],{"class":141,"line":3888},[139,5789,658],{"emptyLinePlaceholder":657},[139,5791,5792,5795,5797,5799,5801,5803,5805,5807,5809,5812,5814,5816,5818,5820],{"class":141,"line":3905},[139,5793,5794],{"class":439},"final_fields",[139,5796,1742],{"class":1269},[139,5798,1393],{"class":439},[139,5800,5371],{"class":416},[139,5802,5283],{"class":439},[139,5804,3941],{"class":416},[139,5806,5637],{"class":1269},[139,5808,5640],{"class":416},[139,5810,5811],{"class":416}," \"${",[139,5813,5293],{"class":439},[139,5815,3941],{"class":416},[139,5817,5637],{"class":1269},[139,5819,5640],{"class":416},[139,5821,259],{"class":439},[139,5823,5824],{"class":141,"line":3920},[139,5825,658],{"emptyLinePlaceholder":657},[139,5827,5828],{"class":141,"line":3925},[139,5829,5830],{"class":2243},"# Overwrite the .env file with the updated key-value pairs\n",[139,5832,5833,5836,5839,5841,5843,5845,5847,5849,5851,5853,5855],{"class":141,"line":3960},[139,5834,5835],{"class":420},"printf",[139,5837,5838],{"class":416}," \"%s\\n\"",[139,5840,5811],{"class":416},[139,5842,5794],{"class":439},[139,5844,3941],{"class":416},[139,5846,5637],{"class":1269},[139,5848,5640],{"class":416},[139,5850,5167],{"class":1269},[139,5852,5031],{"class":416},[139,5854,5065],{"class":439},[139,5856,4851],{"class":416},[139,5858,5859],{"class":141,"line":3969},[139,5860,658],{"emptyLinePlaceholder":657},[139,5862,5863,5866,5868,5870,5873,5875],{"class":141,"line":3978},[139,5864,5865],{"class":420},"echo",[139,5867,5248],{"class":416},[139,5869,5050],{"class":439},[139,5871,5872],{"class":416},"' created successfully in the '",[139,5874,5034],{"class":439},[139,5876,5877],{"class":416},"' vault.\"\n",[139,5879,5880,5882,5885,5887],{"class":141,"line":3988},[139,5881,5865],{"class":420},[139,5883,5884],{"class":416}," \"Updated the '",[139,5886,5065],{"class":439},[139,5888,5889],{"class":416},"' file with the secret references.\"\n",[10,5891,5892],{},"We need to pass the vault name, the item entry name and the env file path while executing the script. Now this env file can be uploaded to the repo along with the rest of the code.",[131,5894,5896],{"className":403,"code":5895,"language":405,"meta":79,"style":79},".\u002Fsave-env.sh --vault AppVaults --item-name FamPro --env-file .\u002Fclient\u002F.env\n",[70,5897,5898],{"__ignoreMap":79},[139,5899,5900,5903,5905,5908,5911,5914,5917],{"class":141,"line":142},[139,5901,5902],{"class":412},".\u002Fsave-env.sh",[139,5904,5225],{"class":420},[139,5906,5907],{"class":416}," AppVaults",[139,5909,5910],{"class":420}," --item-name",[139,5912,5913],{"class":416}," FamPro",[139,5915,5916],{"class":420}," --env-file",[139,5918,5919],{"class":416}," .\u002Fclient\u002F.env\n",[131,5921,5923],{"className":403,"code":5922,"language":405,"meta":79,"style":79},"APPWRITE_ENDPOINT=op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_ENDPOINT\nAPPWRITE_PROJECT_ID=op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_PROJECT_ID\nAPPWRITE_DATABASE_ID=op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_DATABASE_ID\nAPPWRITE_JAR_COLLECTION_ID=op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_JAR_COLLECTION_ID\nAPPWRITE_TRANSACTION_COLLECTION_ID=op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_TRANSACTION_COLLECTION_ID\nAPPWRITE_API_KEY=op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_API_KEY\nPASSAGE_APP_ID=op:\u002F\u002FAppVaults\u002FFamPro\u002FPASSAGE_APP_ID\nPASSAGE_API_KEY=op:\u002F\u002FAppVaults\u002FFamPro\u002FPASSAGE_API_KEY\n",[70,5924,5925,5935,5945,5955,5965,5975,5985,5995],{"__ignoreMap":79},[139,5926,5927,5930,5932],{"class":141,"line":142},[139,5928,5929],{"class":439},"APPWRITE_ENDPOINT",[139,5931,1742],{"class":1269},[139,5933,5934],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_ENDPOINT\n",[139,5936,5937,5940,5942],{"class":141,"line":148},[139,5938,5939],{"class":439},"APPWRITE_PROJECT_ID",[139,5941,1742],{"class":1269},[139,5943,5944],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_PROJECT_ID\n",[139,5946,5947,5950,5952],{"class":141,"line":154},[139,5948,5949],{"class":439},"APPWRITE_DATABASE_ID",[139,5951,1742],{"class":1269},[139,5953,5954],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_DATABASE_ID\n",[139,5956,5957,5960,5962],{"class":141,"line":160},[139,5958,5959],{"class":439},"APPWRITE_JAR_COLLECTION_ID",[139,5961,1742],{"class":1269},[139,5963,5964],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_JAR_COLLECTION_ID\n",[139,5966,5967,5970,5972],{"class":141,"line":166},[139,5968,5969],{"class":439},"APPWRITE_TRANSACTION_COLLECTION_ID",[139,5971,1742],{"class":1269},[139,5973,5974],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_TRANSACTION_COLLECTION_ID\n",[139,5976,5977,5980,5982],{"class":141,"line":172},[139,5978,5979],{"class":439},"APPWRITE_API_KEY",[139,5981,1742],{"class":1269},[139,5983,5984],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FAPPWRITE_API_KEY\n",[139,5986,5987,5990,5992],{"class":141,"line":178},[139,5988,5989],{"class":439},"PASSAGE_APP_ID",[139,5991,1742],{"class":1269},[139,5993,5994],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FPASSAGE_APP_ID\n",[139,5996,5997,6000,6002],{"class":141,"line":184},[139,5998,5999],{"class":439},"PASSAGE_API_KEY",[139,6001,1742],{"class":1269},[139,6003,6004],{"class":416},"op:\u002F\u002FAppVaults\u002FFamPro\u002FPASSAGE_API_KEY\n",[10,6006,6007],{},"To load the secrets back into the env file we can use the below command (with the correct file paths)",[131,6009,6011],{"className":403,"code":6010,"language":405,"meta":79,"style":79},"op inject -i .env_template -o .env\n",[70,6012,6013],{"__ignoreMap":79},[139,6014,6015,6017,6020,6023,6026,6029],{"class":141,"line":142},[139,6016,5474],{"class":412},[139,6018,6019],{"class":416}," inject",[139,6021,6022],{"class":420}," -i",[139,6024,6025],{"class":416}," .env_template",[139,6027,6028],{"class":420}," -o",[139,6030,6031],{"class":416}," .env\n",[34,6033,6035],{"id":6034},"supported-features","Supported features",[10,6037,6038],{},"The app supports the following features at the moment",[4466,6040,6041,6044,6047,6050,6053],{},[4469,6042,6043],{},"Creating a family account.",[4469,6045,6046],{},"Adding members to the family with a member or child role. Only a user with a member role can add other members.",[4469,6048,6049],{},"Creating multiple jars per user (member or child). Again, only users with a member role can create jars.",[4469,6051,6052],{},"Making ad-hoc transactions against a jar. A user with a member role can make transactions in any of the jars in the family. A child can only view and do transactions in their jars.",[4469,6054,6055],{},"Transactions done by a user with a child role remain pending. These transactions need to be approved by a user with a member role before the jar balance can be updated.",[34,6057,6059],{"id":6058},"further-enhancements","Further enhancements",[4466,6061,6062,6065,6068],{},[4469,6063,6064],{},"Improve the dashboard experience",[4469,6066,6067],{},"Allow deletion of member accounts",[4469,6069,6070],{},"Allow modification and deletion of transactions",[34,6072,6074],{"id":6073},"app-source-code-and-link","App source code and link",[10,6076,6077],{},"The full source code of the app along with the 1Password CLI script can be found here",[6079,6080],"media-embed",{"url":6081},"https:\u002F\u002Fgithub.com\u002Fra-jeev\u002FFamPro",[10,6083,6084,6085,73],{},"The code for the appwrite shell plugin can be checked in ",[14,6086,6089],{"href":6087,"rel":6088},"https:\u002F\u002Fgithub.com\u002F1Password\u002Fshell-plugins\u002Fpull\u002F336",[18],"this PR",[10,6091,6092,6093],{},"You can play around with the app here: ",[14,6094,6095],{"href":6095,"rel":6096},"https:\u002F\u002Ffam-pro.vercel.app\u002F",[18],[34,6098,6100],{"id":6099},"conclusion","Conclusion",[10,6102,6103,6104,6109],{},"Overall it was a great experience building with 1Password, Appwrite and Nuxt3. I did face some challenges in the process outlined above, but we could overcome those. 1Password offers a seamless auth experience and it would be interesting to see what features they add on top of the existing functionality. I also thank the ",[14,6105,6108],{"href":6106,"rel":6107},"https:\u002F\u002Fhashnode.com\u002F",[18],"Hashnode"," team for organizing this hackathon.",[10,6111,6112],{},"I hope you enjoyed reading the article. It would mean a lot to me if you can show appreciation by sharing your feedback. If you found any mistake then please let me know in the comments.",[10,6114,6115],{},[30,6116,6117],{},"-- Keep adding the bits, soon you'll have more bytes than you may need. :-)",[6119,6120,6121],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":79,"searchDepth":148,"depth":148,"links":6123},[6124,6125,6130,6136,6137,6138,6139,6140],{"id":36,"depth":148,"text":37},{"id":58,"depth":148,"text":59,"children":6126},[6127,6128,6129],{"id":90,"depth":154,"text":91},{"id":118,"depth":154,"text":119},{"id":474,"depth":154,"text":475},{"id":1246,"depth":148,"text":1247,"children":6131},[6132,6133,6134,6135],{"id":1333,"depth":154,"text":1334},{"id":1700,"depth":154,"text":1701},{"id":4460,"depth":154,"text":4461},{"id":4480,"depth":154,"text":4481},{"id":4790,"depth":148,"text":4791},{"id":6034,"depth":148,"text":6035},{"id":6058,"depth":148,"text":6059},{"id":6073,"depth":148,"text":6074},{"id":6099,"depth":148,"text":6100},null,"\u002Fimages\u002Fposts\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite\u002Fea13967cd552231b6545aab22352a803-8a1198ee04.jpeg","2023-07-01T06:35:17.622Z","Last year I built an app for my son (and myself). The app idea was very simple, a digital piggy bank tracker integrated with optional auto credit of pocket money. We're actively...",false,"md","cljjmpomt000n09jvf4097h23",{},"\u002Fcreating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite",{"title":5,"description":6144},"creating-1password-plugin-and-use-it-to-build-an-app-with-nuxt3-passage-appwrite",[6153,413,6154,6155,6156],"nuxt","1password","buildwith1password","1password-hackathon","EOACTff2Y-47NkGVcd4r6vbr0AgZQUNyspDV1Eiuy7A",1780470201008]